mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 06:31:47 +03:00
26db1d436d
standardize the error raising in both GraphQL Resolvers and Controllers. Now, All user aware errors should be throwed with `HttpException`'s variants, for example `NotFoundException`. > Directly throwing `GraphQLError` are forbidden. The GraphQL errorFormatter will handle it automatically and set `code`, `status` in error extensions. At the same time, the frontend `GraphQLError` should be imported from `@affine/graphql`, which introduce a better error extensions type. ---- controller example: ```js @Get('/docs/${id}') doc() { // ... // imported from '@nestjs/common' throw new NotFoundException('Doc is not found.'); // ... } ``` the above will response as: ``` status: 404 Not Found { "message": "Doc is not found.", "statusCode": 404, "error": "Not Found" } ``` resolver example: ```js @Mutation() invite() { // ... throw new PayloadTooLargeException('Workspace seats is full.') // ... } ``` the above will response as: ``` status: 200 Ok { "data": null, "errors": [ { "message": "Workspace seats is full.", "extensions": { "code": 404, "status": "Not Found" } } ] } ``` for frontend GraphQLError user-friend, a helper function introduced: ```js import { findGraphQLError } from '@affine/graphql' fetch(query) .catch(errOrArr => { const e = findGraphQLError(errOrArr, e => e.extensions.code === 404) if (e) { // handle } }) ```
91 lines
2.4 KiB
TypeScript
91 lines
2.4 KiB
TypeScript
import {
|
|
ForbiddenException,
|
|
HttpStatus,
|
|
INestApplication,
|
|
} from '@nestjs/common';
|
|
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
|
import { Test } from '@nestjs/testing';
|
|
import testFn, { TestFn } from 'ava';
|
|
import request from 'supertest';
|
|
|
|
import { ConfigModule } from '../src/fundamentals/config';
|
|
import { GqlModule } from '../src/fundamentals/graphql';
|
|
|
|
@Resolver(() => String)
|
|
class TestResolver {
|
|
greating = 'hello world';
|
|
|
|
@Query(() => String)
|
|
hello() {
|
|
return this.greating;
|
|
}
|
|
|
|
@Mutation(() => String)
|
|
update(@Args('greating') greating: string) {
|
|
this.greating = greating;
|
|
return this.greating;
|
|
}
|
|
|
|
@Query(() => String)
|
|
errorQuery() {
|
|
throw new ForbiddenException('forbidden query');
|
|
}
|
|
|
|
@Query(() => String)
|
|
unknownErrorQuery() {
|
|
throw new Error('unknown error');
|
|
}
|
|
}
|
|
|
|
const test = testFn as TestFn<{ app: INestApplication }>;
|
|
|
|
function gql(app: INestApplication, query: string) {
|
|
return request(app.getHttpServer())
|
|
.post('/graphql')
|
|
.send({ query })
|
|
.expect(200);
|
|
}
|
|
|
|
test.beforeEach(async ctx => {
|
|
const module = await Test.createTestingModule({
|
|
imports: [ConfigModule.forRoot(), GqlModule],
|
|
providers: [TestResolver],
|
|
}).compile();
|
|
|
|
ctx.context.app = await module
|
|
.createNestApplication({
|
|
logger: false,
|
|
})
|
|
.init();
|
|
});
|
|
|
|
test('should be able to execute query', async t => {
|
|
const res = await gql(t.context.app, `query { hello }`);
|
|
t.is(res.body.data.hello, 'hello world');
|
|
});
|
|
|
|
test('should be able to execute mutation', async t => {
|
|
const res = await gql(t.context.app, `mutation { update(greating: "hi") }`);
|
|
|
|
t.is(res.body.data.update, 'hi');
|
|
|
|
const newRes = await gql(t.context.app, `query { hello }`);
|
|
t.is(newRes.body.data.hello, 'hi');
|
|
});
|
|
|
|
test('should be able to handle known http exception', async t => {
|
|
const res = await gql(t.context.app, `query { errorQuery }`);
|
|
const err = res.body.errors[0];
|
|
t.is(err.message, 'forbidden query');
|
|
t.is(err.extensions.code, HttpStatus.FORBIDDEN);
|
|
t.is(err.extensions.status, HttpStatus[HttpStatus.FORBIDDEN]);
|
|
});
|
|
|
|
test('should be able to handle unknown internal error', async t => {
|
|
const res = await gql(t.context.app, `query { unknownErrorQuery }`);
|
|
const err = res.body.errors[0];
|
|
t.is(err.message, 'Internal Server Error');
|
|
t.is(err.extensions.code, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
t.is(err.extensions.status, HttpStatus[HttpStatus.INTERNAL_SERVER_ERROR]);
|
|
});
|