diff --git a/src/lib/error/api-error.test.ts b/src/lib/error/api-error.test.ts index 7e1cae4039..8077246fd8 100644 --- a/src/lib/error/api-error.test.ts +++ b/src/lib/error/api-error.test.ts @@ -1,9 +1,47 @@ import { ErrorObject } from 'ajv'; import { ApiErrorSchema, + fromLegacyError, fromOpenApiValidationError, fromOpenApiValidationErrors, + UnleashApiErrorNameWithoutExtraData, + UnleashApiErrorTypes, + UnleashError, } from './api-error'; +import BadDataError from './bad-data-error'; + +describe('v5 deprecation: backwards compatibility', () => { + it.each(UnleashApiErrorTypes)( + 'Adds details to error type: "%s"', + (name: UnleashApiErrorNameWithoutExtraData) => { + const message = `Error type: ${name}`; + const error = new UnleashError({ name, message }).toJSON(); + + expect(error.message).toBe(message); + expect(error.details).toStrictEqual([ + { + message, + description: message, + }, + ]); + }, + ); +}); + +describe('Standard/legacy error conversion', () => { + it('Moves message to the details list for baddataerror', () => { + const message = `: message!`; + const result = fromLegacyError(new BadDataError(message)).toJSON(); + + expect(result.message.includes('`details`')); + expect(result.details).toStrictEqual([ + { + message, + description: message, + }, + ]); + }); +}); describe('OpenAPI error conversion', () => { it('Gives useful error messages for missing properties', () => { diff --git a/src/lib/error/api-error.ts b/src/lib/error/api-error.ts index cdaff9c42c..70fa805118 100644 --- a/src/lib/error/api-error.ts +++ b/src/lib/error/api-error.ts @@ -2,32 +2,32 @@ import { v4 as uuidV4 } from 'uuid'; import { FromSchema } from 'json-schema-to-ts'; import { ErrorObject } from 'ajv'; -const UnleashApiErrorTypes = [ - 'OwaspValidationError', - 'PasswordUndefinedError', - 'NoAccessError', - 'UsedTokenError', - 'InvalidOperationError', - 'IncompatibleProjectError', - 'OperationDeniedError', - 'NotFoundError', - 'NameExistsError', +export const UnleashApiErrorTypes = [ + 'ContentTypeError', + 'DisabledError', 'FeatureHasTagError', - 'RoleInUseError', - 'ProjectWithoutOwnerError', - 'UnknownError', + 'IncompatibleProjectError', + 'InvalidOperationError', + 'MinimumOneEnvironmentError', + 'NameExistsError', + 'NoAccessError', + 'NotFoundError', + 'NotImplementedError', + 'OperationDeniedError', + 'OwaspValidationError', 'PasswordMismatch', 'PasswordMismatchError', - 'DisabledError', - 'ContentTypeError', - 'NotImplementedError', + 'PasswordUndefinedError', + 'ProjectWithoutOwnerError', + 'RoleInUseError', + 'UnknownError', + 'UsedTokenError', // server errors; not the end user's fault 'InternalError', ] as const; const UnleashApiErrorTypesWithExtraData = [ - 'MinimumOneEnvironmentError', 'BadDataError', 'BadRequestError', 'ValidationError', @@ -42,10 +42,8 @@ const AllUnleashApiErrorTypes = [ ] as const; type UnleashApiErrorName = typeof AllUnleashApiErrorTypes[number]; -type UnleashApiErrorNameWithoutExtraData = Exclude< - UnleashApiErrorName, - typeof UnleashApiErrorTypesWithExtraData[number] ->; +export type UnleashApiErrorNameWithoutExtraData = + typeof UnleashApiErrorTypes[number]; const statusCode = (errorName: UnleashApiErrorName): number => { switch (errorName) { @@ -174,6 +172,7 @@ export class UnleashError extends Error { id: this.id, name: this.name, message: this.message, + details: [{ message: this.message, description: this.message }], ...this.additionalParameters, }; } @@ -228,11 +227,10 @@ export const fromLegacyError = (e: Error): UnleashError => { if ( [ - 'ValidationError', - 'BadRequestError', 'BadDataError', + 'BadRequestError', 'InvalidTokenError', - 'MinimumOneEnvironmentError', + 'ValidationError', ].includes(name) ) { return new UnleashError({ @@ -240,10 +238,9 @@ export const fromLegacyError = (e: Error): UnleashError => { | 'ValidationError' | 'BadRequestError' | 'BadDataError' - | 'InvalidTokenError' - | 'MinimumOneEnvironmentError', + | 'InvalidTokenError', message: - 'Your request body failed to validate. Refer to the `details` list to see what happened.', + 'Request validation failed: your request body failed to validate. Refer to the `details` list to see what happened.', details: [{ description: e.message, message: e.message }], }); } diff --git a/src/lib/routes/util.ts b/src/lib/routes/util.ts index 078c6d89e9..3a262c7e80 100644 --- a/src/lib/routes/util.ts +++ b/src/lib/routes/util.ts @@ -26,14 +26,11 @@ export const handleErrors: ( logger: Logger, error: Error, ) => void = (res, logger, error) => { - logger.warn(error.message); - // @ts-expect-error - // eslint-disable-next-line no-param-reassign - error.isJoi = true; - const finalError = error instanceof UnleashError ? error : fromLegacyError(error); + logger.warn(finalError.id, finalError.message); + if (['InternalError', 'UnknownError'].includes(finalError.name)) { logger.error('Server failed executing request', error); }