Projet

Général

Profil

0001-pwa-add-generic-service-worker-24405.patch

Frédéric Péters, 10 juin 2018 13:30

Télécharger (6,03 ko)

Voir les différences:

Subject: [PATCH] pwa: add generic service worker (#24405)

 .../pwa/templates/combo/service-worker.js     | 126 ++++++++++++++++++
 combo/apps/pwa/urls.py                        |   7 +-
 combo/apps/pwa/views.py                       |   7 +
 tests/test_pwa.py                             |   3 +
 4 files changed, 141 insertions(+), 2 deletions(-)
 create mode 100644 combo/apps/pwa/templates/combo/service-worker.js
combo/apps/pwa/templates/combo/service-worker.js
1
{% load gadjo %}
2
/* global self, caches, fetch, URL, Response */
3
'use strict';
4

  
5
var config = {
6
  version: 'v{% start_timestamp %}',
7
  staticCacheItems: [
8
    '/offline/'
9
  ],
10
  cachePathPattern: /^\/static\/.*/,
11
  handleFetchPathPattern: /.*/,
12
  offlinePage: '/offline/'
13
};
14

  
15
function cacheName (key, opts) {
16
  return `${opts.version}-${key}`;
17
}
18

  
19
function addToCache (cacheKey, request, response) {
20
  if (response.ok && cacheKey !== null) {
21
    var copy = response.clone();
22
    caches.open(cacheKey).then( cache => {
23
      cache.put(request, copy);
24
    });
25
  }
26
  return response;
27
}
28

  
29
function fetchFromCache (event) {
30
  return caches.match(event.request).then(response => {
31
    if (!response) {
32
      throw Error(`${event.request.url} not found in cache`);
33
    }
34
    return response;
35
  });
36
}
37

  
38
function offlineResponse (resourceType, opts) {
39
  if (resourceType === 'content') {
40
    return caches.match(opts.offlinePage);
41
  }
42
  return undefined;
43
}
44

  
45
self.addEventListener('install', event => {
46
  function onInstall (event, opts) {
47
    var cacheKey = cacheName('static', opts);
48
    return caches.open(cacheKey)
49
      .then(cache => cache.addAll(opts.staticCacheItems));
50
  }
51

  
52
  event.waitUntil(
53
    onInstall(event, config).then( () => self.skipWaiting() )
54
  );
55
});
56

  
57
self.addEventListener('activate', event => {
58
  function onActivate (event, opts) {
59
    return caches.keys()
60
      .then(cacheKeys => {
61
        var oldCacheKeys = cacheKeys.filter(key => key.indexOf(opts.version) !== 0);
62
        var deletePromises = oldCacheKeys.map(oldKey => caches.delete(oldKey));
63
        return Promise.all(deletePromises);
64
      });
65
  }
66

  
67
  event.waitUntil(
68
    onActivate(event, config)
69
      .then( () => self.clients.claim() )
70
  );
71
});
72

  
73
self.addEventListener('fetch', event => {
74

  
75
  function shouldHandleFetch (event, opts) {
76
    var request            = event.request;
77
    var url                = new URL(request.url);
78
    var criteria           = {
79
      matchesPathPattern: opts.handleFetchPathPattern.test(url.pathname),
80
      isGETRequest      : request.method === 'GET',
81
      isFromMyOrigin    : url.origin === self.location.origin
82
    };
83
    var failingCriteria    = Object.keys(criteria)
84
      .filter(criteriaKey => !criteria[criteriaKey]);
85
    return !failingCriteria.length;
86
  }
87

  
88
  function onFetch (event, opts) {
89
    var request = event.request;
90
    var url           = new URL(request.url);
91
    var acceptHeader = request.headers.get('Accept');
92
    var resourceType = 'static';
93
    var cacheKey;
94

  
95
    if (acceptHeader.indexOf('text/html') !== -1) {
96
      resourceType = 'content';
97
    } else if (acceptHeader.indexOf('image') !== -1) {
98
      resourceType = 'image';
99
    }
100

  
101
    cacheKey = null;
102
    if (opts.cachePathPattern.test(url.pathname)) {
103
      cacheKey = cacheName(resourceType, opts);
104
    }
105

  
106
    /* always network first */
107
    if (true || resourceType === 'content') {
108
      event.respondWith(
109
        fetch(request)
110
          .then(response => addToCache(cacheKey, request, response))
111
          .catch(() => fetchFromCache(event))
112
          .catch(() => offlineResponse(resourceType, opts))
113
      );
114
    } else {
115
      event.respondWith(
116
        fetchFromCache(event)
117
          .catch(() => fetch(request))
118
            .then(response => addToCache(cacheKey, request, response))
119
          .catch(() => offlineResponse(resourceType, opts))
120
      );
121
    }
122
  }
123
  if (shouldHandleFetch(event, config)) {
124
    onFetch(event, config);
125
  }
126
});
combo/apps/pwa/urls.py
16 16

  
17 17
from django.conf.urls import url
18 18

  
19
from .views import manifest_json
19
from .views import manifest_json, service_worker_js
20 20

  
21
urlpatterns = [url('^manifest.json', manifest_json)]
21
urlpatterns = [
22
    url('^manifest.json', manifest_json),
23
    url('^service-worker.js', service_worker_js),
24
]
combo/apps/pwa/views.py
17 17
from django.http import HttpResponse, Http404
18 18
from django.template.loader import get_template, TemplateDoesNotExist
19 19

  
20

  
20 21
def manifest_json(request, *args, **kwargs):
21 22
    try:
22 23
        template = get_template('combo/manifest.json')
23 24
    except TemplateDoesNotExist:
24 25
        raise Http404()
25 26
    return HttpResponse(template.render({}, request), content_type='application/json')
27

  
28

  
29
def service_worker_js(request, *args, **kwargs):
30
    template = get_template('combo/service-worker.js')
31
    return HttpResponse(template.render({}, request),
32
            content_type='application/javascript; charset=utf-8')
tests/test_pwa.py
13 13
    templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
14 14
    with override_settings(TEMPLATES=templates_settings):
15 15
        assert app.get('/manifest.json', status=200).json['name'] == 'test'
16

  
17
def test_service_worker(app):
18
    app.get('/service-worker.js', status=200)
16
-