I'm a beginner in ember and service workers. My goal is to setup a simple ember app that works offline. I basically have a list of elements that are available through an API (GET/POST).
When I'm online, everything works as expected. I can GET the list and POST new items. When I'm offline the app works, but network requests are not executed once I go back online. All network requests are actually executed while I'm offline (and obviously fail). I would expect that the service worker caches the network requests and executes them only once I'm back online. Is this wrong?
Here some information about my setup:
Ember version:
- ember-cli: 2.13.1
- node: 7.10.0
- os: darwin x64
Service Worker Add-ons (as listed in app/package.json):
"ember-service-worker": "^0.6.6",
"ember-service-worker-asset-cache": "^0.6.1",
"ember-service-worker-cache-fallback": "^0.6.1",
"ember-service-worker-index": "^0.6.1",
I should probably also mention that I use ember-django-adapter in version 1.1.3.
This is my app/ember-cli-build.js
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
'esw-cache-fallback': {
// RegExp patterns specifying which URLs to cache.
patterns: [
'http://localhost:8000/api/v1/(.*)',
],
// changing this version number will bust the cache
version: '1'
}
});
return app.toTree();
};
My network requests (GET/POST) go to http://localhost:8000/api/v1/properties/.
This is my app/adapters/applications.js
import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
namespace: 'api/v1',
host: 'http://localhost:8000',
authorizer: 'authorizer:token',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
buildURL: function(type, id, record) {
return this._super(type, id, record) + '/';
}
});
The service worker registers when I open the app:
(function () {
'use strict';
self.addEventListener('install', function installEventListenerCallback(event) {
return self.skipWaiting();
});
self.addEventListener('activate', function installEventListenerCallback(event) {
return self.clients.claim();
});
const FILES = ['assets/connect.css', 'assets/connect.js', 'assets/connect.map', 'assets/failed.png', 'assets/passed.png', 'assets/test-support.css', 'assets/test-support.js', 'assets/test-support.map', 'assets/tests.js', 'assets/tests.map', 'assets/vendor.css', 'assets/vendor.js', 'assets/vendor.map'];
const PREPEND = undefined;
const VERSION$1 = '1';
const REQUEST_MODE = 'cors';
/*
* Deletes all caches that start with the `prefix`, except for the
* cache defined by `currentCache`
*/
var cleanupCaches = (prefix, currentCache) => {
return caches.keys().then((cacheNames) => {
cacheNames.forEach((cacheName) => {
let isOwnCache = cacheName.indexOf(prefix) === 0;
let isNotCurrentCache = cacheName !== currentCache;
if (isOwnCache && isNotCurrentCache) {
caches.delete(cacheName);
}
});
});
};
const CACHE_KEY_PREFIX = 'esw-asset-cache';
const CACHE_NAME = `${CACHE_KEY_PREFIX}-${VERSION$1}`;
const CACHE_URLS = FILES.map((file) => {
return new URL(file, (PREPEND || self.location)).toString();
});
/*
* Removes all cached requests from the cache that aren't in the `CACHE_URLS`
* list.
*/
const PRUNE_CURRENT_CACHE = () => {
caches.open(CACHE_NAME).then((cache) => {
return cache.keys().then((keys) => {
keys.forEach((request) => {
if (CACHE_URLS.indexOf(request.url) === -1) {
cache.delete(request);
}
});
});
});
};
self.addEventListener('install', (event) => {
event.waitUntil(
caches
.open(CACHE_NAME)
.then((cache) => {
return Promise.all(CACHE_URLS.map((url) => {
let request = new Request(url, { mode: REQUEST_MODE });
return fetch(request)
.then((response) => {
if (response.status >= 400) {
throw new Error(`Request for ${url} failed with status ${response.statusText}`);
}
return cache.put(url, response);
})
.catch(function(error) {
console.error(`Not caching ${url} due to ${error}`);
});
}));
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
Promise.all([
cleanupCaches(CACHE_KEY_PREFIX, CACHE_NAME),
PRUNE_CURRENT_CACHE()
])
);
});
self.addEventListener('fetch', (event) => {
let isGETRequest = event.request.method === 'GET';
let shouldRespond = CACHE_URLS.indexOf(event.request.url) !== -1;
if (isGETRequest && shouldRespond) {
event.respondWith(
caches.match(event.request, { cacheName: CACHE_NAME })
.then((response) => {
if (response) {
return response;
}
return fetch(event.request);
})
);
}
});
const VERSION$2 = '1';
const PATTERNS = ['http://localhost:8000/api/v1/(.*)'];
/**
* Create an absolute URL, allowing regex expressions to pass
*
* @param {string} url
* @param {string|object} baseUrl
* @public
*/
function createNormalizedUrl(url, baseUrl = self.location) {
return decodeURI(new URL(encodeURI(url), baseUrl).toString());
}
/**
* Create an (absolute) URL Regex from a given string
*
* @param {string} url
* @returns {RegExp}
* @public
*/
function createUrlRegEx(url) {
let normalized = createNormalizedUrl(url);
return new RegExp(`^${normalized}$`);
}
/**
* Check if given URL matches any pattern
*
* @param {string} url
* @param {array} patterns
* @returns {boolean}
* @public
*/
function urlMatchesAnyPattern(url, patterns) {
return !!patterns.find((pattern) => pattern.test(decodeURI(url)));
}
const CACHE_KEY_PREFIX$1 = 'esw-cache-fallback';
const CACHE_NAME$1 = `${CACHE_KEY_PREFIX$1}-${VERSION$2}`;
const PATTERN_REGEX = PATTERNS.map(createUrlRegEx);
self.addEventListener('fetch', (event) => {
let request = event.request;
if (request.method !== 'GET' || !/^https?/.test(request.url)) {
return;
}
if (urlMatchesAnyPattern(request.url, PATTERN_REGEX)) {
event.respondWith(
caches.open(CACHE_NAME$1).then((cache) => {
return fetch(request)
.then((response) => {
cache.put(request, response.clone());
return response;
})
.catch(() => caches.match(event.request));
})
);
}
});
self.addEventListener('activate', (event) => {
event.waitUntil(cleanupCaches(CACHE_KEY_PREFIX$1, CACHE_NAME$1));
});
const VERSION$3 = '1';
const INDEX_HTML_PATH = 'index.html';
const CACHE_KEY_PREFIX$2 = 'esw-index';
const CACHE_NAME$2 = `${CACHE_KEY_PREFIX$2}-${VERSION$3}`;
const INDEX_HTML_URL = new URL(INDEX_HTML_PATH, self.location).toString();
self.addEventListener('install', (event) => {
event.waitUntil(
fetch(INDEX_HTML_URL, { credentials: 'include' }).then((response) => {
return caches
.open(CACHE_NAME$2)
.then((cache) => cache.put(INDEX_HTML_URL, response));
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(cleanupCaches(CACHE_KEY_PREFIX$2, CACHE_NAME$2));
});
self.addEventListener('fetch', (event) => {
let request = event.request;
let isGETRequest = request.method === 'GET';
let isHTMLRequest = request.headers.get('accept').indexOf('text/html') !== -1;
let isLocal = new URL(request.url).origin === location.origin
if (isGETRequest && isHTMLRequest && isLocal) {
event.respondWith(
caches.match(INDEX_HTML_URL, { cacheName: CACHE_NAME$2 })
);
}
});
}());
This is how network requests appear in Chrome:Network request while offline
I assume the problem is in the configuration of ember-service-worker-cache-fallback. But I'm not quite sure about that. Any idea or link to a working example is welcome. I didn't find a lot about ember-service-worker-cache-fallback so far.
Thanks!
Aucun commentaire:
Enregistrer un commentaire