Merge pull request #8144 from backstage/jhaals/errorz

errors: rework error response naming
This commit is contained in:
Patrik Oldsberg
2021-11-19 09:49:39 +01:00
committed by GitHub
6 changed files with 108 additions and 46 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/errors': patch
---
Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`.
Rename the error name for unknown errors from `unknown` to `error`.
+15 -4
View File
@@ -33,8 +33,11 @@ export type ErrorLike = {
[unknownKeys: string]: unknown;
};
// @public @deprecated
export type ErrorResponse = ErrorResponseBody;
// @public
export type ErrorResponse = {
export type ErrorResponseBody = {
error: SerializedError;
request?: {
method: string;
@@ -65,19 +68,27 @@ export class NotFoundError extends CustomErrorBase {}
// @public
export class NotModifiedError extends CustomErrorBase {}
// @public
// @public @deprecated
export function parseErrorResponse(response: Response): Promise<ErrorResponse>;
// @public
export function parseErrorResponseBody(
response: Response,
): Promise<ErrorResponseBody>;
// @public
export class ResponseError extends Error {
// @deprecated
constructor(props: {
message: string;
response: Response;
data: ErrorResponse;
data: ErrorResponseBody;
cause: Error;
});
readonly body: ErrorResponseBody;
readonly cause: Error;
readonly data: ErrorResponse;
// @deprecated
get data(): ErrorResponseBody;
static fromResponse(response: Response): Promise<ResponseError>;
readonly response: Response;
}
+21 -9
View File
@@ -14,16 +14,16 @@
* limitations under the License.
*/
import { deserializeError } from '../serialization';
import {
parseErrorResponse,
ErrorResponse,
deserializeError,
} from '../serialization';
ErrorResponseBody,
parseErrorResponseBody,
} from '../serialization/response';
/**
* An error thrown as the result of a failed server request.
*
* The server is expected to respond on the ErrorResponse format.
* The server is expected to respond on the ErrorResponseBody format.
*
* @public
*/
@@ -39,7 +39,7 @@ export class ResponseError extends Error {
/**
* The parsed JSON error body, as sent by the server.
*/
readonly data: ErrorResponse;
readonly body: ErrorResponseBody;
/**
* The Error cause, as seen by the remote server. This is parsed out of the
@@ -60,7 +60,7 @@ export class ResponseError extends Error {
* been consumed before.
*/
static async fromResponse(response: Response): Promise<ResponseError> {
const data = await parseErrorResponse(response);
const data = await parseErrorResponseBody(response);
const status = data.response.statusCode || response.status;
const statusText = data.error.name || response.statusText;
@@ -75,16 +75,28 @@ export class ResponseError extends Error {
});
}
/**
* @deprecated will be removed.
**/
constructor(props: {
message: string;
response: Response;
data: ErrorResponse;
data: ErrorResponseBody;
cause: Error;
}) {
super(props.message);
this.name = 'ResponseError';
this.response = props.response;
this.data = props.data;
this.body = props.data;
this.cause = props.cause;
}
/**
* The parsed JSON error body, as sent by the server.
* @deprecated use body instead.
*/
get data(): ErrorResponseBody {
// eslint-disable-next-line no-console
console.warn('ErrorResponse.data is deprecated, use .body instead.');
return this.body;
}
}
+2 -2
View File
@@ -16,5 +16,5 @@
export { deserializeError, serializeError, stringifyError } from './error';
export type { SerializedError } from './error';
export { parseErrorResponse } from './response';
export type { ErrorResponse } from './response';
export { parseErrorResponse, parseErrorResponseBody } from './response';
export type { ErrorResponse, ErrorResponseBody } from './response';
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import { parseErrorResponse, ErrorResponse } from './response';
import { parseErrorResponseBody, ErrorResponseBody } from './response';
describe('parseErrorResponse', () => {
describe('parseErrorResponseBody', () => {
it('handles the happy path', async () => {
const body: ErrorResponse = {
const body: ErrorResponseBody = {
error: { name: 'Fours', message: 'Expected fives', stack: 'lines' },
request: { method: 'GET', url: '/' },
response: { statusCode: 444 },
@@ -31,13 +31,13 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'application/json' }),
};
await expect(parseErrorResponse(response as Response)).resolves.toEqual(
await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
body,
);
});
it('uses request header and text body when wrong content type, even if parsable', async () => {
const body: ErrorResponse = {
const body: ErrorResponseBody = {
error: { name: 'Threes', message: 'Expected twos' },
request: { method: 'GET', url: '/' },
response: { statusCode: 333 },
@@ -50,19 +50,21 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'not-application/not-json' }),
};
await expect(parseErrorResponse(response as Response)).resolves.toEqual({
error: {
name: 'Unknown',
message: `Request failed with status 444 Fours, ${JSON.stringify(
body,
)}`,
await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
{
error: {
name: 'Error',
message: `Request failed with status 444 Fours, ${JSON.stringify(
body,
)}`,
},
response: { statusCode: 444 },
},
response: { statusCode: 444 },
});
);
});
it('uses request header and text body when not parsable', async () => {
const body: ErrorResponse = {
const body: ErrorResponseBody = {
error: { name: 'Threes', message: 'Expected twos' },
request: { method: 'GET', url: '/' },
response: { statusCode: 333 },
@@ -75,15 +77,17 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'application/json' }),
};
await expect(parseErrorResponse(response as Response)).resolves.toEqual({
error: {
name: 'Unknown',
message: `Request failed with status 444 Fours, ${JSON.stringify(
body,
).substring(1)}`,
await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
{
error: {
name: 'Error',
message: `Request failed with status 444 Fours, ${JSON.stringify(
body,
).substring(1)}`,
},
response: { statusCode: 444 },
},
response: { statusCode: 444 },
});
);
});
it('uses request header when failing to get body', async () => {
@@ -96,12 +100,14 @@ describe('parseErrorResponse', () => {
headers: new Headers({ 'Content-Type': 'application/json' }),
};
await expect(parseErrorResponse(response as Response)).resolves.toEqual({
error: {
name: 'Unknown',
message: `Request failed with status 444 Fours`,
await expect(parseErrorResponseBody(response as Response)).resolves.toEqual(
{
error: {
name: 'Error',
message: `Request failed with status 444 Fours`,
},
response: { statusCode: 444 },
},
response: { statusCode: 444 },
});
);
});
});
+30 -3
View File
@@ -20,8 +20,16 @@ import { SerializedError } from './error';
* A standard shape of JSON data returned as the body of backend errors.
*
* @public
* @deprecated - Use {@link ErrorResponseBody} instead.
*/
export type ErrorResponse = {
export type ErrorResponse = ErrorResponseBody;
/**
* A standard shape of JSON data returned as the body of backend errors.
*
* @public
*/
export type ErrorResponseBody = {
/** Details of the error that was caught */
error: SerializedError;
@@ -51,10 +59,29 @@ export type ErrorResponse = {
*
* @public
* @param response - The response of a failed request
* @deprecated - Use {@link parseErrorResponseBody} instead.
*/
export async function parseErrorResponse(
response: Response,
): Promise<ErrorResponse> {
return parseErrorResponseBody(response);
}
/**
* Attempts to construct an ErrorResponseBody out of a failed server request.
* Assumes that the response has already been checked to be not ok. This
* function consumes the body of the response, and assumes that it hasn't
* been consumed before.
*
* The code is forgiving, and constructs a useful synthetic body as best it can
* if the response body wasn't on the expected form.
*
* @public
* @param response - The response of a failed request
*/
export async function parseErrorResponseBody(
response: Response,
): Promise<ErrorResponseBody> {
try {
const text = await response.text();
if (text) {
@@ -73,7 +100,7 @@ export async function parseErrorResponse(
return {
error: {
name: 'Unknown',
name: 'Error',
message: `Request failed with status ${response.status} ${response.statusText}, ${text}`,
},
response: {
@@ -87,7 +114,7 @@ export async function parseErrorResponse(
return {
error: {
name: 'Unknown',
name: 'Error',
message: `Request failed with status ${response.status} ${response.statusText}`,
},
response: {