kopia lustrzana https://gitlab.com/rysiekpl/libresilient
service-worker: code for handling edge-cases with new config.json configuring not-yet-loaded plugins (ref. #30)
rodzic
1fb9ab7e05
commit
50fad1daa0
|
@ -386,7 +386,7 @@ describe("service-worker", () => {
|
|||
test("basic set-up: a stale cached valid config.json file gets used, no fetch happens, fresh config.json is retrieved using the configured plugins and cached", async () => {
|
||||
self.LibResilientConfig = null
|
||||
|
||||
var configData = {loggedComponents: ['service-worker', 'cache', 'resolve-config'], plugins: [{name: "cache"}, {name: "resolve-config"}], defaultPluginTimeout: 5000}
|
||||
var configData = {loggedComponents: ['service-worker', 'cache', 'fetch'], plugins: [{name: "fetch"},{name: "cache"}], defaultPluginTimeout: 5000}
|
||||
var configUrl = '/config.json'
|
||||
var configResponse = new Response(
|
||||
new Blob(
|
||||
|
@ -410,7 +410,7 @@ describe("service-worker", () => {
|
|||
})
|
||||
|
||||
|
||||
var newConfigData = {loggedComponents: ['service-worker', 'resolve-config'], plugins: [{name: "resolve-config"}], defaultPluginTimeout: 2000}
|
||||
var newConfigData = {loggedComponents: ['service-worker', 'fetch'], plugins: [{name: "fetch"}], defaultPluginTimeout: 2000}
|
||||
let resolveConfigFetch = jest.fn((request, init)=>{
|
||||
return Promise.resolve(
|
||||
new Response(
|
||||
|
@ -430,10 +430,10 @@ describe("service-worker", () => {
|
|||
})
|
||||
)
|
||||
})
|
||||
global.LibResilientPluginConstructors.set('resolve-config', ()=>{
|
||||
global.LibResilientPluginConstructors.set('fetch', ()=>{
|
||||
return {
|
||||
name: 'resolve-config',
|
||||
description: 'Resolve with config data.',
|
||||
name: 'fetch',
|
||||
description: 'Resolve with config data (pretending to be fetch).',
|
||||
version: '0.0.1',
|
||||
fetch: resolveConfigFetch
|
||||
}
|
||||
|
@ -451,8 +451,8 @@ describe("service-worker", () => {
|
|||
// verify current config (the one from the pre-cached stale `config.json`)
|
||||
expect(typeof self.LibResilientConfig).toEqual('object')
|
||||
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(5000)
|
||||
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "cache"}, {name: "resolve-config"}])
|
||||
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache', 'resolve-config'])
|
||||
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
|
||||
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache', 'fetch'])
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
expect(resolveConfigFetch).toHaveBeenCalled();
|
||||
|
||||
|
@ -555,31 +555,90 @@ describe("service-worker", () => {
|
|||
})
|
||||
|
||||
|
||||
test("fetching content should work", async () => {
|
||||
self.LibResilientConfig = {
|
||||
plugins: [{
|
||||
name: 'fetch'
|
||||
}],
|
||||
loggedComponents: [
|
||||
'service-worker', 'fetch'
|
||||
]
|
||||
}
|
||||
require("../service-worker.js");
|
||||
test("basic set-up: a stale cached valid config.json file gets used, no fetch happens; valid config.json (configuring additional plugins) is retrieved using the configured plugins other than fetch, and is not cached", async () => {
|
||||
self.LibResilientConfig = null
|
||||
|
||||
var configData = {loggedComponents: ['service-worker', 'resolve-config'], plugins: [{name: "resolve-config"}], defaultPluginTimeout: 5000}
|
||||
var configUrl = '/config.json'
|
||||
var configResponse = new Response(
|
||||
new Blob(
|
||||
[JSON.stringify(configData)],
|
||||
{type: "application/json"}
|
||||
),
|
||||
{
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {
|
||||
'ETag': 'TestingETagHeader',
|
||||
// very stale date
|
||||
'Date': new Date(0).toUTCString()
|
||||
},
|
||||
url: configUrl
|
||||
})
|
||||
await caches
|
||||
.open('v1')
|
||||
.then((cache)=>{
|
||||
return cache.put(configUrl, configResponse)
|
||||
})
|
||||
|
||||
|
||||
var newConfigData = {loggedComponents: ['service-worker', 'resolve-config', 'cache'], plugins: [{name: "resolve-config"}, {name: "cache"}], defaultPluginTimeout: 2000}
|
||||
let resolveConfigFetch = jest.fn((request, init)=>{
|
||||
return Promise.resolve(
|
||||
new Response(
|
||||
new Blob(
|
||||
[JSON.stringify(newConfigData)],
|
||||
{type: "application/json"}
|
||||
),
|
||||
{
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {
|
||||
'ETag': 'TestingETagHeader',
|
||||
// very current date
|
||||
'Date': new Date().toUTCString()
|
||||
},
|
||||
url: configUrl
|
||||
})
|
||||
)
|
||||
})
|
||||
global.LibResilientPluginConstructors.set('resolve-config', ()=>{
|
||||
return {
|
||||
name: 'resolve-config',
|
||||
description: 'Resolve with config data.',
|
||||
version: '0.0.1',
|
||||
fetch: resolveConfigFetch
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
require("../service-worker.js");
|
||||
} catch(e) {}
|
||||
await self.trigger('install')
|
||||
// this is silly but works, and is necessary because
|
||||
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await self.trigger('activate')
|
||||
|
||||
var response = await self.trigger('fetch', new Request('/test.json'))
|
||||
expect(fetch).toHaveBeenCalled();
|
||||
expect(await response.json()).toEqual({ test: "success" })
|
||||
expect(response.headers.has('X-LibResilient-Method')).toEqual(true)
|
||||
expect(response.headers.get('X-LibResilient-Method')).toEqual('fetch')
|
||||
expect(response.headers.has('X-LibResilient-Etag')).toEqual(true)
|
||||
expect(response.headers.get('X-LibResilient-ETag')).toEqual('TestingETagHeader')
|
||||
});
|
||||
// verify current config (the one from the pre-cached stale `config.json`)
|
||||
expect(typeof self.LibResilientConfig).toEqual('object')
|
||||
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(configData.defaultPluginTimeout)
|
||||
expect(self.LibResilientConfig.plugins).toStrictEqual(configData.plugins)
|
||||
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(configData.loggedComponents)
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
expect(resolveConfigFetch).toHaveBeenCalled();
|
||||
|
||||
// verify that the *new* config got cached
|
||||
cdata = await caches
|
||||
.open('v1')
|
||||
.then((cache)=>{
|
||||
return cache.match(configUrl)
|
||||
})
|
||||
.then((cresponse)=>{
|
||||
return cresponse.json()
|
||||
})
|
||||
expect(cdata).toStrictEqual(configData)
|
||||
})
|
||||
|
||||
test("failed fetch by first configured plugin should not affect a successful fetch by a second one", async () => {
|
||||
self.LibResilientConfig = {
|
||||
|
|
|
@ -340,7 +340,35 @@ let initServiceWorker = async () => {
|
|||
|
||||
// did that work?
|
||||
if (cdata != false) {
|
||||
self.log('service-worker', `config.json successfully fetched through plugins; caching.`)
|
||||
|
||||
// if we got the new config.json via a method *other* than plain old fetch(),
|
||||
// we will not be able to use importScripts() to load any pugins that have not been loaded already
|
||||
if (cresponse.headers.get('x-libresilient-method') != 'fetch') {
|
||||
|
||||
// go through the plugins in the new config and check if we already have their constructors
|
||||
// i.e. if the plugin scripts have already been loaded
|
||||
for (p in cdata.plugins) {
|
||||
var pname = cdata.plugins[p].name
|
||||
|
||||
// plugin constructor not available, meaning: we'd have to importScripts() it
|
||||
// but we can't since this was not retrieved via fetch(), so we cannot assume
|
||||
// that the main domain of the website is up and available
|
||||
//
|
||||
// if we cache this newly retrieved config.json, next time the service worker gets restarted
|
||||
// we will end up with an error while trying to run importScripts() for this plugin
|
||||
// which in turn would lead to the service worker being unregistered
|
||||
//
|
||||
// if the main domain is not available, this would mean the website stops working
|
||||
// even though we *were* able to retrieve the new config.json via plugins!
|
||||
// so, ignoring this new config.json.
|
||||
if (!LibResilientPluginConstructors.has(pname)) {
|
||||
self.log('service-worker', `config.json was retrieved through plugins other than fetch, but specifies additional plugins (${pname}); ignoring.`)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.log('service-worker', `config.json successfully retrieved through plugins; caching.`)
|
||||
// cache it, asynchronously
|
||||
cacheConfigJSON(configURL, cresponse)
|
||||
}
|
||||
|
@ -351,6 +379,9 @@ let initServiceWorker = async () => {
|
|||
// we only get a cryptic "Error while registering a service worker"
|
||||
// unless we explicitly print the errors out in the console
|
||||
console.error(e)
|
||||
// we got an error while initializing the plugins
|
||||
// better play it safe!
|
||||
self.registration.unregister()
|
||||
throw e
|
||||
}
|
||||
return true;
|
||||
|
@ -589,7 +620,7 @@ let initFromRequest = (req) => {
|
|||
let libresilientFetch = (plugin, url, init, reqInfo) => {
|
||||
// status of the plugin
|
||||
reqInfo.update({
|
||||
method: plugin.name,
|
||||
method: (plugin && "name" in plugin) ? plugin.name : "unknown",
|
||||
state: "running"
|
||||
})
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue