Skip to content

Commit

Permalink
Merge pull request #426 from PhiloInc/graphiql-function
Browse files Browse the repository at this point in the history
Allow GraphiQLOptions to be a function
  • Loading branch information
helfer authored Jun 20, 2017
2 parents 1f1f0b8 + 8720ed4 commit 54d7d9e
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 133 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

### VNEXT
* Allow GraphiQLOptions to be a function ([@NeoPhi](https://github.com/NeoPhi)) on [#426](https://github.com/apollographql/graphql-server/pull/426)

### v0.8.5
* Fix: graphql-server-micro now properly returns response promises [#401](https://github.com/apollographql/graphql-server/pull/401)
Expand Down
29 changes: 12 additions & 17 deletions packages/graphql-server-express/src/expressApollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export function graphqlExpress(options: GraphQLOptions | ExpressGraphQLOptionsFu
};
}

export interface ExpressGraphiQLOptionsFunction {
(req?: express.Request): GraphiQL.GraphiQLData | Promise<GraphiQL.GraphiQLData>;
}

/* This middleware returns the html for the GraphiQL interactive query UI
*
* GraphiQLData arguments
Expand All @@ -64,22 +68,13 @@ export function graphqlExpress(options: GraphQLOptions | ExpressGraphQLOptionsFu
* - (optional) result: the result of the query to pre-fill in the GraphiQL UI
*/

export function graphiqlExpress(options: GraphiQL.GraphiQLData) {
return (req: express.Request, res: express.Response) => {
const q = req.url && url.parse(req.url, true).query || {};
const query = q.query || '';
const operationName = q.operationName || '';

const graphiQLString = GraphiQL.renderGraphiQL({
endpointURL: options.endpointURL,
subscriptionsEndpoint: options.subscriptionsEndpoint,
query: query || options.query,
variables: q.variables && JSON.parse(q.variables) || options.variables,
operationName: operationName || options.operationName,
passHeader: options.passHeader,
});
res.setHeader('Content-Type', 'text/html');
res.write(graphiQLString);
res.end();
export function graphiqlExpress(options: GraphiQL.GraphiQLData | ExpressGraphiQLOptionsFunction) {
return (req: express.Request, res: express.Response, next) => {
const query = req.url && url.parse(req.url, true).query;
GraphiQL.resolveGraphiQLString(query, options, req).then(graphiqlString => {
res.setHeader('Content-Type', 'text/html');
res.write(graphiqlString);
res.end();
}, error => next(error));
};
}
1 change: 1 addition & 0 deletions packages/graphql-server-express/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export {
ExpressGraphQLOptionsFunction,
ExpressHandler,
ExpressGraphiQLOptionsFunction,
graphqlExpress,
graphiqlExpress,
} from './expressApollo';
Expand Down
63 changes: 21 additions & 42 deletions packages/graphql-server-hapi/src/hapiApollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,64 +70,43 @@ graphqlHapi.attributes = {
version: '0.0.1',
};

export interface GraphiQLPluginOptions {
export interface HapiGraphiQLOptionsFunction {
(req?: Request): GraphiQL.GraphiQLData | Promise<GraphiQL.GraphiQLData>;
}

export interface HapiGraphiQLPluginOptions {
path: string;
route?: any;
graphiqlOptions: GraphiQL.GraphiQLData;
graphiqlOptions: GraphiQL.GraphiQLData | HapiGraphiQLOptionsFunction;
}

const graphiqlHapi: IRegister = function(server: Server, options: GraphiQLPluginOptions, next) {
server.method('getGraphiQLParams', getGraphiQLParams);
server.method('renderGraphiQL', renderGraphiQL);
const graphiqlHapi: IRegister = function(server: Server, options: HapiGraphiQLPluginOptions, next) {
if (!options || !options.graphiqlOptions) {
throw new Error('Apollo Server GraphiQL requires options.');
}

const config = Object.assign(options.route || {}, {
plugins: {
graphiql: options.graphiqlOptions,
},
pre: [{
assign: 'graphiqlParams',
method: 'getGraphiQLParams',
}, {
assign: 'graphiQLString',
method: 'renderGraphiQL(route, pre.graphiqlParams)',
}],
});
if (arguments.length !== 3) {
throw new Error(`Apollo Server GraphiQL expects exactly 3 arguments, got ${arguments.length}`);
}

server.route({
method: 'GET',
path: options.path || '/graphql',
config,
path: options.path || '/graphiql',
config: options.route || {},
handler: (request, reply) => {
reply(request.pre['graphiQLString']).header('Content-Type', 'text/html');
const query = request.query;
GraphiQL.resolveGraphiQLString(query, options.graphiqlOptions, request).then(graphiqlString => {
reply(graphiqlString).header('Content-Type', 'text/html');
}, error => reply(error));
},
});
next();

return next();
};

graphiqlHapi.attributes = {
name: 'graphiql',
version: '0.0.1',
};

function getGraphiQLParams(request, reply) {
const q = request.query || {};
const query = q.query || '';
const variables = q.variables;
const operationName = q.operationName || '';
reply({ query, variables, operationName});
}

function renderGraphiQL(route, graphiqlParams: any, reply) {
const graphiqlOptions = route.settings.plugins['graphiql'];
const graphiQLString = GraphiQL.renderGraphiQL({
endpointURL: graphiqlOptions.endpointURL,
subscriptionsEndpoint: graphiqlOptions.subscriptionsEndpoint,
query: graphiqlParams.query || graphiqlOptions.query,
variables: graphiqlParams.variables && JSON.parse(graphiqlParams.variables) || graphiqlOptions.variables,
operationName: graphiqlParams.operationName || graphiqlOptions.operationName,
passHeader: graphiqlOptions.passHeader,
});
reply(graphiQLString);
}

export { graphqlHapi, graphiqlHapi };
3 changes: 2 additions & 1 deletion packages/graphql-server-hapi/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { IRegister,
HapiOptionsFunction,
HapiPluginOptions,
GraphiQLPluginOptions,
HapiGraphiQLOptionsFunction,
HapiGraphiQLPluginOptions,
graphqlHapi, graphiqlHapi } from './hapiApollo';
30 changes: 28 additions & 2 deletions packages/graphql-server-integration-testsuite/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const schema = new GraphQLSchema({
export interface CreateAppOptions {
excludeParser?: boolean;
graphqlOptions?: GraphQLOptions | {(): GraphQLOptions | Promise<{}>};
graphiqlOptions?: GraphiQL.GraphiQLData;
graphiqlOptions?: GraphiQL.GraphiQLData | {(): GraphiQL.GraphiQLData | Promise<{}>};
}

export interface CreateAppFunc {
Expand Down Expand Up @@ -630,7 +630,7 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => {
app = createApp({graphqlOptions: {
schema,
formatError: (err) => {
throw new Error('I should be catched');
throw new Error('I should be caught');
},
}});
const req = request(app)
Expand Down Expand Up @@ -727,6 +727,32 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => {
});
});

it('allows options to be a function', () => {
app = createApp({graphiqlOptions: () => ({
endpointURL: '/graphql',
})});

const req = request(app)
.get('/graphiql')
.set('Accept', 'text/html');
return req.then((response) => {
expect(response.status).to.equal(200);
});
});

it('handles options function errors', () => {
app = createApp({graphiqlOptions: () => {
throw new Error('I should be caught');
}});

const req = request(app)
.get('/graphiql')
.set('Accept', 'text/html');
return req.then((response) => {
expect(response.status).to.equal(500);
});
});

it('presents options variables', () => {
app = createApp({graphiqlOptions: {
endpointURL: '/graphql',
Expand Down
8 changes: 7 additions & 1 deletion packages/graphql-server-koa/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export { KoaGraphQLOptionsFunction, KoaHandler, graphqlKoa, graphiqlKoa } from './koaApollo';
export {
KoaGraphQLOptionsFunction,
KoaHandler,
KoaGraphiQLOptionsFunction,
graphqlKoa,
graphiqlKoa,
} from './koaApollo';
27 changes: 12 additions & 15 deletions packages/graphql-server-koa/src/koaApollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,19 @@ export function graphqlKoa(options: GraphQLOptions | KoaGraphQLOptionsFunction):
};
}

export function graphiqlKoa(options: GraphiQL.GraphiQLData) {
return (ctx: koa.Context) => {

const q = ctx.request.query || {};
const query = q.query || '';
const operationName = q.operationName || '';
export interface KoaGraphiQLOptionsFunction {
(ctx: koa.Context): GraphiQL.GraphiQLData | Promise<GraphiQL.GraphiQLData>;
}

const graphiQLString = GraphiQL.renderGraphiQL({
endpointURL: options.endpointURL,
subscriptionsEndpoint: options.subscriptionsEndpoint,
query: query || options.query,
variables: q.variables && JSON.parse(q.variables) || options.variables,
operationName: operationName || options.operationName,
passHeader: options.passHeader,
export function graphiqlKoa(options: GraphiQL.GraphiQLData | KoaGraphiQLOptionsFunction) {
return (ctx: koa.Context) => {
const query = ctx.request.query;
GraphiQL.resolveGraphiQLString(query, options, ctx).then(graphiqlString => {
ctx.set('Content-Type', 'text/html');
ctx.body = graphiqlString;
}, error => {
ctx.status = 500;
ctx.body = error.message;
});
ctx.set('Content-Type', 'text/html');
ctx.body = graphiQLString;
};
}
2 changes: 2 additions & 0 deletions packages/graphql-server-lambda/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export {
LambdaHandler,
IHeaders,
LambdaGraphQLOptionsFunction,
LambdaGraphiQLOptionsFunction,
graphqlLambda,
graphiqlLambda,
} from './lambdaApollo';
45 changes: 22 additions & 23 deletions packages/graphql-server-lambda/src/lambdaApollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export function graphqlLambda( options: GraphQLOptions | LambdaGraphQLOptionsFun
};
}

export interface LambdaGraphiQLOptionsFunction {
(event: any, context: lambda.Context): GraphiQL.GraphiQLData | Promise<GraphiQL.GraphiQLData>;
}

/* This Lambda Function Handler returns the html for the GraphiQL interactive query UI
*
* GraphiQLData arguments
Expand All @@ -78,30 +82,25 @@ export function graphqlLambda( options: GraphQLOptions | LambdaGraphQLOptionsFun
* - (optional) result: the result of the query to pre-fill in the GraphiQL UI
*/

export function graphiqlLambda(options: GraphiQL.GraphiQLData) {
export function graphiqlLambda(options: GraphiQL.GraphiQLData | LambdaGraphiQLOptionsFunction) {
return (event, lambdaContext: lambda.Context, callback: lambda.Callback) => {
const q = event.queryStringParameters || {};
const query = q.query || '';
const variables = q.variables || '{}';
const operationName = q.operationName || '';

const graphiQLString = GraphiQL.renderGraphiQL({
endpointURL: options.endpointURL,
subscriptionsEndpoint: options.subscriptionsEndpoint,
query: query || options.query,
variables: q.variables && JSON.parse(variables) || options.variables,
operationName: operationName || options.operationName,
passHeader: options.passHeader,
});
callback(
null,
{
'statusCode': 200,
'headers': {
'Content-Type': 'text/html',
const query = event.queryStringParameters;
GraphiQL.resolveGraphiQLString(query, options, event, lambdaContext).then(graphiqlString => {
callback(
null,
{
'statusCode': 200,
'headers': {
'Content-Type': 'text/html',
},
'body': graphiqlString,
},
'body': graphiQLString,
},
);
);
}, error => {
callback(null, {
statusCode: 500,
body: error.message,
});
});
};
}
8 changes: 6 additions & 2 deletions packages/graphql-server-micro/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { MicroGraphQLOptionsFunction,
microGraphql, microGraphiql } from './microApollo';
export {
MicroGraphQLOptionsFunction,
MicroGraphiQLOptionsFunction,
microGraphql,
microGraphiql,
} from './microApollo';
28 changes: 14 additions & 14 deletions packages/graphql-server-micro/src/microApollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,21 @@ export function microGraphql(options: GraphQLOptions | MicroGraphQLOptionsFuncti
};
}

export function microGraphiql(options: GraphiQL.GraphiQLData): RequestHandler {
return (req: IncomingMessage, res: ServerResponse) => {
const q = req.url && url.parse(req.url, true).query || {};
const query = q.query || '';
const operationName = q.operationName || '';
export interface MicroGraphiQLOptionsFunction {
(req?: IncomingMessage): GraphiQL.GraphiQLData | Promise<GraphiQL.GraphiQLData>;
}

const graphiQLString = GraphiQL.renderGraphiQL({
endpointURL: options.endpointURL,
query: query || options.query,
variables: q.variables && JSON.parse(q.variables) || options.variables,
operationName: operationName || options.operationName,
passHeader: options.passHeader,
export function microGraphiql(options: GraphiQL.GraphiQLData | MicroGraphiQLOptionsFunction): RequestHandler {
return (req: IncomingMessage, res: ServerResponse) => {
const query = req.url && url.parse(req.url, true).query || {};
GraphiQL.resolveGraphiQLString(query, options, req).then(graphiqlString => {
res.setHeader('Content-Type', 'text/html');
res.write(graphiqlString);
res.end();
}, error => {
res.statusCode = 500;
res.write(error.message);
res.end();
});
res.setHeader('Content-Type', 'text/html');
res.write(graphiQLString);
res.end();
};
}
1 change: 1 addition & 0 deletions packages/graphql-server-module-graphiql/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { GraphiQLData, renderGraphiQL } from './renderGraphiQL';
export { resolveGraphiQLString } from './resolveGraphiQLString';
Loading

0 comments on commit 54d7d9e

Please sign in to comment.