Skip to content

Commit

Permalink
Merge pull request #1105 from apollographql/glasser/extensions-0.1.0
Browse files Browse the repository at this point in the history
Allow user-defined GraphQLExtensions, add apollo-engine-reporting
  • Loading branch information
glasser authored Jun 1, 2018
2 parents 38c8713 + 4405405 commit 1a305eb
Show file tree
Hide file tree
Showing 24 changed files with 319 additions and 169 deletions.
9 changes: 6 additions & 3 deletions docs/source/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,16 @@ const GraphQLOptions = {
validationRules?: Array<ValidationRule>,

// a function applied to each graphQL execution result
formatResponse?: Function
formatResponse?: Function,

// a custom default field resolver
fieldResolver?: Function
fieldResolver?: Function,

// a boolean that will print additional debug logging if execution errors occur
debug?: boolean
debug?: boolean,

// (optional) extra GraphQL extensions from graphql-extensions
extensions?: Array<() => GraphQLExtension>
}
```

Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-adonis/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"homepage": "https://github.com/apollographql/apollo-server#readme",
"dependencies": {
"apollo-server-core": "2.0.0-beta.1",
"apollo-server-core": "2.0.0-beta.2",
"apollo-server-module-graphiql": "^1.3.4"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-azure-functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"homepage": "https://github.com/apollographql/apollo-server#readme",
"dependencies": {
"apollo-server-core": "2.0.0-beta.1",
"apollo-server-core": "2.0.0-beta.2",
"apollo-server-module-graphiql": "^1.3.4"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"homepage": "https://github.com/apollographql/apollo-server#readme",
"dependencies": {
"apollo-server-core": "2.0.0-beta.1",
"apollo-server-core": "2.0.0-beta.2",
"apollo-server-module-graphiql": "^1.3.4"
},
"typings": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-cloudflare/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface FetchEvent extends Event {

export class ApolloServer extends ApolloServerBase {
public async listen() {
const graphql = this.request.bind(this);
const graphql = this.graphQLServerOptionsForRequest.bind(this);
addEventListener('fetch', (event: FetchEvent) => {
event.respondWith(graphqlCloudflare(graphql)(event.request));
});
Expand Down
8 changes: 5 additions & 3 deletions packages/apollo-server-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "apollo-server-core",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"description": "Core engine for Apollo GraphQL server",
"main": "dist/index.js",
"scripts": {
Expand Down Expand Up @@ -47,10 +47,12 @@
},
"dependencies": {
"apollo-cache-control": "^0.1.1",
"apollo-tracing": "^0.1.0",
"graphql-extensions": "^0.0.x",
"apollo-engine-reporting": "0.0.0-beta.8",
"apollo-tracing": "^0.2.0-beta.0",
"graphql-extensions": "0.1.0-beta.7",
"graphql-subscriptions": "^0.5.8",
"graphql-tools": "^3.0.2",
"node-fetch": "^2.1.2",
"subscriptions-transport-ws": "^0.9.9",
"ws": "^5.1.1"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-server-core/src/ApolloServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function createHttpServer(server) {

runHttpQuery([req, res], {
method: req.method,
options: server.request(req as any),
options: server.graphQLServerOptionsForRequest(req as any),
query: JSON.parse(body),
request: new MockReq(),
})
Expand Down
37 changes: 32 additions & 5 deletions packages/apollo-server-core/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import {
ValidationContext,
FieldDefinitionNode,
} from 'graphql';
import { GraphQLExtension } from 'graphql-extensions';
import { TracingExtension } from 'apollo-tracing';
import { CacheControlExtension } from 'apollo-cache-control';
import { EngineReportingAgent } from 'apollo-engine-reporting';

import { ApolloEngine } from 'apollo-engine';
import {
Expand Down Expand Up @@ -59,8 +63,9 @@ export class ApolloServerBase<Request = RequestInit> {
private schema: GraphQLSchema;
private context?: Context | ContextFunction;
private graphqlPath: string = '/graphql';
private engineReportingAgent?: EngineReportingAgent;
private engineProxy: ApolloEngine;
private engineEnabled: boolean = false;
private extensions: Array<() => GraphQLExtension>;

private http?: HttpServer;
private subscriptionServer?: SubscriptionServer;
Expand All @@ -75,6 +80,8 @@ export class ApolloServerBase<Request = RequestInit> {
typeDefs,
introspection,
mocks,
extensions,
engine,
...requestOptions
} = config;

Expand Down Expand Up @@ -122,6 +129,22 @@ export class ApolloServerBase<Request = RequestInit> {
mocks: typeof mocks === 'boolean' ? {} : mocks,
});
}

// Note: if we're using engineproxy (directly or indirectly), we will extend
// this when we listen.
this.extensions = [];

if (engine || (engine !== false && process.env.ENGINE_API_KEY)) {
this.engineReportingAgent = new EngineReportingAgent(
engine === true ? {} : engine,
);
// Let's keep this extension first so it wraps everything.
this.extensions.push(() => this.engineReportingAgent.newExtension());
}

if (extensions) {
this.extensions = [...this.extensions, ...extensions];
}
}

public use({ getHttp, path }: RegistrationOptions) {
Expand Down Expand Up @@ -324,10 +347,15 @@ export class ApolloServerBase<Request = RequestInit> {
}

// XXX should this allow for header overrides from graphql-playground?
if (this.engineProxy || engineInRequestPath) this.engineEnabled = true;
if (this.engineProxy || engineInRequestPath) {
this.extensions.push(() => new TracingExtension());
// XXX provide a way to pass options to CacheControlExtension (eg
// defaultMaxAge)
this.extensions.push(() => new CacheControlExtension());
}
}

request(request: Request) {
graphQLServerOptionsForRequest(request: Request) {
let context: Context = this.context ? this.context : { request };

try {
Expand All @@ -344,8 +372,7 @@ export class ApolloServerBase<Request = RequestInit> {

return {
schema: this.schema,
tracing: this.engineEnabled,
cacheControl: this.engineEnabled,
extensions: this.extensions,
context,
// allow overrides from options
...this.requestOptions,
Expand Down
2 changes: 2 additions & 0 deletions packages/apollo-server-core/src/graphqlOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { GraphQLExtension } from 'graphql-extensions';
* - (optional) formatResponse: a function applied to each graphQL execution result
* - (optional) fieldResolver: a custom default field resolver
* - (optional) debug: a boolean that will print additional debug logging if execution errors occur
* - (optional) extensions: an array of functions which create GraphQLExtensions (each GraphQLExtension object is used for one request)
*
*/
export interface GraphQLServerOptions<
Expand All @@ -39,6 +40,7 @@ export interface GraphQLServerOptions<
tracing?: boolean;
// cacheControl?: boolean | CacheControlExtensionOptions;
cacheControl?: boolean | any;
extensions?: Array<() => GraphQLExtension>;
}

export default GraphQLServerOptions;
Expand Down
1 change: 1 addition & 0 deletions packages/apollo-server-core/src/nodeHttpToRequest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IncomingMessage } from 'http';
import { Request, Headers } from 'node-fetch';

export function convertNodeHttpToRequest(req: IncomingMessage): Request {
const headers = new Headers();
Expand Down
3 changes: 2 additions & 1 deletion packages/apollo-server-core/src/runHttpQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface HttpQueryRequest {
options:
| GraphQLOptions
| ((...args: Array<any>) => Promise<GraphQLOptions> | GraphQLOptions);
request: Request;
request: Pick<Request, 'url' | 'method' | 'headers'>;
}

export class HttpQueryError extends Error {
Expand Down Expand Up @@ -255,6 +255,7 @@ export async function runHttpQuery(
tracing: optionsObject.tracing,
cacheControl: optionsObject.cacheControl,
request: request.request,
extensions: optionsObject.extensions,
};

if (optionsObject.formatParams) {
Expand Down
54 changes: 54 additions & 0 deletions packages/apollo-server-core/src/runQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { LogAction, LogStep } from './logging';
// environment.
import { makeCompatible } from 'meteor-promise';
import Fiber = require('fibers');
import { GraphQLExtensionStack, GraphQLExtension } from 'graphql-extensions';
makeCompatible(Promise, Fiber);

const queryType = new GraphQLObjectType({
Expand Down Expand Up @@ -366,6 +367,59 @@ describe('runQuery', () => {
});
});

describe('graphql extensions', () => {
class CustomExtension implements GraphQLExtension<any> {
format(): [string, any] {
return ['customExtension', { foo: 'bar' }];
}
}

it('creates the extension stack', async () => {
const queryString = `{ testString }`;
const expected = { testString: 'it works' };
const extensions = [() => new CustomExtension()];
return runQuery({
schema: new GraphQLSchema({
query: new GraphQLObjectType({
name: 'QueryType',
fields: {
testString: {
type: GraphQLString,
resolve(root, args, context) {
expect(context._extensionStack).to.be.instanceof(
GraphQLExtensionStack,
);
expect(
context._extensionStack.extensions[0],
).to.be.instanceof(CustomExtension);
},
},
},
}),
}),
queryString,
extensions,
request: new MockReq(),
});
});

it('runs format response from extensions', async () => {
const queryString = `{ testString }`;
const expected = { testString: 'it works' };
const extensions = [() => new CustomExtension()];
return runQuery({
schema,
queryString,
extensions,
request: new MockReq(),
}).then(res => {
return expect(res.extensions).to.deep.equal({
customExtension: { foo: 'bar' },
});
});
});
});

describe('async_hooks', () => {
let asyncHooks;
let asyncHook;
Expand Down
Loading

0 comments on commit 1a305eb

Please sign in to comment.