Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow user-defined GraphQLExtensions, add apollo-engine-reporting #1105

Merged
merged 6 commits into from
Jun 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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