diff --git a/packages/apollo-server-core/CHANGELOG.md b/packages/apollo-server-core/CHANGELOG.md index a188c3d0e47..03ed98ee7bb 100644 --- a/packages/apollo-server-core/CHANGELOG.md +++ b/packages/apollo-server-core/CHANGELOG.md @@ -13,3 +13,4 @@ * `apollo-server-core`: context creation can be async and errors are formatted to include error code [PR #1024](https://github.com/apollographql/apollo-server/pull/1024) * `apollo-server-core`: add `mocks` parameter to the base constructor(applies to all variants) [PR#1017](https://github.com/apollographql/apollo-server/pull/1017) * `apollo-server-core`: Remove printing of stack traces with `debug` option and include response in logging function[PR#1018](https://github.com/apollographql/apollo-server/pull/1018) +* `apollo-server-core`: Returned relevant error codes when an error is thrown by a context function [#1709](https://github.com/apollographql/apollo-server/issues/1709) diff --git a/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts b/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts index 2f0a1565d0b..840d4bfbc2a 100644 --- a/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts +++ b/packages/apollo-server-core/src/__tests__/runHttpQuery.test.ts @@ -4,6 +4,11 @@ import MockReq = require('mock-req'); import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; import { runHttpQuery, HttpQueryError } from '../runHttpQuery'; +import { + AuthenticationError, + ForbiddenError, + ValidationError, +} from 'apollo-server-core'; const queryType = new GraphQLObjectType({ name: 'QueryType', @@ -46,4 +51,89 @@ describe('runHttpQuery', () => { }); }); }); + describe('error handling in context function', () => { + const mockQueryRequest = { + method: 'GET', + query: { + query: '{ testString }', + }, + options: { + schema, + }, + request: new MockReq(), + }; + + it('raises a 401 error if an AuthenticationError is thrown', () => { + const noQueryRequest = Object.assign({}, mockQueryRequest, { + options: { + ...mockQueryRequest.options, + context: () => { + throw new AuthenticationError('This is the error'); + }, + }, + }); + + expect.assertions(2); + return runHttpQuery([], noQueryRequest).catch((err: HttpQueryError) => { + expect(err.statusCode).toEqual(401); + expect(err.message.trim()).toEqual( + '{"errors":[{"message":"Context creation failed: This is the error","extensions":{"code":"UNAUTHENTICATED"}}]}', + ); + }); + }); + it('raises a 403 error if a ForbiddenError is thrown', () => { + const noQueryRequest = Object.assign({}, mockQueryRequest, { + options: { + ...mockQueryRequest.options, + context: () => { + throw new ForbiddenError('This is the error'); + }, + }, + }); + + expect.assertions(2); + return runHttpQuery([], noQueryRequest).catch((err: HttpQueryError) => { + expect(err.statusCode).toEqual(403); + expect(err.message.trim()).toEqual( + '{"errors":[{"message":"Context creation failed: This is the error","extensions":{"code":"FORBIDDEN"}}]}', + ); + }); + }); + it('raises a 400 error if any other GraphQL error is thrown', () => { + const noQueryRequest = Object.assign({}, mockQueryRequest, { + options: { + ...mockQueryRequest.options, + context: () => { + throw new ValidationError('This is the error'); + }, + }, + }); + + expect.assertions(2); + return runHttpQuery([], noQueryRequest).catch((err: HttpQueryError) => { + expect(err.statusCode).toEqual(400); + expect(err.message.trim()).toEqual( + '{"errors":[{"message":"Context creation failed: This is the error","extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}', + ); + }); + }); + it('raises a 500 error if any other error is thrown', () => { + const noQueryRequest = Object.assign({}, mockQueryRequest, { + options: { + ...mockQueryRequest.options, + context: () => { + throw new Error('This is the error'); + }, + }, + }); + + expect.assertions(2); + return runHttpQuery([], noQueryRequest).catch((err: HttpQueryError) => { + expect(err.statusCode).toEqual(500); + expect(err.message.trim()).toEqual( + '{"errors":[{"message":"Context creation failed: This is the error","extensions":{"code":"INTERNAL_SERVER_ERROR"}}]}', + ); + }); + }); + }); }); diff --git a/packages/apollo-server-core/src/runHttpQuery.ts b/packages/apollo-server-core/src/runHttpQuery.ts index 87da2ebd9ee..36826936e0f 100644 --- a/packages/apollo-server-core/src/runHttpQuery.ts +++ b/packages/apollo-server-core/src/runHttpQuery.ts @@ -135,6 +135,11 @@ export async function runHttpQuery( e.extensions.code && e.extensions.code !== 'INTERNAL_SERVER_ERROR' ) { + if (e.extensions.code === 'UNAUTHENTICATED') { + return throwHttpGraphQLError(401, [e], options); + } else if (e.extensions.code === 'FORBIDDEN') { + return throwHttpGraphQLError(403, [e], options); + } return throwHttpGraphQLError(400, [e], options); } else { return throwHttpGraphQLError(500, [e], options);