Skip to content

Commit

Permalink
Allow plugins to modify the HTTP status code under ERROR conditions. (#…
Browse files Browse the repository at this point in the history
…2714)

Allow plugins to modify the HTTP status code under ERROR conditions.
  • Loading branch information
abernix authored May 23, 2019
2 parents bae1f5c + 736ba41 commit 163bba7
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 3 deletions.
4 changes: 3 additions & 1 deletion packages/apollo-server-core/src/requestPipelineAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
} from 'graphql';
import { KeyValueCache } from 'apollo-server-caching';

type Mutable<T> = { -readonly [P in keyof T]: T[P] };

export interface GraphQLServiceContext {
schema: GraphQLSchema;
schemaHash: string;
Expand All @@ -45,7 +47,7 @@ export interface GraphQLResponse {
data?: Record<string, any>;
errors?: ReadonlyArray<GraphQLFormattedError>;
extensions?: Record<string, any>;
http?: Pick<Response, 'headers'>;
http?: Pick<Response, 'headers'> & Partial<Pick<Mutable<Response>, 'status'>>;
}

export interface GraphQLRequestMetrics {
Expand Down
5 changes: 4 additions & 1 deletion packages/apollo-server-core/src/runHttpQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,10 @@ export async function processHTTPRequest<TContext>(
// doesn't reach GraphQL execution
if (response.errors && typeof response.data === 'undefined') {
// don't include options, since the errors have already been formatted
return throwHttpGraphQLError(400, response.errors as any);
return throwHttpGraphQLError(
(response.http && response.http.status) || 400,
response.errors as any,
);
}

if (response.http) {
Expand Down
64 changes: 63 additions & 1 deletion packages/apollo-server-integration-testsuite/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ import {
VERSION,
} from 'apollo-link-persisted-queries';

import { createApolloFetch, ApolloFetch, GraphQLRequest } from 'apollo-fetch';
import {
createApolloFetch,
ApolloFetch,
GraphQLRequest,
ParsedResponse,
} from 'apollo-fetch';
import {
AuthenticationError,
UserInputError,
gql,
Config,
ApolloServerBase,
PluginDefinition,
} from 'apollo-server-core';
import { Headers } from 'apollo-server-env';
import { GraphQLExtension, GraphQLResponse } from 'graphql-extensions';
Expand Down Expand Up @@ -394,6 +400,62 @@ export function testApolloServer<AS extends ApolloServerBase>(
});
});

describe('Plugins', () => {
let apolloFetch: ApolloFetch;
let apolloFetchResponse: ParsedResponse;

const setupApolloServerAndFetchPairForPlugins = async (
plugins: PluginDefinition[] = [],
) => {
const { url: uri } = await createApolloServer({
typeDefs: gql`
type Query {
justAField: String
}
`,
plugins,
});

apolloFetch = createApolloFetch({ uri })
// Store the response so we can inspect it.
.useAfter(({ response }, next) => {
apolloFetchResponse = response;
next();
});
};

it('returns correct status code for a normal operation', async () => {
await setupApolloServerAndFetchPairForPlugins();

const result = await apolloFetch({ query: '{ justAField }' });
expect(result.errors).toBeUndefined();
expect(apolloFetchResponse.status).toEqual(200);
});

it('allows setting a custom status code for an error', async () => {
await setupApolloServerAndFetchPairForPlugins([
{
requestDidStart() {
return {
didResolveOperation() {
throw new Error('known_error');
},
willSendResponse({ response: { http, errors } }) {
if (errors[0].message === 'known_error') {
http.status = 403;
}
},
};
},
},
]);

const result = await apolloFetch({ query: '{ justAField }' });
expect(result.errors).toBeDefined();
expect(apolloFetchResponse.status).toEqual(403);
});
});

describe('formatError', () => {
it('wraps thrown error from validation rules', async () => {
const throwError = jest.fn(() => {
Expand Down

0 comments on commit 163bba7

Please sign in to comment.