From 8686eb38cf7c49de7875bcbb431de5717985376d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 8 Mar 2021 17:02:51 +0100 Subject: [PATCH] Introduce the `@backstage/errors` package. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Encode thrown errors in the backend as a JSON payload using a facility in that package, and render helpful frontend displays of those errors. Signed-off-by: Fredrik Adelöw --- .changeset/chilled-kiwis-bathe.md | 5 + .changeset/healthy-cars-do.md | 29 ++++ .changeset/olive-apples-shop.md | 5 + .changeset/silver-weeks-hide.md | 10 ++ .changeset/young-suits-sit.md | 10 ++ packages/backend-common/package.json | 1 + packages/backend-common/src/index.ts | 1 - .../src/middleware/errorHandler.test.ts | 66 +++++++-- .../src/middleware/errorHandler.ts | 59 ++++---- .../src/reading/AzureUrlReader.test.ts | 2 +- .../src/reading/AzureUrlReader.ts | 2 +- .../src/reading/BitbucketUrlReader.test.ts | 2 +- .../src/reading/BitbucketUrlReader.ts | 2 +- .../src/reading/FetchUrlReader.ts | 2 +- .../src/reading/GithubUrlReader.test.ts | 2 +- .../src/reading/GithubUrlReader.ts | 2 +- .../src/reading/GitlabUrlReader.test.ts | 2 +- .../src/reading/GitlabUrlReader.ts | 2 +- .../src/reading/UrlReaderPredicateMux.ts | 2 +- packages/catalog-client/package.json | 1 + packages/catalog-client/src/CatalogClient.ts | 15 +- packages/core/package.json | 1 + .../CopyTextButton/CopyTextButton.tsx | 23 +-- .../ErrorResponse/ErrorResponse.tsx | 135 ++++++++++++++++++ .../src/components/ErrorResponse/index.ts | 17 +++ packages/core/src/components/index.ts | 1 + packages/create-app/package.json | 1 + packages/create-app/src/lib/versions.ts | 2 + packages/errors/.eslintrc.js | 3 + packages/errors/CHANGELOG.md | 0 packages/errors/README.md | 9 ++ packages/errors/package.json | 42 ++++++ packages/errors/src/error/CustomErrorBase.ts | 37 +++++ .../src/error/common.test.ts} | 6 +- .../errors.ts => errors/src/error/common.ts} | 28 +--- packages/errors/src/error/index.ts | 25 ++++ packages/errors/src/error/server.ts | 43 ++++++ packages/errors/src/index.ts | 18 +++ .../payload/ServerResponseErrorBody.test.ts | 135 ++++++++++++++++++ .../src/payload/ServerResponseErrorBody.ts | 89 ++++++++++++ packages/errors/src/payload/index.ts | 18 +++ packages/errors/src/setupTests.ts | 17 +++ packages/techdocs-common/package.json | 1 + packages/techdocs-common/src/helpers.ts | 3 +- .../src/stages/prepare/commonGit.ts | 2 +- .../techdocs-common/src/stages/prepare/dir.ts | 8 +- .../techdocs-common/src/stages/prepare/url.ts | 4 +- plugins/auth-backend/package.json | 1 + .../src/lib/catalog/CatalogIdentityClient.ts | 2 +- .../src/lib/oauth/OAuthAdapter.ts | 2 +- .../src/lib/oauth/OAuthEnvironmentHandler.ts | 2 +- plugins/auth-backend/src/service/router.ts | 2 +- plugins/catalog-backend/package.json | 1 + .../src/catalog/DatabaseEntitiesCatalog.ts | 2 +- .../src/database/CommonDatabase.test.ts | 2 +- .../src/database/CommonDatabase.ts | 6 +- .../src/ingestion/LocationReaders.ts | 60 ++++++-- .../processors/CodeOwnersProcessor.ts | 3 +- .../src/ingestion/processors/results.ts | 2 +- .../src/service/EntityFilters.ts | 2 +- .../src/service/filterQuery.ts | 2 +- .../src/service/router.test.ts | 3 +- plugins/catalog-backend/src/service/router.ts | 13 +- plugins/catalog-backend/src/service/util.ts | 2 +- .../EntityPageLayout/EntityPageLayout.tsx | 4 +- plugins/scaffolder-backend/package.json | 1 + .../src/lib/catalog/CatalogEntityClient.ts | 2 +- .../actions/TemplateActionRegistry.ts | 2 +- .../actions/builtin/catalog/register.ts | 2 +- .../actions/builtin/fetch/cookiecutter.ts | 3 +- .../actions/builtin/fetch/helpers.ts | 3 +- .../scaffolder/actions/builtin/fetch/plain.ts | 3 +- .../actions/builtin/publish/azure.ts | 2 +- .../actions/builtin/publish/bitbucket.ts | 2 +- .../actions/builtin/publish/github.ts | 2 +- .../actions/builtin/publish/gitlab.ts | 2 +- .../actions/builtin/publish/util.ts | 2 +- .../src/scaffolder/stages/helpers.ts | 2 +- .../src/scaffolder/stages/prepare/file.ts | 2 +- .../scaffolder/stages/templater/helpers.ts | 2 +- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 7 +- .../src/scaffolder/tasks/TaskWorker.ts | 2 +- .../scaffolder-backend/src/service/router.ts | 8 +- plugins/techdocs-backend/package.json | 1 + .../src/DocsBuilder/builder.ts | 2 +- 85 files changed, 878 insertions(+), 175 deletions(-) create mode 100644 .changeset/chilled-kiwis-bathe.md create mode 100644 .changeset/healthy-cars-do.md create mode 100644 .changeset/olive-apples-shop.md create mode 100644 .changeset/silver-weeks-hide.md create mode 100644 .changeset/young-suits-sit.md create mode 100644 packages/core/src/components/ErrorResponse/ErrorResponse.tsx create mode 100644 packages/core/src/components/ErrorResponse/index.ts create mode 100644 packages/errors/.eslintrc.js create mode 100644 packages/errors/CHANGELOG.md create mode 100644 packages/errors/README.md create mode 100644 packages/errors/package.json create mode 100644 packages/errors/src/error/CustomErrorBase.ts rename packages/{backend-common/src/errors.test.ts => errors/src/error/common.test.ts} (91%) rename packages/{backend-common/src/errors.ts => errors/src/error/common.ts} (76%) create mode 100644 packages/errors/src/error/index.ts create mode 100644 packages/errors/src/error/server.ts create mode 100644 packages/errors/src/index.ts create mode 100644 packages/errors/src/payload/ServerResponseErrorBody.test.ts create mode 100644 packages/errors/src/payload/ServerResponseErrorBody.ts create mode 100644 packages/errors/src/payload/index.ts create mode 100644 packages/errors/src/setupTests.ts diff --git a/.changeset/chilled-kiwis-bathe.md b/.changeset/chilled-kiwis-bathe.md new file mode 100644 index 0000000000000..31e891a8fcd0c --- /dev/null +++ b/.changeset/chilled-kiwis-bathe.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': patch +--- + +Throw the new `ServerResponseError` from `@backstage/errors` diff --git a/.changeset/healthy-cars-do.md b/.changeset/healthy-cars-do.md new file mode 100644 index 0000000000000..8de19817093b1 --- /dev/null +++ b/.changeset/healthy-cars-do.md @@ -0,0 +1,29 @@ +--- +'@backstage/backend-common': minor +--- + +Encode thrown errors in the backend as a JSON payload. This is technically a breaking change, since the response format even of errors are part of the contract. If you relied on the response being text, you will now have some extra JSON "noise" in it. It should still be readable by end users though. + +Before: + +``` +NotFoundError: No entity named 'tara.macgovern2' found, with kind 'user' in namespace 'default' + at eval (webpack-internal:///../../plugins/catalog-backend/src/service/router.ts:117:17) +``` + +After: + +```json +{ + "error": { + "statusCode": 404, + "name": "NotFoundError", + "message": "No entity named 'tara.macgovern2' found, with kind 'user' in namespace 'default'", + "stack": "NotFoundError: No entity named 'tara.macgovern2' found, with kind 'user' in namespace 'default'\n at eval (webpack-internal:///../../plugins/catalog-backend/src/service/router.ts:117:17)" + }, + "request": { + "method": "GET", + "url": "/entities/by-name/user/default/tara.macgovern2" + } +} +``` diff --git a/.changeset/olive-apples-shop.md b/.changeset/olive-apples-shop.md new file mode 100644 index 0000000000000..160f72af5fc3f --- /dev/null +++ b/.changeset/olive-apples-shop.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Add a `ErrorResponsePanel` to render `ServerResponseError` from `@backstage/errors` diff --git a/.changeset/silver-weeks-hide.md b/.changeset/silver-weeks-hide.md new file mode 100644 index 0000000000000..15a75016a293b --- /dev/null +++ b/.changeset/silver-weeks-hide.md @@ -0,0 +1,10 @@ +--- +'@backstage/techdocs-common': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-techdocs-backend': patch +--- + +Use errors from `@backstage/errors` diff --git a/.changeset/young-suits-sit.md b/.changeset/young-suits-sit.md new file mode 100644 index 0000000000000..5728c52ef8809 --- /dev/null +++ b/.changeset/young-suits-sit.md @@ -0,0 +1,10 @@ +--- +'@backstage/backend-common': minor +--- + +Removed the custom error types (e.g. `NotFoundError`). Those are now instead in the new `@backstage/errors` package. This is a breaking change, and you will have to update your imports if you were using these types. + +```diff +-import { NotFoundError } from '@backstage/backend-common'; ++import { NotFoundError } from '@backstage/errors'; +``` diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 43b5114de455b..933d2ccb3a3d9 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -32,6 +32,7 @@ "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.2", "@backstage/config-loader": "^0.5.1", + "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.1", "@octokit/rest": "^18.0.12", "@types/cors": "^2.8.6", diff --git a/packages/backend-common/src/index.ts b/packages/backend-common/src/index.ts index f3653f2627573..29b1062750c47 100644 --- a/packages/backend-common/src/index.ts +++ b/packages/backend-common/src/index.ts @@ -17,7 +17,6 @@ export * from './config'; export * from './database'; export * from './discovery'; -export * from './errors'; export * from './hot'; export * from './logging'; export * from './middleware'; diff --git a/packages/backend-common/src/middleware/errorHandler.test.ts b/packages/backend-common/src/middleware/errorHandler.test.ts index 78baaf5090afe..50af586686bed 100644 --- a/packages/backend-common/src/middleware/errorHandler.test.ts +++ b/packages/backend-common/src/middleware/errorHandler.test.ts @@ -14,10 +14,17 @@ * limitations under the License. */ +import { + AuthenticationError, + ConflictError, + InputError, + NotAllowedError, + NotFoundError, + NotModifiedError, +} from '@backstage/errors'; import express from 'express'; import createError from 'http-errors'; import request from 'supertest'; -import * as errors from '../errors'; import { errorHandler } from './errorHandler'; describe('errorHandler', () => { @@ -31,17 +38,27 @@ describe('errorHandler', () => { const response = await request(app).get('/breaks'); expect(response.status).toBe(500); - expect(response.text).toBe('some message'); + expect(response.body).toEqual({ + error: { + statusCode: 500, + name: 'Error', + message: 'some message', + }, + request: { + method: 'GET', + url: '/breaks', + }, + }); }); - it('doesnt try to send the response again if its already been sent', async () => { + it('does not try to send the response again if its already been sent', async () => { const app = express(); const mockSend = jest.fn(); app.use('/works_with_async_fail', (_, res) => { res.status(200).send('hello'); - // mutate the response object to test the middlware. + // mutate the response object to test the middleware. // it's hard to catch errors inside middleware from the outside. // @ts-ignore res.send = mockSend; @@ -67,38 +84,61 @@ describe('errorHandler', () => { const response = await request(app).get('/breaks'); expect(response.status).toBe(432); - expect(response.text).toContain('Some Message'); + expect(response.body).toEqual({ + error: { + statusCode: 432, + name: 'BadRequestError', + message: 'Some Message', + }, + request: { + method: 'GET', + url: '/breaks', + }, + }); }); it('handles well-known error classes', async () => { const app = express(); app.use('/NotModifiedError', () => { - throw new errors.NotModifiedError(); + throw new NotModifiedError(); }); app.use('/InputError', () => { - throw new errors.InputError(); + throw new InputError(); }); app.use('/AuthenticationError', () => { - throw new errors.AuthenticationError(); + throw new AuthenticationError(); }); app.use('/NotAllowedError', () => { - throw new errors.NotAllowedError(); + throw new NotAllowedError(); }); app.use('/NotFoundError', () => { - throw new errors.NotFoundError(); + throw new NotFoundError(); }); app.use('/ConflictError', () => { - throw new errors.ConflictError(); + throw new ConflictError(); }); app.use(errorHandler()); const r = request(app); expect((await r.get('/NotModifiedError')).status).toBe(304); expect((await r.get('/InputError')).status).toBe(400); + expect((await r.get('/InputError')).body.error.name).toBe('InputError'); expect((await r.get('/AuthenticationError')).status).toBe(401); + expect((await r.get('/AuthenticationError')).body.error.name).toBe( + 'AuthenticationError', + ); expect((await r.get('/NotAllowedError')).status).toBe(403); + expect((await r.get('/NotAllowedError')).body.error.name).toBe( + 'NotAllowedError', + ); expect((await r.get('/NotFoundError')).status).toBe(404); + expect((await r.get('/NotFoundError')).body.error.name).toBe( + 'NotFoundError', + ); expect((await r.get('/ConflictError')).status).toBe(409); + expect((await r.get('/ConflictError')).body.error.name).toBe( + 'ConflictError', + ); }); it('logs all 500 errors', async () => { @@ -126,7 +166,7 @@ describe('errorHandler', () => { mockLogger.child.mockImplementation(() => mockLogger as any); app.use('/NotFound', () => { - throw new errors.NotFoundError(); + throw new NotFoundError(); }); app.use(errorHandler({ logger: mockLogger as any })); @@ -142,7 +182,7 @@ describe('errorHandler', () => { mockLogger.child.mockImplementation(() => mockLogger as any); app.use('/NotFound', () => { - throw new errors.NotFoundError(); + throw new NotFoundError(); }); app.use(errorHandler({ logger: mockLogger as any, logClientErrors: true })); diff --git a/packages/backend-common/src/middleware/errorHandler.ts b/packages/backend-common/src/middleware/errorHandler.ts index 4b609a649f469..4dc6d4c85f9c8 100644 --- a/packages/backend-common/src/middleware/errorHandler.ts +++ b/packages/backend-common/src/middleware/errorHandler.ts @@ -16,7 +16,15 @@ import { ErrorRequestHandler, NextFunction, Request, Response } from 'express'; import { Logger } from 'winston'; -import * as errors from '../errors'; +import { + NotModifiedError, + InputError, + AuthenticationError, + NotAllowedError, + NotFoundError, + ConflictError, + ServerResponseErrorBody, +} from '@backstage/errors'; import { getRootLogger } from '../logging'; export type ErrorHandlerOptions = { @@ -48,14 +56,13 @@ export type ErrorHandlerOptions = { * This is commonly the very last middleware in the chain. * * Its primary purpose is not to do translation of business logic exceptions, - * but rather to be a gobal catch-all for uncaught "fatal" errors that are + * but rather to be a global catch-all for uncaught "fatal" errors that are * expected to result in a 500 error. However, it also does handle some common * error types (such as http-error exceptions) and returns the enclosed status * code accordingly. * * @returns An Express error request handler */ - export function errorHandler( options: ErrorHandlerOptions = {}, ): ErrorRequestHandler { @@ -66,12 +73,12 @@ export function errorHandler( type: 'errorHandler', }); - return ( - error: Error, - _request: Request, - res: Response, - next: NextFunction, - ) => { + return (error: Error, req: Request, res: Response, next: NextFunction) => { + const statusCode = getStatusCode(error); + if (options.logClientErrors || statusCode >= 500) { + logger.error(error); + } + if (res.headersSent) { // If the headers have already been sent, do not send the response again // as this will throw an error in the backend. @@ -79,16 +86,20 @@ export function errorHandler( return; } - const status = getStatusCode(error); - const message = showStackTraces ? error.stack : error.message; - - if (options.logClientErrors || status >= 500) { - logger.error(error); - } + const body: ServerResponseErrorBody = { + error: { + statusCode, + name: error.name || 'Error', + message: error.message || '', + stack: showStackTraces ? error.stack : undefined, + }, + request: { + method: req.method, + url: req.url, + }, + }; - res.status(status); - res.setHeader('content-type', 'text/plain'); - res.send(message); + res.status(statusCode).json(body); }; } @@ -109,17 +120,17 @@ function getStatusCode(error: Error): number { // Handle well-known error types switch (error.name) { - case errors.NotModifiedError.name: + case NotModifiedError.name: return 304; - case errors.InputError.name: + case InputError.name: return 400; - case errors.AuthenticationError.name: + case AuthenticationError.name: return 401; - case errors.NotAllowedError.name: + case NotAllowedError.name: return 403; - case errors.NotFoundError.name: + case NotFoundError.name: return 404; - case errors.ConflictError.name: + case ConflictError.name: return 409; default: break; diff --git a/packages/backend-common/src/reading/AzureUrlReader.test.ts b/packages/backend-common/src/reading/AzureUrlReader.test.ts index b52097baf55c0..bb761812876e7 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.test.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.test.ts @@ -26,7 +26,7 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import * as os from 'os'; import path from 'path'; -import { NotModifiedError } from '../errors'; +import { NotModifiedError } from '@backstage/errors'; import { getVoidLogger } from '../logging'; import { AzureUrlReader } from './AzureUrlReader'; import { ReadTreeResponseFactory } from './tree'; diff --git a/packages/backend-common/src/reading/AzureUrlReader.ts b/packages/backend-common/src/reading/AzureUrlReader.ts index 21b988e5b07ed..16e7628ec8406 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.ts @@ -26,7 +26,7 @@ import fetch from 'cross-fetch'; import parseGitUrl from 'git-url-parse'; import { Minimatch } from 'minimatch'; import { Readable } from 'stream'; -import { NotFoundError, NotModifiedError } from '../errors'; +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { ReadTreeResponseFactory } from './tree'; import { stripFirstDirectoryFromPath } from './tree/util'; import { diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts index df9febb352add..aa78bf27d5bac 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts @@ -26,7 +26,7 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import os from 'os'; import path from 'path'; -import { NotModifiedError } from '../errors'; +import { NotModifiedError } from '@backstage/errors'; import { BitbucketUrlReader } from './BitbucketUrlReader'; import { ReadTreeResponseFactory } from './tree'; diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.ts b/packages/backend-common/src/reading/BitbucketUrlReader.ts index 3954e7ee4a47a..f102be7283730 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.ts @@ -26,7 +26,7 @@ import fetch from 'cross-fetch'; import parseGitUrl from 'git-url-parse'; import { Minimatch } from 'minimatch'; import { Readable } from 'stream'; -import { NotFoundError, NotModifiedError } from '../errors'; +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { ReadTreeResponseFactory } from './tree'; import { stripFirstDirectoryFromPath } from './tree/util'; import { diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index 0b26dfe911c43..57bab5e58d86b 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -15,7 +15,7 @@ */ import fetch from 'cross-fetch'; -import { NotFoundError } from '../errors'; +import { NotFoundError } from '@backstage/errors'; import { ReaderFactory, ReadTreeResponse, diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index 94e4d0fd315ce..67e7f27069093 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -27,7 +27,7 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import os from 'os'; import path from 'path'; -import { NotFoundError, NotModifiedError } from '../errors'; +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { GhBlobResponse, GhBranchResponse, diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index ac5a93bd5fb8e..77e33cbd541eb 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -25,7 +25,7 @@ import fetch from 'cross-fetch'; import parseGitUrl from 'git-url-parse'; import { Minimatch } from 'minimatch'; import { Readable } from 'stream'; -import { NotFoundError, NotModifiedError } from '../errors'; +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { ReadTreeResponseFactory } from './tree'; import { ReaderFactory, diff --git a/packages/backend-common/src/reading/GitlabUrlReader.test.ts b/packages/backend-common/src/reading/GitlabUrlReader.test.ts index 636540caa816b..9dcc92a4a6355 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.test.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.test.ts @@ -25,7 +25,7 @@ import path from 'path'; import { getVoidLogger } from '../logging'; import { GitlabUrlReader } from './GitlabUrlReader'; import { ReadTreeResponseFactory } from './tree'; -import { NotModifiedError, NotFoundError } from '../errors'; +import { NotModifiedError, NotFoundError } from '@backstage/errors'; import { GitLabIntegration, readGitLabIntegrationConfig, diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts index 66ceae05c6556..c93103b2da2f5 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.ts @@ -24,7 +24,7 @@ import fetch from 'cross-fetch'; import parseGitUrl from 'git-url-parse'; import { Minimatch } from 'minimatch'; import { Readable } from 'stream'; -import { NotFoundError, NotModifiedError } from '../errors'; +import { NotFoundError, NotModifiedError } from '@backstage/errors'; import { ReadTreeResponseFactory } from './tree'; import { stripFirstDirectoryFromPath } from './tree/util'; import { diff --git a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts index 91f4ae63ab36a..06ca0c5971b8f 100644 --- a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts +++ b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { NotAllowedError } from '../errors'; +import { NotAllowedError } from '@backstage/errors'; import { ReadTreeOptions, ReadTreeResponse, diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 9115a29ab36c6..36d63a42304a2 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -31,6 +31,7 @@ "dependencies": { "@backstage/catalog-model": "^0.7.4", "@backstage/config": "^0.1.2", + "@backstage/errors": "^0.1.1", "cross-fetch": "^3.0.6" }, "devDependencies": { diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index a1af0240f6487..2d9a07a7a801e 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -21,6 +21,7 @@ import { LOCATION_ANNOTATION, stringifyLocationReference, } from '@backstage/catalog-model'; +import { ServerResponseError } from '@backstage/errors'; import fetch from 'cross-fetch'; import { AddLocationRequest, @@ -153,10 +154,7 @@ export class CatalogClient implements CatalogApi { }, ); if (!response.ok) { - const payload = await response.text(); - throw new Error( - `Request failed with ${response.status} ${response.statusText}, ${payload}`, - ); + throw await ServerResponseError.forResponse(response); } return undefined; } @@ -177,9 +175,7 @@ export class CatalogClient implements CatalogApi { }); if (!response.ok) { - const payload = await response.text(); - const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; - throw new Error(message); + throw await ServerResponseError.forResponse(response); } return await response.json(); @@ -200,10 +196,7 @@ export class CatalogClient implements CatalogApi { if (response.status === 404) { return undefined; } - - const payload = await response.text(); - const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`; - throw new Error(message); + throw await ServerResponseError.forResponse(response); } return await response.json(); diff --git a/packages/core/package.json b/packages/core/package.json index 6eda51100fc52..4a95fe63e28d3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -31,6 +31,7 @@ "dependencies": { "@backstage/config": "^0.1.3", "@backstage/core-api": "^0.2.13", + "@backstage/errors": "^0.1.1", "@backstage/theme": "^0.2.4", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", diff --git a/packages/core/src/components/CopyTextButton/CopyTextButton.tsx b/packages/core/src/components/CopyTextButton/CopyTextButton.tsx index 9f6cdd7e7cc08..7f20aed496f60 100644 --- a/packages/core/src/components/CopyTextButton/CopyTextButton.tsx +++ b/packages/core/src/components/CopyTextButton/CopyTextButton.tsx @@ -14,21 +14,11 @@ * limitations under the License. */ -import React, { useRef, useState, MouseEventHandler } from 'react'; -import { IconButton, makeStyles, Tooltip } from '@material-ui/core'; -import PropTypes from 'prop-types'; -import CopyIcon from '@material-ui/icons/FileCopy'; -import { BackstageTheme } from '@backstage/theme'; import { errorApiRef, useApi } from '@backstage/core-api'; - -const useStyles = makeStyles(theme => ({ - button: { - '&:hover': { - backgroundColor: theme.palette.highlight, - cursor: 'pointer', - }, - }, -})); +import { IconButton, Tooltip } from '@material-ui/core'; +import CopyIcon from '@material-ui/icons/FileCopy'; +import PropTypes from 'prop-types'; +import React, { MouseEventHandler, useRef, useState } from 'react'; /** * Copy text button with visual feedback in the form of @@ -61,7 +51,6 @@ export const CopyTextButton = (props: Props) => { ...defaultProps, ...props, }; - const classes = useStyles(props); const errorApi = useApi(errorApiRef); const inputRef = useRef(null); const [open, setOpen] = useState(false); @@ -84,7 +73,7 @@ export const CopyTextButton = (props: Props) => { <>