From f6f25c611ef12603e46c675ec01febd356579b70 Mon Sep 17 00:00:00 2001 From: Nick Nance Date: Sat, 18 Jun 2016 10:19:51 -0700 Subject: [PATCH] Core refactor hapi (#36) * Revert "get supertest working for TS" This reverts commit 300b32fa5a1a50b4a66058e9a8de5c0cc5d6f380. * initial hapi plugin * working hapi server * update exports for es6 support --- package.json | 1 + src/core/runQuery.ts | 49 +++++++++++------------- src/index.ts | 5 +-- src/integrations/expressApollo.test.ts | 7 ++-- src/integrations/expressApollo.ts | 12 ++++-- src/integrations/hapiApollo.ts | 52 ++++++++++++++++++++++++++ tsconfig.json | 2 +- typings.json | 8 +--- 8 files changed, 90 insertions(+), 46 deletions(-) create mode 100644 src/integrations/hapiApollo.ts diff --git a/package.json b/package.json index 94695425..c1131912 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/core/runQuery.ts b/src/core/runQuery.ts index ad68c06e..47aa476f 100644 --- a/src/core/runQuery.ts +++ b/src/core/runQuery.ts @@ -7,58 +7,51 @@ import { execute, } from 'graphql'; -import { Promise } from 'es6-promise'; - export interface GqlResponse { data?: Object; errors?: Array; } -function runQuery({ - schema, - query, - rootValue, - context, - variables, - operationName, - }: { - 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 { +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. +} + +function runQuery(options: QueryOptions): Promise { 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 ); } diff --git a/src/index.ts b/src/index.ts index e83a2f95..7ebef8bf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,2 @@ -import expressApollo from './integrations/expressApollo'; - -export { expressApollo }; +export { graphqlHTTP } from './integrations/expressApollo'; +export { HapiApollo } from './integrations/hapiApollo'; diff --git a/src/integrations/expressApollo.test.ts b/src/integrations/expressApollo.test.ts index b04bdfbe..acc7fe28 100644 --- a/src/integrations/expressApollo.test.ts +++ b/src/integrations/expressApollo.test.ts @@ -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); }); }); diff --git a/src/integrations/expressApollo.ts b/src/integrations/expressApollo.ts index 90482a3f..3092c04d 100644 --- a/src/integrations/expressApollo.ts +++ b/src/integrations/expressApollo.ts @@ -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, diff --git a/src/integrations/hapiApollo.ts b/src/integrations/hapiApollo.ts new file mode 100644 index 00000000..47c03d57 --- /dev/null +++ b/src/integrations/hapiApollo.ts @@ -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(); + } +} diff --git a/tsconfig.json b/tsconfig.json index 14595d65..877ec8b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es6", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, diff --git a/typings.json b/typings.json index 53afc67b..95fe850d 100644 --- a/typings.json +++ b/typings.json @@ -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" } }