Files
twenty/packages/twenty-server/src/engine/utils/global-exception-handler.util.ts
Weiko 1dff5bf957 Fix custom errors thrown as 500 (#6238)
We call convertExceptionToGraphQLError in the exception handler for http
exceptions but we don't take into account those that already are
graphqlErrors and because of that the logic of convertExceptionToGraphql
is to fallback to a 500.
Now if the exception is a BaseGraphqlError (custom graphql error we
throw in the code), we throw them directly.

BEFORE
<img width="957" alt="Screenshot 2024-07-12 at 15 33 03"
src="https://github.com/user-attachments/assets/22ddae13-4996-4ad3-8f86-dd17c2922ca8">


AFTER
<img width="923" alt="Screenshot 2024-07-12 at 15 32 01"
src="https://github.com/user-attachments/assets/d3d6db93-6d28-495c-a4b4-ba4e47d45abd">

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2024-07-12 17:56:21 +02:00

133 lines
3.3 KiB
TypeScript

import { HttpException } from '@nestjs/common';
import { GraphQLError } from 'graphql';
import { ExceptionHandlerUser } from 'src/engine/integrations/exception-handler/interfaces/exception-handler-user.interface';
import {
AuthenticationError,
BaseGraphQLError,
ConflictError,
ErrorCode,
ForbiddenError,
MethodNotAllowedError,
NotFoundError,
TimeoutError,
ValidationError,
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service';
const graphQLPredefinedExceptions = {
400: ValidationError,
401: AuthenticationError,
403: ForbiddenError,
404: NotFoundError,
405: MethodNotAllowedError,
408: TimeoutError,
409: ConflictError,
};
export const graphQLErrorCodesToFilter = [
ErrorCode.GRAPHQL_VALIDATION_FAILED,
ErrorCode.UNAUTHENTICATED,
ErrorCode.FORBIDDEN,
ErrorCode.NOT_FOUND,
ErrorCode.METHOD_NOT_ALLOWED,
ErrorCode.TIMEOUT,
ErrorCode.CONFLICT,
ErrorCode.BAD_USER_INPUT,
];
export const handleExceptionAndConvertToGraphQLError = (
exception: Error,
exceptionHandlerService: ExceptionHandlerService,
user?: ExceptionHandlerUser,
): BaseGraphQLError => {
handleException(exception, exceptionHandlerService, user);
return convertExceptionToGraphQLError(exception);
};
export const shouldFilterException = (exception: Error): boolean => {
if (
exception instanceof GraphQLError &&
(exception?.extensions?.http?.status ?? 500) < 500
) {
return true;
}
if (
exception instanceof BaseGraphQLError &&
graphQLErrorCodesToFilter.includes(exception?.extensions?.code)
) {
return true;
}
if (exception instanceof HttpException && exception.getStatus() < 500) {
return true;
}
return false;
};
const handleException = (
exception: Error,
exceptionHandlerService: ExceptionHandlerService,
user?: ExceptionHandlerUser,
): void => {
if (shouldFilterException(exception)) {
return;
}
exceptionHandlerService.captureExceptions([exception], { user });
};
export const convertExceptionToGraphQLError = (
exception: Error,
): BaseGraphQLError => {
if (exception instanceof HttpException) {
return convertHttpExceptionToGraphql(exception);
}
if (exception instanceof BaseGraphQLError) {
return exception;
}
return convertExceptionToGraphql(exception);
};
const convertHttpExceptionToGraphql = (exception: HttpException) => {
const status = exception.getStatus();
let error: BaseGraphQLError;
if (status in graphQLPredefinedExceptions) {
const message = exception.getResponse()['message'] ?? exception.message;
error = new graphQLPredefinedExceptions[exception.getStatus()](message);
} else {
error = new BaseGraphQLError(
'Internal Server Error',
exception.getStatus().toString(),
);
}
// Only show the stack trace in development mode
if (process.env.NODE_ENV === 'development') {
error.stack = exception.stack;
error.extensions['response'] = exception.getResponse();
}
return error;
};
export const convertExceptionToGraphql = (exception: Error) => {
const error = new BaseGraphQLError(
exception.name,
ErrorCode.INTERNAL_SERVER_ERROR,
);
error.stack = exception.stack;
error.extensions['response'] = exception.message;
return error;
};