2023-11-08 18:21:41 +01:00
|
|
|
import { toUpper, snakeCase, pick, isEmpty } from 'lodash/fp';
|
|
|
|
import { errors } from '@strapi/utils';
|
2024-01-15 14:54:58 +01:00
|
|
|
import { GraphQLError, type GraphQLFormattedError } from 'graphql';
|
2023-11-08 18:21:41 +01:00
|
|
|
|
|
|
|
const { HttpError, ForbiddenError, UnauthorizedError, ApplicationError, ValidationError } = errors;
|
2021-10-29 12:23:28 +02:00
|
|
|
|
2023-11-08 18:21:41 +01:00
|
|
|
const formatToCode = (name: string) => `STRAPI_${toUpper(snakeCase(name))}`;
|
|
|
|
const formatErrorToExtension = (error: any) => ({
|
|
|
|
error: pick(['name', 'message', 'details'])(error),
|
|
|
|
});
|
2021-10-29 12:23:28 +02:00
|
|
|
|
2024-01-15 14:54:58 +01:00
|
|
|
function createFormattedError(
|
|
|
|
formattedError: GraphQLFormattedError,
|
|
|
|
message: string,
|
|
|
|
code: string,
|
|
|
|
originalError: unknown
|
|
|
|
) {
|
|
|
|
const options = {
|
|
|
|
...formattedError,
|
|
|
|
extensions: {
|
|
|
|
...formattedError.extensions,
|
|
|
|
...formatErrorToExtension(originalError),
|
|
|
|
code,
|
|
|
|
},
|
|
|
|
};
|
2021-10-29 12:23:28 +02:00
|
|
|
|
2024-01-15 14:54:58 +01:00
|
|
|
return new GraphQLError(message, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The handler for Apollo Server v4's formatError config option
|
|
|
|
*
|
|
|
|
* Intercepts specific Strapi error types to send custom error response codes in the GraphQL response
|
|
|
|
*/
|
|
|
|
export function formatGraphqlError(formattedError: GraphQLFormattedError, originalError: unknown) {
|
|
|
|
// If this error doesn't have an associated originalError, it
|
2021-11-02 12:08:15 +01:00
|
|
|
if (isEmpty(originalError)) {
|
2024-01-15 14:54:58 +01:00
|
|
|
return formattedError;
|
2021-10-29 12:23:28 +02:00
|
|
|
}
|
|
|
|
|
2024-01-15 14:54:58 +01:00
|
|
|
const { message = '', name = 'UNKNOWN' } = originalError as Error;
|
|
|
|
|
2021-10-29 12:23:28 +02:00
|
|
|
if (originalError instanceof ForbiddenError || originalError instanceof UnauthorizedError) {
|
2024-01-15 14:54:58 +01:00
|
|
|
return createFormattedError(formattedError, message, 'FORBIDDEN', originalError);
|
2021-10-29 12:23:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (originalError instanceof ValidationError) {
|
2024-01-15 14:54:58 +01:00
|
|
|
return createFormattedError(formattedError, message, 'BAD_USER_INPUT', originalError);
|
2021-10-29 12:23:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (originalError instanceof ApplicationError || originalError instanceof HttpError) {
|
2024-01-15 14:54:58 +01:00
|
|
|
const errorName = formatToCode(name);
|
|
|
|
return createFormattedError(formattedError, message, errorName, originalError);
|
2021-10-29 12:23:28 +02:00
|
|
|
}
|
2021-10-27 18:54:58 +02:00
|
|
|
|
2024-01-15 14:54:58 +01:00
|
|
|
if (originalError instanceof GraphQLError) {
|
|
|
|
return formattedError;
|
2021-11-02 12:08:15 +01:00
|
|
|
}
|
|
|
|
|
2024-01-15 14:54:58 +01:00
|
|
|
// else if originalError doesn't appear to be from Strapi or GraphQL..
|
|
|
|
|
|
|
|
// Log the error
|
2021-11-02 12:08:15 +01:00
|
|
|
strapi.log.error(originalError);
|
2024-01-15 14:54:58 +01:00
|
|
|
|
|
|
|
// Create a generic 500 to send so we don't risk leaking any data
|
|
|
|
return createFormattedError(
|
|
|
|
new GraphQLError('Internal Server Error'),
|
|
|
|
'Internal Server Error',
|
|
|
|
'INTERNAL_SERVER_ERROR',
|
|
|
|
originalError
|
|
|
|
);
|
2023-11-08 18:21:41 +01:00
|
|
|
}
|