mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-28 03:12:19 +08:00
refactor(server): dont convert graphql bad request into internal server error (#10203)
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import type { TestFn } from 'ava';
|
||||
import ava from 'ava';
|
||||
import request from 'supertest';
|
||||
|
||||
import { buildAppModule } from '../../app.module';
|
||||
import { createTestingApp } from '../utils';
|
||||
import { createTestingApp, TestingApp } from '../utils';
|
||||
|
||||
const gql = '/graphql';
|
||||
|
||||
const test = ava as TestFn<{
|
||||
app: INestApplication;
|
||||
app: TestingApp;
|
||||
}>;
|
||||
|
||||
test.before('start app', async t => {
|
||||
@@ -83,3 +82,9 @@ test('should be able to call apis', async t => {
|
||||
// make sure the request id is set
|
||||
t.truthy(res.headers['x-request-id']);
|
||||
});
|
||||
|
||||
test('should not throw internal error when graphql call with invalid params', async t => {
|
||||
await t.throwsAsync(t.context.app.gql(`query { workspace("1") }`), {
|
||||
message: /Failed to execute gql: query { workspace\("1"\) \}, status: 400/,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,12 +162,18 @@ export class TestingApp extends ApplyType<INestApplication>() {
|
||||
if (res.status !== 200) {
|
||||
throw new Error(
|
||||
`Failed to execute gql: ${query}, status: ${res.status}, body: ${JSON.stringify(
|
||||
res.body
|
||||
res.body,
|
||||
null,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (res.body.errors?.length) {
|
||||
if (TEST_LOG_LEVEL !== 'fatal') {
|
||||
// print the error stack when log level is not fatal, for better debugging
|
||||
console.error('%o', res.body);
|
||||
}
|
||||
throw new Error(res.body.errors[0].message);
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +251,12 @@ export const USER_FRIENDLY_ERRORS = {
|
||||
type: 'bad_request',
|
||||
message: 'Bad request.',
|
||||
},
|
||||
graphql_bad_request: {
|
||||
type: 'bad_request',
|
||||
args: { code: 'string', message: 'string' },
|
||||
message: ({ code, message }) =>
|
||||
`GraphQL bad request, code: ${code}, ${message}`,
|
||||
},
|
||||
|
||||
// Input errors
|
||||
query_too_long: {
|
||||
|
||||
@@ -28,6 +28,17 @@ export class BadRequest extends UserFriendlyError {
|
||||
}
|
||||
}
|
||||
@ObjectType()
|
||||
class GraphqlBadRequestDataType {
|
||||
@Field() code!: string
|
||||
@Field() message!: string
|
||||
}
|
||||
|
||||
export class GraphqlBadRequest extends UserFriendlyError {
|
||||
constructor(args: GraphqlBadRequestDataType, message?: string | ((args: GraphqlBadRequestDataType) => string)) {
|
||||
super('bad_request', 'graphql_bad_request', message, args);
|
||||
}
|
||||
}
|
||||
@ObjectType()
|
||||
class QueryTooLongDataType {
|
||||
@Field() max!: number
|
||||
}
|
||||
@@ -787,6 +798,7 @@ export enum ErrorNames {
|
||||
TOO_MANY_REQUEST,
|
||||
NOT_FOUND,
|
||||
BAD_REQUEST,
|
||||
GRAPHQL_BAD_REQUEST,
|
||||
QUERY_TOO_LONG,
|
||||
USER_NOT_FOUND,
|
||||
USER_AVATAR_NOT_FOUND,
|
||||
@@ -891,5 +903,5 @@ registerEnumType(ErrorNames, {
|
||||
export const ErrorDataUnionType = createUnionType({
|
||||
name: 'ErrorDataUnion',
|
||||
types: () =>
|
||||
[QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const,
|
||||
[GraphqlBadRequestDataType, QueryTooLongDataType, WrongSignInCredentialsDataType, UnknownOauthProviderDataType, MissingOauthQueryParameterDataType, InvalidEmailDataType, InvalidPasswordLengthDataType, WorkspacePermissionNotFoundDataType, SpaceNotFoundDataType, MemberNotFoundInSpaceDataType, NotInSpaceDataType, AlreadyInSpaceDataType, SpaceAccessDeniedDataType, SpaceOwnerNotFoundDataType, SpaceShouldHaveOnlyOneOwnerDataType, DocNotFoundDataType, DocAccessDeniedDataType, VersionRejectedDataType, InvalidHistoryTimestampDataType, DocHistoryNotFoundDataType, BlobNotFoundDataType, ExpectToGrantDocUserRolesDataType, ExpectToRevokeDocUserRolesDataType, ExpectToUpdateDocUserRoleDataType, UnsupportedSubscriptionPlanDataType, SubscriptionAlreadyExistsDataType, SubscriptionNotExistsDataType, SameSubscriptionRecurringDataType, SubscriptionPlanNotFoundDataType, CopilotDocNotFoundDataType, CopilotMessageNotFoundDataType, CopilotPromptNotFoundDataType, CopilotProviderSideErrorDataType, CopilotInvalidContextDataType, CopilotContextFileNotSupportedDataType, CopilotFailedToModifyContextDataType, CopilotFailedToMatchContextDataType, RuntimeConfigNotFoundDataType, InvalidRuntimeConfigTypeDataType, InvalidLicenseUpdateParamsDataType, WorkspaceMembersExceedLimitToDowngradeDataType] as const,
|
||||
});
|
||||
|
||||
@@ -38,9 +38,7 @@ export class GQLLoggerPlugin implements ApolloServerPlugin {
|
||||
},
|
||||
didEncounterErrors: ctx => {
|
||||
ctx.errors.forEach(gqlErr => {
|
||||
const error = mapAnyError(
|
||||
gqlErr.originalError ? gqlErr.originalError : gqlErr
|
||||
);
|
||||
const error = mapAnyError(gqlErr);
|
||||
error.log('GraphQL');
|
||||
|
||||
metrics.gql.counter('query_error_counter').add(1, {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ApolloServerErrorCode } from '@apollo/server/errors';
|
||||
import {
|
||||
ArgumentsHost,
|
||||
Catch,
|
||||
@@ -9,10 +10,12 @@ import { GqlContextType } from '@nestjs/graphql';
|
||||
import { ThrottlerException } from '@nestjs/throttler';
|
||||
import { BaseWsExceptionFilter } from '@nestjs/websockets';
|
||||
import { Response } from 'express';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { of } from 'rxjs';
|
||||
import { Socket } from 'socket.io';
|
||||
|
||||
import {
|
||||
GraphqlBadRequest,
|
||||
InternalServerError,
|
||||
NotFound,
|
||||
TooManyRequest,
|
||||
@@ -21,7 +24,28 @@ import {
|
||||
import { metrics } from '../metrics';
|
||||
import { getRequestIdFromHost } from '../utils';
|
||||
|
||||
function isGraphQLBadRequest(error: any): error is GraphQLError {
|
||||
// https://www.apollographql.com/docs/apollo-server/data/errors
|
||||
const code = error.extensions?.code;
|
||||
return (
|
||||
code === ApolloServerErrorCode.GRAPHQL_PARSE_FAILED ||
|
||||
code === ApolloServerErrorCode.GRAPHQL_VALIDATION_FAILED ||
|
||||
code === ApolloServerErrorCode.BAD_USER_INPUT ||
|
||||
code === ApolloServerErrorCode.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
export function mapAnyError(error: any): UserFriendlyError {
|
||||
if (error instanceof GraphQLError) {
|
||||
const err = error;
|
||||
if (isGraphQLBadRequest(error)) {
|
||||
return new GraphqlBadRequest({
|
||||
code: err.extensions.code as string,
|
||||
message: err.message,
|
||||
});
|
||||
}
|
||||
error = err.originalError ?? error;
|
||||
}
|
||||
if (error instanceof UserFriendlyError) {
|
||||
return error;
|
||||
} else if (error instanceof ThrottlerException) {
|
||||
|
||||
@@ -315,7 +315,7 @@ type EditorType {
|
||||
name: String!
|
||||
}
|
||||
|
||||
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
||||
union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocAccessDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType
|
||||
|
||||
enum ErrorNames {
|
||||
ACCESS_DENIED
|
||||
@@ -364,6 +364,7 @@ enum ErrorNames {
|
||||
FAILED_TO_CHECKOUT
|
||||
FAILED_TO_SAVE_UPDATES
|
||||
FAILED_TO_UPSERT_SNAPSHOT
|
||||
GRAPHQL_BAD_REQUEST
|
||||
INTERNAL_SERVER_ERROR
|
||||
INVALID_CHECKOUT_PARAMETERS
|
||||
INVALID_EMAIL
|
||||
@@ -475,6 +476,11 @@ type GrantedDocUserTypeEdge {
|
||||
node: GrantedDocUserType!
|
||||
}
|
||||
|
||||
type GraphqlBadRequestDataType {
|
||||
code: String!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type InvalidEmailDataType {
|
||||
email: String!
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user