fix(server): handle graphql bad input error (#10854)
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
applyDecorators,
|
applyDecorators,
|
||||||
Body,
|
Body,
|
||||||
@@ -130,7 +132,12 @@ function gql(app: INestApplication, query: string) {
|
|||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.post('/graphql')
|
.post('/graphql')
|
||||||
.send({ query })
|
.send({ query })
|
||||||
.expect(200);
|
.expect(res => {
|
||||||
|
assert(
|
||||||
|
res.status === 200 || res.status === 400,
|
||||||
|
'GraphQL query should return 200 or 400'
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test.before(async ({ context }) => {
|
test.before(async ({ context }) => {
|
||||||
@@ -199,6 +206,15 @@ test('should be able to handle validation error in graphql query', async t => {
|
|||||||
t.true(t.context.logger.error.notCalled);
|
t.true(t.context.logger.error.notCalled);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should be able to handle graphql input error', async t => {
|
||||||
|
const res = await gql(t.context.app, `mutation { validate(email: 123) }`);
|
||||||
|
const err = res.body.errors[0];
|
||||||
|
t.is(err.message, 'String cannot represent a non string value: 123');
|
||||||
|
t.is(err.extensions.status, HttpStatus.BAD_REQUEST);
|
||||||
|
t.is(err.extensions.name, 'GRAPHQL_BAD_REQUEST');
|
||||||
|
t.true(t.context.logger.error.notCalled);
|
||||||
|
});
|
||||||
|
|
||||||
test('should be able to respond request', async t => {
|
test('should be able to respond request', async t => {
|
||||||
const res = await request(t.context.app.getHttpServer())
|
const res = await request(t.context.app.getHttpServer())
|
||||||
.get('/ok')
|
.get('/ok')
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { GraphQLError } from 'graphql';
|
|||||||
|
|
||||||
import { Config } from '../config';
|
import { Config } from '../config';
|
||||||
import { UserFriendlyError } from '../error';
|
import { UserFriendlyError } from '../error';
|
||||||
|
import { isGraphQLBadRequest, mapAnyError } from '../nestjs/exception';
|
||||||
import { GQLLoggerPlugin } from './logger-plugin';
|
import { GQLLoggerPlugin } from './logger-plugin';
|
||||||
|
|
||||||
export type GraphqlContext = {
|
export type GraphqlContext = {
|
||||||
@@ -65,6 +66,15 @@ export type GraphqlContext = {
|
|||||||
formattedError.extensions = error.originalError.toJSON();
|
formattedError.extensions = error.originalError.toJSON();
|
||||||
formattedError.extensions.stacktrace = error.originalError.stack;
|
formattedError.extensions.stacktrace = error.originalError.stack;
|
||||||
return formattedError;
|
return formattedError;
|
||||||
|
} else if (
|
||||||
|
error instanceof GraphQLError &&
|
||||||
|
isGraphQLBadRequest(error)
|
||||||
|
) {
|
||||||
|
const err = mapAnyError(error);
|
||||||
|
// @ts-expect-error allow assign
|
||||||
|
formattedError.extensions = err.toJSON();
|
||||||
|
formattedError.extensions.stacktrace = err.stack;
|
||||||
|
return formattedError;
|
||||||
} else {
|
} else {
|
||||||
// @ts-expect-error allow assign
|
// @ts-expect-error allow assign
|
||||||
formattedError.message = 'Internal Server Error';
|
formattedError.message = 'Internal Server Error';
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
import { metrics } from '../metrics';
|
import { metrics } from '../metrics';
|
||||||
import { getRequestIdFromHost } from '../utils';
|
import { getRequestIdFromHost } from '../utils';
|
||||||
|
|
||||||
function isGraphQLBadRequest(error: any): error is GraphQLError {
|
export function isGraphQLBadRequest(error: GraphQLError) {
|
||||||
// https://www.apollographql.com/docs/apollo-server/data/errors
|
// https://www.apollographql.com/docs/apollo-server/data/errors
|
||||||
const code = error.extensions?.code;
|
const code = error.extensions?.code;
|
||||||
return (
|
return (
|
||||||
@@ -39,14 +39,13 @@ function isGraphQLBadRequest(error: any): error is GraphQLError {
|
|||||||
|
|
||||||
export function mapAnyError(error: any): UserFriendlyError {
|
export function mapAnyError(error: any): UserFriendlyError {
|
||||||
if (error instanceof GraphQLError) {
|
if (error instanceof GraphQLError) {
|
||||||
const err = error;
|
|
||||||
if (isGraphQLBadRequest(error)) {
|
if (isGraphQLBadRequest(error)) {
|
||||||
return new GraphqlBadRequest({
|
return new GraphqlBadRequest({
|
||||||
code: err.extensions.code as string,
|
code: error.extensions.code as string,
|
||||||
message: err.message,
|
message: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
error = err.originalError ?? error;
|
error = error.originalError ?? error;
|
||||||
}
|
}
|
||||||
if (error instanceof UserFriendlyError) {
|
if (error instanceof UserFriendlyError) {
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
Reference in New Issue
Block a user