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>
133 lines
3.3 KiB
TypeScript
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;
|
|
};
|