Core refactor hapi (#36)

* Revert "get supertest working for TS"

This reverts commit 300b32fa5a.

* initial hapi plugin

* working hapi server

* update exports for es6 support
This commit is contained in:
Nick Nance 2016-06-18 10:19:51 -07:00 committed by Jonas Helfer
parent 300b32fa5a
commit f6f25c611e
8 changed files with 90 additions and 46 deletions

View file

@ -41,6 +41,7 @@
"es6-promise": "^3.2.1",
"express": "^4.13.4",
"graphql": "^0.6.0",
"hapi": "^13.4.1",
"source-map-support": "^0.4.0"
},
"devDependencies": {

View file

@ -7,58 +7,51 @@ import {
execute,
} from 'graphql';
import { Promise } from 'es6-promise';
export interface GqlResponse {
data?: Object;
errors?: Array<string>;
}
function runQuery({
schema,
query,
rootValue,
context,
variables,
operationName,
}: {
schema: GraphQLSchema,
query: string | Document,
rootValue?: any,
context?: any,
variables?: { [key: string]: any },
operationName?: string,
export interface QueryOptions {
schema: GraphQLSchema;
query: string | Document;
rootValue?: any;
context?: any;
variables?: { [key: string]: any };
operationName?: string;
//logFunction?: function => void
//validationRules?: No, too risky. If you want extra validation rules, then parse it yourself.
}): Promise<GraphQLResult> {
}
function runQuery(options: QueryOptions): Promise<GraphQLResult> {
let documentAST: Document;
// if query is already an AST, don't parse or validate
if (typeof query === 'string') {
if (typeof options.query === 'string') {
// parse
try {
documentAST = parse(query);
documentAST = parse(options.query as string);
} catch (syntaxError) {
return Promise.resolve({ errors: [syntaxError] });
}
// validate
const validationErrors = validate(schema, documentAST);
const validationErrors = validate(options.schema, documentAST);
if (validationErrors.length) {
return Promise.resolve({ errors: validationErrors });
}
} else {
documentAST = query;
documentAST = options.query as Document;
}
// execute
return execute(
schema,
options.schema,
documentAST,
rootValue,
context,
variables,
operationName
options.rootValue,
options.context,
options.variables,
options.operationName
);
}

View file

@ -1,3 +1,2 @@
import expressApollo from './integrations/expressApollo';
export { expressApollo };
export { graphqlHTTP } from './integrations/expressApollo';
export { HapiApollo } from './integrations/hapiApollo';

View file

@ -11,7 +11,8 @@ import {
// TODO use import, not require... help appreciated.
import * as express from 'express';
import request from 'supertest-as-promised';
// tslint:disable-next-line
const request = require('supertest-as-promised');
import { graphqlHTTP, ExpressApolloOptions, renderGraphiQL } from './expressApollo';
@ -52,9 +53,7 @@ describe('expressApollo', () => {
};
return request(app).get(
'/graphql?query={ testString }'
)
.expect(200)
.then((res) => {
).then((res) => {
return expect(res.body.data).to.deep.equal(expected);
});
});

View file

@ -2,7 +2,7 @@ import * as express from 'express';
import * as graphql from 'graphql';
import { runQuery } from '../core/runQuery';
import { renderGraphiQL, GraphiQLData } from '../modules/renderGraphiQL';
import * as GraphiQL from '../modules/renderGraphiQL';
// TODO: will these be the same or different for other integrations?
export interface ExpressApolloOptions {
@ -15,7 +15,11 @@ export interface ExpressApolloOptions {
// answer: yes, it does. Func(req) => options
}
export function graphqlHTTP(options: ExpressApolloOptions) {
export interface ExpressHandler {
(req: express.Request, res: express.Response, next): void;
}
export function graphqlHTTP(options: ExpressApolloOptions): ExpressHandler {
if (!options) {
throw new Error('Apollo graphqlHTTP middleware requires options.');
}
@ -46,9 +50,9 @@ function getQueryString(req: express.Request): string {
// this returns the html for the GraphiQL interactive query UI
// TODO: it's still missing a way to tell it where the GraphQL endpoint is.
export function renderGraphiQL(options: GraphiQLData) {
export function renderGraphiQL(options: GraphiQL.GraphiQLData) {
return (req: express.Request, res: express.Response, next) => {
const graphiQLString = renderGraphiQL({
const graphiQLString = GraphiQL.renderGraphiQL({
query: options.query,
variables: options.variables,
operationName: options.operationName,

View file

@ -0,0 +1,52 @@
import * as hapi from 'hapi';
import * as graphql from 'graphql';
import { runQuery } from '../core/runQuery';
export interface IRegister {
(server: hapi.Server, options: any, next: any): void;
attributes?: any;
}
export interface HapiApolloOptions {
schema: graphql.GraphQLSchema;
formatError?: Function;
rootValue?: any;
context?: any;
logFunction?: Function;
}
export class HapiApollo {
constructor() {
this.register.attributes = {
name: 'graphql',
version: '0.0.1',
};
}
public register: IRegister = (server: hapi.Server, options: HapiApolloOptions, next) => {
server.route({
method: 'GET',
path: '/test',
handler: (request, reply) => {
reply('test passed');
},
});
server.route({
method: 'POST',
path: '/',
handler: (request, reply) => {
return runQuery({
schema: options.schema,
query: request.payload,
}).then(gqlResponse => {
reply({ data: gqlResponse.data });
}).catch(errors => {
reply({ errors: errors }).code(500);
});
},
});
next();
}
}

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,

View file

@ -4,18 +4,14 @@
"graphql": "github:nitintutlani/typed-graphql"
},
"globalDependencies": {
"bluebird": "registry:dt/bluebird#2.0.0+20160319051630",
"body-parser": "registry:dt/body-parser#0.0.0+20160317120654",
"es6-promise": "registry:dt/es6-promise#0.0.0+20160317120654",
"express": "registry:dt/express#4.0.0+20160317120654",
"express-serve-static-core": "registry:dt/express-serve-static-core#0.0.0+20160322035842",
"hapi": "registry:dt/hapi#13.0.0+20160521152637",
"hapi": "registry:dt/hapi#13.0.0+20160602125023",
"koa": "registry:dt/koa#2.0.0+20160317120654",
"mime": "registry:dt/mime#0.0.0+20160316155526",
"mocha": "registry:dt/mocha#2.2.5+20160317120654",
"node": "registry:dt/node#6.0.0+20160524002506",
"serve-static": "registry:dt/serve-static#0.0.0+20160501131543",
"superagent": "registry:dt/superagent#1.4.0+20160317120654",
"supertest-as-promised": "registry:dt/supertest-as-promised#2.0.2+20160317120654"
"serve-static": "registry:dt/serve-static#0.0.0+20160501131543"
}
}