From 8a3e465910a3f41450a1a1bf6aafd418462c53e5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Aug 2021 19:12:43 +0200 Subject: [PATCH] techdocs: switch to xhr-based event source polyfill Signed-off-by: Patrik Oldsberg --- .changeset/pink-pugs-relate.md | 5 ++++ plugins/techdocs/package.json | 3 +-- plugins/techdocs/src/client.test.ts | 25 ++++++++--------- plugins/techdocs/src/client.ts | 5 ++-- .../techdocs/src/event-source-polyfill.d.ts | 27 +++++++++++++++++++ yarn.lock | 17 ++++-------- 6 files changed, 54 insertions(+), 28 deletions(-) create mode 100644 .changeset/pink-pugs-relate.md create mode 100644 plugins/techdocs/src/event-source-polyfill.d.ts diff --git a/.changeset/pink-pugs-relate.md b/.changeset/pink-pugs-relate.md new file mode 100644 index 0000000000..e9de00901e --- /dev/null +++ b/.changeset/pink-pugs-relate.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Switch `EventSource` implementation with header support from a Node.js API-based one to an XHR-based one. diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index b853ae04f7..7051109c2e 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -45,7 +45,7 @@ "@material-ui/lab": "4.0.0-alpha.45", "@material-ui/styles": "^4.10.0", "dompurify": "^2.2.9", - "eventsource": "^1.1.0", + "event-source-polyfill": "^1.0.25", "react": "^16.13.1", "react-dom": "^16.13.1", "react-lazylog": "^4.5.2", @@ -65,7 +65,6 @@ "@testing-library/react-hooks": "^3.4.2", "@testing-library/user-event": "^13.1.8", "@types/dompurify": "^2.2.2", - "@types/eventsource": "^1.1.5", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", "@types/react": "*", diff --git a/plugins/techdocs/src/client.test.ts b/plugins/techdocs/src/client.test.ts index aa57c97c65..7624f250d9 100644 --- a/plugins/techdocs/src/client.test.ts +++ b/plugins/techdocs/src/client.test.ts @@ -18,13 +18,14 @@ import { Config } from '@backstage/config'; import { UrlPatternDiscovery } from '@backstage/core-app-api'; import { IdentityApi } from '@backstage/core-plugin-api'; import { NotFoundError } from '@backstage/errors'; -import EventSource from 'eventsource'; +import { EventSourcePolyfill } from 'event-source-polyfill'; import { TechDocsStorageClient } from './client'; -const MockedEventSource: jest.MockedClass = - EventSource as any; +const MockedEventSource = EventSourcePolyfill as jest.MockedClass< + typeof EventSourcePolyfill +>; -jest.mock('eventsource'); +jest.mock('event-source-polyfill'); const mockEntity = { kind: 'Component', @@ -82,7 +83,7 @@ describe('TechDocsStorageClient', () => { MockedEventSource.prototype.addEventListener.mockImplementation( (type, fn) => { - if (type === 'finish') { + if (type === 'finish' && typeof fn === 'function') { fn({ data: '{"updated": false}' } as any); } }, @@ -106,7 +107,7 @@ describe('TechDocsStorageClient', () => { MockedEventSource.prototype.addEventListener.mockImplementation( (type, fn) => { - if (type === 'finish') { + if (type === 'finish' && typeof fn === 'function') { fn({ data: '{"updated": false}' } as any); } }, @@ -132,7 +133,7 @@ describe('TechDocsStorageClient', () => { MockedEventSource.prototype.addEventListener.mockImplementation( (type, fn) => { - if (type === 'finish') { + if (type === 'finish' && typeof fn === 'function') { fn({ data: '{"updated": false}' } as any); } }, @@ -153,7 +154,7 @@ describe('TechDocsStorageClient', () => { MockedEventSource.prototype.addEventListener.mockImplementation( (type, fn) => { - if (type === 'finish') { + if (type === 'finish' && typeof fn === 'function') { fn({ data: '{"updated": true}' } as any); } }, @@ -174,11 +175,11 @@ describe('TechDocsStorageClient', () => { MockedEventSource.prototype.addEventListener.mockImplementation( (type, fn) => { - if (type === 'log') { + if (type === 'log' && typeof fn === 'function') { fn({ data: '"A log message"' } as any); } - if (type === 'finish') { + if (type === 'finish' && typeof fn === 'function') { fn({ data: '{"updated": false}' } as any); } }, @@ -210,7 +211,7 @@ describe('TechDocsStorageClient', () => { const instance = MockedEventSource.mock .instances[0] as jest.Mocked; - instance.onerror({ + instance.onerror?.({ status: 404, message: 'Some not found warning', } as any); @@ -236,7 +237,7 @@ describe('TechDocsStorageClient', () => { const instance = MockedEventSource.mock .instances[0] as jest.Mocked; - instance.onerror({ + instance.onerror?.({ type: 'error', data: 'Some other error', } as any); diff --git a/plugins/techdocs/src/client.ts b/plugins/techdocs/src/client.ts index f867ce4011..8a3ad59c03 100644 --- a/plugins/techdocs/src/client.ts +++ b/plugins/techdocs/src/client.ts @@ -18,7 +18,7 @@ import { EntityName } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; import { NotFoundError, ResponseError } from '@backstage/errors'; -import EventSource from 'eventsource'; +import { EventSourcePolyfill } from 'event-source-polyfill'; import { SyncResult, TechDocsApi, TechDocsStorageApi } from './api'; import { TechDocsEntityMetadata, TechDocsMetadata } from './types'; @@ -214,7 +214,8 @@ export class TechDocsStorageClient implements TechDocsStorageApi { const token = await this.identityApi.getIdToken(); return new Promise((resolve, reject) => { - const source = new EventSource(url, { + // Polyfill is used to add support for custom headers and auth + const source = new EventSourcePolyfill(url, { withCredentials: true, headers: token ? { Authorization: `Bearer ${token}` } : {}, }); diff --git a/plugins/techdocs/src/event-source-polyfill.d.ts b/plugins/techdocs/src/event-source-polyfill.d.ts new file mode 100644 index 0000000000..8e298bde38 --- /dev/null +++ b/plugins/techdocs/src/event-source-polyfill.d.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare module 'event-source-polyfill' { + export class EventSourcePolyfill extends EventSource { + constructor( + url: string, + options?: { + withCredentials?: boolean; + headers?: HeadersInit; + }, + ); + } +} diff --git a/yarn.lock b/yarn.lock index 49a7a58059..fc89cd4476 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6574,11 +6574,6 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== -"@types/eventsource@^1.1.5": - version "1.1.5" - resolved "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.1.5.tgz#408e9b45efb176c8bea672ab58c81e7ab00d24bc" - integrity sha512-BA9q9uC2PAMkUS7DunHTxWZZaVpeNzDG8lkBxcKwzKJClfDQ4Z59/Csx7HSH/SIqFN2JWh0tAKAM6k/wRR0OZg== - "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.21": version "4.17.24" resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" @@ -13219,6 +13214,11 @@ event-emitter@^0.3.5: d "1" es5-ext "~0.10.14" +event-source-polyfill@^1.0.25: + version "1.0.25" + resolved "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.25.tgz#d8bb7f99cb6f8119c2baf086d9f6ee0514b6d9c8" + integrity sha512-hQxu6sN1Eq4JjoI7ITdQeGGUN193A2ra83qC0Ltm9I2UJVAten3OFVN6k5RX4YWeCS0BoC8xg/5czOCIHVosQg== + event-stream@=3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" @@ -13279,13 +13279,6 @@ eventsource@^1.0.5: dependencies: original "^1.0.0" -eventsource@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" - integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== - dependencies: - original "^1.0.0" - evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"