From 6d61b44466c18828858a86bdaa907a9313291273 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Jun 2022 11:13:21 +0200 Subject: [PATCH] errors: add ConsumedResponse type + use for ResponseError Signed-off-by: Patrik Oldsberg --- .changeset/sharp-numbers-taste.md | 7 +++++ packages/errors/api-report.md | 23 ++++++++++++-- packages/errors/src/errors/ResponseError.ts | 9 ++++-- packages/errors/src/errors/index.ts | 1 + packages/errors/src/errors/types.ts | 31 +++++++++++++++++++ packages/errors/src/serialization/response.ts | 3 +- 6 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 .changeset/sharp-numbers-taste.md create mode 100644 packages/errors/src/errors/types.ts diff --git a/.changeset/sharp-numbers-taste.md b/.changeset/sharp-numbers-taste.md new file mode 100644 index 0000000000..4819aca18f --- /dev/null +++ b/.changeset/sharp-numbers-taste.md @@ -0,0 +1,7 @@ +--- +'@backstage/errors': minor +--- + +The `ResponseError.fromResponse` now accepts a more narrow response type, in order to avoid incompatibilities between different fetch implementations. + +The `response` property of `ResponseError` has also been narrowed to a new `ConsumedResponse` type that omits all the properties for consuming the body of the response. This is not considered a breaking change as it was always an error to try to consume the body of the response. diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md index a01581135b..aab4e9baec 100644 --- a/packages/errors/api-report.md +++ b/packages/errors/api-report.md @@ -14,6 +14,17 @@ export class AuthenticationError extends CustomErrorBase {} // @public export class ConflictError extends CustomErrorBase {} +// @public +export type ConsumedResponse = { + readonly headers: Headers; + readonly ok: boolean; + readonly redirected: boolean; + readonly status: number; + readonly statusText: string; + readonly type: ResponseType; + readonly url: string; +}; + // @public export class CustomErrorBase extends Error { constructor(message?: string, cause?: Error | unknown); @@ -67,15 +78,21 @@ export class NotModifiedError extends CustomErrorBase {} // @public export function parseErrorResponseBody( - response: Response, + response: ConsumedResponse & { + text(): Promise; + }, ): Promise; // @public export class ResponseError extends Error { readonly body: ErrorResponseBody; readonly cause: Error; - static fromResponse(response: Response): Promise; - readonly response: Response; + static fromResponse( + response: ConsumedResponse & { + text(): Promise; + }, + ): Promise; + readonly response: ConsumedResponse; } // @public diff --git a/packages/errors/src/errors/ResponseError.ts b/packages/errors/src/errors/ResponseError.ts index 84c8137bb5..a26891ef6a 100644 --- a/packages/errors/src/errors/ResponseError.ts +++ b/packages/errors/src/errors/ResponseError.ts @@ -19,6 +19,7 @@ import { ErrorResponseBody, parseErrorResponseBody, } from '../serialization/response'; +import { ConsumedResponse } from './types'; /** * An error thrown as the result of a failed server request. @@ -34,7 +35,7 @@ export class ResponseError extends Error { * Note that the body of this response is always consumed. Its parsed form is * in the `body` field. */ - readonly response: Response; + readonly response: ConsumedResponse; /** * The parsed JSON error body, as sent by the server. @@ -59,7 +60,9 @@ export class ResponseError extends Error { * function consumes the body of the response, and assumes that it hasn't * been consumed before. */ - static async fromResponse(response: Response): Promise { + static async fromResponse( + response: ConsumedResponse & { text(): Promise }, + ): Promise { const data = await parseErrorResponseBody(response); const status = data.response.statusCode || response.status; @@ -77,7 +80,7 @@ export class ResponseError extends Error { private constructor(props: { message: string; - response: Response; + response: ConsumedResponse; data: ErrorResponseBody; cause: Error; }) { diff --git a/packages/errors/src/errors/index.ts b/packages/errors/src/errors/index.ts index bc2fb2aa4e..9e1c2408af 100644 --- a/packages/errors/src/errors/index.ts +++ b/packages/errors/src/errors/index.ts @@ -27,3 +27,4 @@ export { } from './common'; export { CustomErrorBase } from './CustomErrorBase'; export { ResponseError } from './ResponseError'; +export type { ConsumedResponse } from './types'; diff --git a/packages/errors/src/errors/types.ts b/packages/errors/src/errors/types.ts new file mode 100644 index 0000000000..a62975571f --- /dev/null +++ b/packages/errors/src/errors/types.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2022 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. + */ + +/** + * ConsumedResponse represents a Response that is known to have been consumed. + * The methods and properties used to read the body contents are therefore omitted. + * + * @public + */ +export type ConsumedResponse = { + readonly headers: Headers; + readonly ok: boolean; + readonly redirected: boolean; + readonly status: number; + readonly statusText: string; + readonly type: ResponseType; + readonly url: string; +}; diff --git a/packages/errors/src/serialization/response.ts b/packages/errors/src/serialization/response.ts index 3580220525..60894b243e 100644 --- a/packages/errors/src/serialization/response.ts +++ b/packages/errors/src/serialization/response.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ConsumedResponse } from '../errors/types'; import { SerializedError } from './error'; /** @@ -53,7 +54,7 @@ export type ErrorResponseBody = { * @param response - The response of a failed request */ export async function parseErrorResponseBody( - response: Response, + response: ConsumedResponse & { text(): Promise }, ): Promise { try { const text = await response.text();