From 2bb7da0f274f4d4363e8ec791c4918643c9c055a Mon Sep 17 00:00:00 2001 From: Jordan Reimer Date: Wed, 2 Nov 2022 13:23:09 -0600 Subject: [PATCH] Raft Snapshot Download Bug (#17769) * moves service worker message event listener from addon to raft-storage-overview component * adds changelog entry * adds raft-storage-overview test for downloading snapshot via service worker --- changelog/17769.txt | 3 ++ ui/app/components/raft-storage-overview.js | 24 +++++++++- .../service-worker-registration/index.js | 26 ----------- .../components/raft-storage-overview-test.js | 45 +++++++++++++++++-- 4 files changed, 68 insertions(+), 30 deletions(-) create mode 100644 changelog/17769.txt diff --git a/changelog/17769.txt b/changelog/17769.txt new file mode 100644 index 000000000..12fe2d4d1 --- /dev/null +++ b/changelog/17769.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes issue with not being able to download raft snapshot via service worker +``` \ No newline at end of file diff --git a/ui/app/components/raft-storage-overview.js b/ui/app/components/raft-storage-overview.js index 794f99b1a..b77028e67 100644 --- a/ui/app/components/raft-storage-overview.js +++ b/ui/app/components/raft-storage-overview.js @@ -5,6 +5,8 @@ import { inject as service } from '@ember/service'; export default Component.extend({ flashMessages: service(), + auth: service(), + useServiceWorker: null, async init() { @@ -16,12 +18,32 @@ export default Component.extend({ if ('serviceWorker' in navigator) { // this checks to see if there's an active service worker - if it failed to register // for any reason, then this would be null - let worker = await navigator.serviceWorker.getRegistration(config.serviceWorkerScope); + const worker = await navigator.serviceWorker.getRegistration(config.serviceWorkerScope); if (worker) { + navigator.serviceWorker.addEventListener('message', this.serviceWorkerGetToken.bind(this)); + this.set('useServiceWorker', true); } } }, + willDestroy() { + if (this.useServiceWorker) { + navigator.serviceWorker.removeEventListener('message', this.serviceWorkerGetToken); + } + this._super(...arguments); + }, + + serviceWorkerGetToken(event) { + const { action } = event.data; + const [port] = event.ports; + + if (action === 'getToken') { + port.postMessage({ token: this.auth.currentToken }); + } else { + console.error('Unknown event', event); // eslint-disable-line + port.postMessage({ error: 'Unknown request' }); + } + }, actions: { async removePeer(model) { diff --git a/ui/lib/service-worker-authenticated-download/service-worker-registration/index.js b/ui/lib/service-worker-authenticated-download/service-worker-registration/index.js index 525317e81..6236505e0 100644 --- a/ui/lib/service-worker-authenticated-download/service-worker-registration/index.js +++ b/ui/lib/service-worker-authenticated-download/service-worker-registration/index.js @@ -1,32 +1,6 @@ import { addSuccessHandler } from 'ember-service-worker/service-worker-registration'; -import Namespace from '@ember/application/namespace'; - -function getToken() { - // fix this later by allowing registration somewhere in the app lifecycle were we can have access to - // services, etc. - return Namespace.NAMESPACES_BY_ID['vault'].__container__.lookup('service:auth').currentToken; -} addSuccessHandler(function (registration) { - // attach the handler for the message event so we can send over the auth token - navigator.serviceWorker.addEventListener('message', (event) => { - let { action } = event.data; - let port = event.ports[0]; - - if (action === 'getToken') { - let token = getToken(); - if (!token) { - console.error('Unable to retrieve Vault tokent'); // eslint-disable-line - } - port.postMessage({ token: token }); - } else { - console.error('Unknown event', event); // eslint-disable-line - port.postMessage({ - error: 'Unknown request', - }); - } - }); - // attempt to unregister the service worker on unload because we're not doing any sort of caching window.addEventListener('unload', function () { registration.unregister(); diff --git a/ui/tests/integration/components/raft-storage-overview-test.js b/ui/tests/integration/components/raft-storage-overview-test.js index dde28f67e..54b289439 100644 --- a/ui/tests/integration/components/raft-storage-overview-test.js +++ b/ui/tests/integration/components/raft-storage-overview-test.js @@ -2,17 +2,56 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; +import sinon from 'sinon'; module('Integration | Component | raft-storage-overview', function (hooks) { setupRenderingTest(hooks); - test('it renders', async function (assert) { - let model = [ + hooks.beforeEach(function () { + this.model = [ { address: '127.0.0.1:8200', voter: true }, { address: '127.0.0.1:8200', voter: true, leader: true }, ]; - this.set('model', model); + }); + + test('it renders', async function (assert) { await render(hbs``); assert.dom('[data-raft-row]').exists({ count: 2 }); }); + + test('it should download snapshot via service worker', async function (assert) { + assert.expect(3); + + const token = this.owner.lookup('service:auth').currentToken; + const generateMockEvent = (action) => ({ + data: { action }, + ports: [ + { + postMessage(message) { + const getToken = action === 'getToken'; + const expected = getToken ? { token } : { error: 'Unknown request' }; + assert.deepEqual( + message, + expected, + `${ + getToken ? 'Token' : 'Error' + } is returned to service worker in message event listener callback` + ); + }, + }, + ], + }); + + sinon.stub(navigator.serviceWorker, 'getRegistration').resolves(true); + sinon.stub(navigator.serviceWorker, 'addEventListener').callsFake((name, cb) => { + assert.strictEqual(name, 'message', 'Event listener added for service worker message'); + cb(generateMockEvent('getToken')); + cb(generateMockEvent('unknown')); + }); + + await render(hbs``); + // avoid clicking the download button or the url will change + // the service worker invokes the event listener callback when it intercepts the request to /v1/sys/storage/raft/snapshot + // for the test we manually fire the callback as soon as it is passed to the addEventListener stub + }); });