diff --git a/.changeset/fair-knives-relax.md b/.changeset/fair-knives-relax.md new file mode 100644 index 0000000000..bc1357d841 --- /dev/null +++ b/.changeset/fair-knives-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +Introducing new UnhandledErrorForwarder installed by default. For catching unhandled promise rejections, you can override the API to align with general error handling. diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index 308544e0f1..56dd46794a 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -71,8 +71,11 @@ For example, this is the default `ApiFactory` for the `ErrorApi`: createApiFactory({ api: errorApiRef, deps: { alertApi: alertApiRef }, - factory: ({ alertApi }) => - new ErrorAlerter(alertApi, new ErrorApiForwarder()), + factory: ({ alertApi }) => { + const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); + UnhandledErrorForwarder.forward(errorApi, { hidden: false }); + return errorApi; + }, }); ``` diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 5aa709121a..ce85078a88 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -394,6 +394,11 @@ export type SignInResult = { signOut?: () => Promise; }; +// @public (undocumented) +export class UnhandledErrorForwarder { + static forward(errorApi: ErrorApi, errorContext: ErrorContext): void; +} + // @public export class UrlPatternDiscovery implements DiscoveryApi { static compile(pattern: string): UrlPatternDiscovery; diff --git a/packages/core-app-api/src/apis/implementations/ErrorApi/UnhandledErrorForwarder.ts b/packages/core-app-api/src/apis/implementations/ErrorApi/UnhandledErrorForwarder.ts new file mode 100644 index 0000000000..b859148e8e --- /dev/null +++ b/packages/core-app-api/src/apis/implementations/ErrorApi/UnhandledErrorForwarder.ts @@ -0,0 +1,31 @@ +import { ErrorApi, ErrorContext } from '@backstage/core-plugin-api'; + +/* + * Copyright 2020 Spotify AB + * + * 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. + */ + +export class UnhandledErrorForwarder { + /** + * Add event listener, such that unhandled errors can be forwarded using an given `ErrorApi` instance + */ + static forward(errorApi: ErrorApi, errorContext: ErrorContext) { + window.addEventListener( + 'unhandledrejection', + (e: PromiseRejectionEvent) => { + errorApi.post(e.reason as Error, errorContext); + }, + ); + } +} diff --git a/packages/core-app-api/src/apis/implementations/ErrorApi/index.ts b/packages/core-app-api/src/apis/implementations/ErrorApi/index.ts index 49e12b4646..0d82894060 100644 --- a/packages/core-app-api/src/apis/implementations/ErrorApi/index.ts +++ b/packages/core-app-api/src/apis/implementations/ErrorApi/index.ts @@ -16,3 +16,4 @@ export { ErrorAlerter } from './ErrorAlerter'; export { ErrorApiForwarder } from './ErrorApiForwarder'; +export { UnhandledErrorForwarder } from './UnhandledErrorForwarder'; diff --git a/packages/core-app-api/src/app/defaultApis.ts b/packages/core-app-api/src/app/defaultApis.ts index 815418ac6c..2322f72e8d 100644 --- a/packages/core-app-api/src/app/defaultApis.ts +++ b/packages/core-app-api/src/app/defaultApis.ts @@ -30,6 +30,7 @@ import { UrlPatternDiscovery, SamlAuth, OneLoginAuth, + UnhandledErrorForwarder, } from '../apis'; import { @@ -67,8 +68,11 @@ export const defaultApis = [ createApiFactory({ api: errorApiRef, deps: { alertApi: alertApiRef }, - factory: ({ alertApi }) => - new ErrorAlerter(alertApi, new ErrorApiForwarder()), + factory: ({ alertApi }) => { + const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder()); + UnhandledErrorForwarder.forward(errorApi, { hidden: false }); + return errorApi; + }, }), createApiFactory({ api: storageApiRef, diff --git a/packages/core-app-api/src/index.test.ts b/packages/core-app-api/src/index.test.ts index 55ed5c7a38..1d17e78402 100644 --- a/packages/core-app-api/src/index.test.ts +++ b/packages/core-app-api/src/index.test.ts @@ -37,6 +37,7 @@ describe('index', () => { ConfigReader: expect.any(Function), ErrorAlerter: expect.any(Function), ErrorApiForwarder: expect.any(Function), + UnhandledErrorForwarder: expect.any(Function), FeatureFlagged: expect.any(Function), GithubAuth: expect.any(Function), GitlabAuth: expect.any(Function),