Fix yoga scalar validations being captured (#12085)

Yoga graphql error were not correctly interpreted by the exception
handler. Mostly validations on the scalars such as bad enum options,
wrong format for uuid and such.
This PR adds a new convertGraphQLErrorToBaseGraphQLError utility
function in graphql-errors.util.ts that converts those errors to our
custom BaseGraphQLError by using the extension.http.code from the error
when possible so they can be handled the same way we treat the graphql
errors we throw ourselves.

Before
<img width="799" alt="Screenshot 2025-05-16 at 11 04 08"
src="https://github.com/user-attachments/assets/08b0a908-34d8-45a6-b315-8e211d1104ce"
/>

After
<img width="797" alt="Screenshot 2025-05-16 at 11 16 37"
src="https://github.com/user-attachments/assets/3fff0a70-6c3f-413a-b458-56030377fec9"
/>
This commit is contained in:
Weiko
2025-05-16 18:11:52 +02:00
committed by GitHub
parent 6823224177
commit 806bb611e8
2 changed files with 75 additions and 8 deletions

View File

@ -10,7 +10,10 @@ import { GraphQLContext } from 'src/engine/api/graphql/graphql-config/interfaces
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
import { generateGraphQLErrorFromError } from 'src/engine/core-modules/graphql/utils/generate-graphql-error-from-error.util';
import { BaseGraphQLError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import {
BaseGraphQLError,
convertGraphQLErrorToBaseGraphQLError,
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { shouldCaptureException } from 'src/engine/utils/global-exception-handler.util';
const DEFAULT_EVENT_ID_KEY = 'exceptionEventId';
@ -81,13 +84,29 @@ export const useGraphQLErrorHandlerHook = <
return;
}
// Step 1: Flatten errors - extract original errors when available
const originalErrors = result.errors.map((error) => {
return error.originalError || error;
// Step 1: Process errors - extract original errors and convert to BaseGraphQLError
const processedErrors = result.errors.map((error) => {
const originalError = error.originalError || error;
if (error.extensions && originalError !== error) {
originalError.extensions = {
...error.extensions,
...(originalError.extensions || {}),
};
}
if (
originalError instanceof GraphQLError &&
!(originalError instanceof BaseGraphQLError)
) {
return convertGraphQLErrorToBaseGraphQLError(originalError);
}
return originalError;
});
// Step 2: Send errors to monitoring service (with stack traces)
const errorsToCapture = originalErrors.filter(
const errorsToCapture = processedErrors.filter(
shouldCaptureException,
);
@ -107,15 +126,15 @@ export const useGraphQLErrorHandlerHook = <
errorsToCapture.forEach((_, i) => {
if (eventIds?.[i] && eventIdKey !== null) {
originalErrors[
originalErrors.indexOf(errorsToCapture[i])
processedErrors[
processedErrors.indexOf(errorsToCapture[i])
].eventId = eventIds[i];
}
});
}
// Step 3: Transform errors for GraphQL response (clean GraphQL errors)
const transformedErrors = originalErrors.map((error) => {
const transformedErrors = processedErrors.map((error) => {
const graphqlError =
error instanceof BaseGraphQLError
? error

View File

@ -196,3 +196,51 @@ export class InternalServerError extends BaseGraphQLError {
Object.defineProperty(this, 'name', { value: 'InternalServerError' });
}
}
/**
* Converts a GraphQLError to a BaseGraphQLError with the appropriate ErrorCode
* based on HTTP status code if present in extensions.
*/
export const convertGraphQLErrorToBaseGraphQLError = (
error: GraphQLError,
): BaseGraphQLError => {
const httpStatus = error.extensions?.http?.status;
let errorCode = ErrorCode.INTERNAL_SERVER_ERROR;
if (httpStatus && typeof httpStatus === 'number') {
switch (httpStatus) {
case 400:
errorCode = ErrorCode.BAD_USER_INPUT;
break;
case 401:
errorCode = ErrorCode.UNAUTHENTICATED;
break;
case 403:
errorCode = ErrorCode.FORBIDDEN;
break;
case 404:
errorCode = ErrorCode.NOT_FOUND;
break;
case 405:
errorCode = ErrorCode.METHOD_NOT_ALLOWED;
break;
case 408:
case 504:
errorCode = ErrorCode.TIMEOUT;
break;
case 409:
errorCode = ErrorCode.CONFLICT;
break;
default:
if (httpStatus >= 400 && httpStatus < 500) {
// Other 4xx errors
errorCode = ErrorCode.BAD_USER_INPUT;
} else {
// 5xx errors default to internal server error
errorCode = ErrorCode.INTERNAL_SERVER_ERROR;
}
}
}
return new BaseGraphQLError(error.message, errorCode, error.extensions);
};