Merge pull request #7714 from backstage/freben/stringify-error

Add `stringifyError`
This commit is contained in:
Fredrik Adelöw
2021-10-21 19:08:50 +02:00
committed by GitHub
7 changed files with 63 additions and 21 deletions
+4 -1
View File
@@ -18,7 +18,7 @@ export class ConflictError extends CustomErrorBase {}
export class CustomErrorBase extends Error {
constructor(message?: string, cause?: Error | unknown);
// (undocumented)
readonly cause?: Error;
readonly cause?: Error | undefined;
}
// @public
@@ -98,4 +98,7 @@ export function serializeError(
includeStack?: boolean;
},
): SerializedError;
// @public
export function stringifyError(error: unknown): string;
```
+4 -16
View File
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { stringifyError } from '../serialization';
import { isError } from './assertion';
/**
@@ -32,25 +33,12 @@ import { isError } from './assertion';
* ```
*/
export class CustomErrorBase extends Error {
readonly cause?: Error;
readonly cause?: Error | undefined;
constructor(message?: string, cause?: Error | unknown) {
let assignedCause: Error | undefined = undefined;
let fullMessage = message;
if (cause !== undefined) {
let causeStr;
if (isError(cause)) {
assignedCause = cause;
causeStr = String(cause);
// Prefer the cause.toString, but if it's not implemented we use a nicer fallback
if (causeStr === '[object Object]') {
causeStr = `${cause.name}: ${cause.message}`;
}
} else {
causeStr = `unknown error '${cause}'`;
}
const causeStr = stringifyError(cause);
if (fullMessage) {
fullMessage += `; caused by ${causeStr}`;
} else {
@@ -63,6 +51,6 @@ export class CustomErrorBase extends Error {
Error.captureStackTrace?.(this, this.constructor);
this.name = this.constructor.name;
this.cause = assignedCause;
this.cause = isError(cause) ? cause : undefined;
}
}
@@ -14,7 +14,8 @@
* limitations under the License.
*/
import { deserializeError, serializeError } from './error';
import { NotModifiedError } from '../errors';
import { deserializeError, serializeError, stringifyError } from './error';
class CustomError extends Error {
readonly customField: any;
@@ -61,4 +62,30 @@ describe('serialization', () => {
expect(withoutStack1.stack).not.toBeDefined();
expect(withoutStack2.stack).not.toBeDefined();
});
it('stringifies all supported forms', () => {
expect(stringifyError({})).toEqual("unknown error '[object Object]'");
expect(
stringifyError({
toString() {
return 'str';
},
}),
).toEqual("unknown error 'str'");
expect(
stringifyError({
name: 'not used',
message: 'not used',
toString() {
return 'str';
},
}),
).toEqual('str');
expect(stringifyError({ name: 'N', message: 'm1' })).toEqual('N: m1');
expect(stringifyError(new NotModifiedError('m2'))).toEqual(
'NotModifiedError: m2',
);
expect(stringifyError(new Error('m3'))).toEqual('Error: m3');
expect(stringifyError(new TypeError('m4'))).toEqual('TypeError: m4');
});
});
@@ -19,6 +19,7 @@ import {
deserializeError as deserializeErrorInternal,
serializeError as serializeErrorInternal,
} from 'serialize-error';
import { isError } from '../errors';
/**
* The serialized form of an Error.
@@ -78,3 +79,19 @@ export function deserializeError<T extends Error = Error>(
}
return result;
}
/**
* Stringifies an error, including its name and message where available.
*
* @param error - The error.
* @public
*/
export function stringifyError(error: unknown): string {
if (isError(error)) {
// Prefer error.toString, but if it's not implemented we use a nicer fallback
const str = String(error);
return str !== '[object Object]' ? str : `${error.name}: ${error.message}`;
}
return `unknown error '${error}'`;
}
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
export { deserializeError, serializeError } from './error';
export { deserializeError, serializeError, stringifyError } from './error';
export type { SerializedError } from './error';
export { parseErrorResponse } from './response';
export type { ErrorResponse } from './response';