import { v4 as uuidV4 } from 'uuid'; import { FromSchema } from 'json-schema-to-ts'; const UnleashApiErrorTypes = [ 'ContentTypeError', 'DisabledError', 'FeatureHasTagError', 'IncompatibleProjectError', 'InvalidOperationError', 'InvalidTokenError', 'MinimumOneEnvironmentError', 'NameExistsError', 'NoAccessError', 'NotFoundError', 'NotImplementedError', 'OperationDeniedError', 'PasswordMismatch', 'PasswordUndefinedError', 'ProjectWithoutOwnerError', 'RoleInUseError', 'UnknownError', 'UsedTokenError', 'BadDataError', 'ValidationError', 'AuthenticationRequired', 'UnauthorizedError', 'NoAccessError', 'InvalidTokenError', 'OwaspValidationError', // server errors; not the end user's fault 'InternalError', ] as const; type UnleashApiErrorName = typeof UnleashApiErrorTypes[number]; const statusCode = (errorName: string): number => { switch (errorName) { case 'ContentTypeError': return 415; case 'ValidationError': return 400; case 'BadDataError': return 400; case 'OwaspValidationError': return 400; case 'PasswordUndefinedError': return 400; case 'MinimumOneEnvironmentError': return 400; case 'InvalidTokenError': return 401; case 'NoAccessError': return 403; case 'UsedTokenError': return 403; case 'InvalidOperationError': return 403; case 'IncompatibleProjectError': return 403; case 'OperationDeniedError': return 403; case 'NotFoundError': return 404; case 'NameExistsError': return 409; case 'FeatureHasTagError': return 409; case 'RoleInUseError': return 400; case 'ProjectWithoutOwnerError': return 409; case 'UnknownError': return 500; case 'InternalError': return 500; case 'PasswordMismatch': return 401; case 'UnauthorizedError': return 401; case 'DisabledError': return 422; case 'NotImplementedError': return 405; case 'NoAccessError': return 403; case 'AuthenticationRequired': return 401; default: return 500; } }; export abstract class UnleashError extends Error { id: string; name: string; statusCode: number; additionalParameters: object; constructor(message: string, name?: string) { super(); this.id = uuidV4(); this.name = name || this.constructor.name; super.message = message; this.statusCode = statusCode(this.name); } help(): string { return `Get help for id ${this.id}`; } toJSON(): ApiErrorSchema { return { id: this.id, name: this.name, message: this.message, details: [{ message: this.message, description: this.message }], }; } toString(): string { return `${this.name}: ${this.message}`; } } class GenericUnleashError extends UnleashError { constructor({ name, message, }: { name: UnleashApiErrorName; message: string; }) { super(message, name); } } export const apiErrorSchema = { $id: '#/components/schemas/apiError', type: 'object', required: ['id', 'name', 'message'], description: 'An Unleash API error. Contains information about what went wrong.', properties: { name: { type: 'string', description: 'The kind of error that occurred. Meant for machine consumption.', example: 'ValidationError', }, id: { type: 'string', description: 'A unique identifier for this error instance. Can be used to search logs etc.', example: '0b84c7fd-5278-4087-832d-0b502c7929b3', }, message: { type: 'string', description: 'A human-readable explanation of what went wrong.', example: "We couldn't find an addon provider with the name that you are trying to add ('bogus-addon')", }, }, components: {}, } as const; export const fromLegacyError = (e: Error): UnleashError => { if (e instanceof UnleashError) { return e; } const name = UnleashApiErrorTypes.includes(e.name as UnleashApiErrorName) ? (e.name as UnleashApiErrorName) : 'UnknownError'; if (name === 'NoAccessError') { return new GenericUnleashError({ name: 'NoAccessError', message: e.message, }); } if (name === 'ValidationError' || name === 'BadDataError') { return new GenericUnleashError({ name: 'BadDataError', message: e.message, }); } if (name === 'OwaspValidationError') { return new GenericUnleashError({ name: 'OwaspValidationError', message: e.message, }); } if (name === 'AuthenticationRequired') { return new GenericUnleashError({ name: 'AuthenticationRequired', message: `You must be authenticated to view this content. Please log in.`, }); } return new GenericUnleashError({ name: name as UnleashApiErrorName, message: e.message, }); }; export type ApiErrorSchema = FromSchema;