diff --git a/CHANGELOG.md b/CHANGELOG.md index b07cb80e..66d49381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,17 @@ All of the packages in the `apollo-server` repo are released with the same versi ### vNEXT -* Upgrade `subscription-transport-ws` to 0.9.9 for Graphiql * Remove tests and guaranteed support for Node 4 [PR #1024](https://github.com/apollographql/apollo-server/pull/1024) * Cleanup docs [PR #1233](https://github.com/apollographql/apollo-server/pull/1233/files) +### 1.4.0 + +* [Issue #626] Integrate apollo-fastify plugin. [PR #1013](https://github.com/apollographql/apollo-server/pull/1013) +* add hapi 16 next() invocation [PR #743](https://github.com/apollographql/apollo-server/pull/743) +* Add skipValidation option [PR #839](https://github.com/apollographql/apollo-server/pull/839) +* `apollo-server-module-graphiql`: adds an option to the constructor to disable url rewriting when editing a query [PR #1047](https://github.com/apollographql/apollo-server/pull/1047) +* Upgrade `subscription-transport-ws` to 0.9.9 for Graphiql + ### v1.3.6 * Recognize requests with Apollo Persisted Queries and return `PersistedQueryNotSupported` to the client instead of a confusing error. [PR #982](https://github.com/apollographql/apollo-server/pull/982) diff --git a/docs/README.md b/docs/README.md index 6d92e4fe..462832dc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,29 @@ -# Apollo Server docs +# Documentation -Read the docs at [apollographql.com/docs/apollo-server](https://www.apollographql.com/docs/apollo-server/). +This is the documentation **source** for this repository. -To run the docs app locally: +The **deployed** version of the documentation for this repository is available at: -```bash -npm install -npm start -``` +* https://www.apollographql.com/docs/apollo-server/ + +## Documentation for the documentation + +This `README.md` is intentionally short since the [documentation for the documentation](https://docs-docs.netlify.com/docs/docs/) provides details for the documentation framework _itself_. Additional information should generally be added to that documentation rather than here in this `README.md`, in order to provide a centralized resource that benefits all documentation deployments. + +## Running locally + +For more information, consult the documentation for the documentation, referenced above. + +In general though: + +* `npm install` in this directory +* `npm start` in this directory +* Open a browser to the link provided in the console. + +> **Important note:** Changes to the markdown source does not result in an automatic "hot reload" in the browser; it is necessary to reload the page manually in the browser to see it re-rendered. Additionally, changes to `_config.yml` require stopping the server and restarting with `npm start` again. + +## Deploy previews + +Documentation repositories should be setup with a "deploy preview" feature which automatically provides "preview" links in the _status checks_ section of pull-requests. + +In the event that it's not possible to run the documentation locally, pushing changes to the branch for a pull-request can be a suitable alternative that ensures changes to the documentation are properly rendered. diff --git a/docs/package.json b/docs/package.json index 133c1361..ed706c90 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,12 +9,13 @@ "apollo-hexo-config": "1.0.8", "chexo": "1.0.5", "hexo": "3.7.1", + "hexo-browsersync": "0.3.0", "hexo-prism-plus": "1.0.0", "hexo-renderer-ejs": "0.3.1", "hexo-renderer-less": "0.2.0", "hexo-renderer-marked": "0.3.2", "hexo-server": "0.3.2", - "meteor-theme-hexo": "1.0.15" + "meteor-theme-hexo": "1.0.16" }, "scripts": { "start": "npm run build && chexo apollo-hexo-config -- server", @@ -23,6 +24,6 @@ "test": "npm run clean; npm run build" }, "dependencies": { - "hexo-versioned-netlify-redirects": "^1.0.7" + "hexo-versioned-netlify-redirects": "1.0.7" } } diff --git a/packages/apollo-server-express/src/apolloServerHttp.test.ts b/packages/apollo-server-express/src/apolloServerHttp.test.ts deleted file mode 100644 index 9e57ab91..00000000 --- a/packages/apollo-server-express/src/apolloServerHttp.test.ts +++ /dev/null @@ -1,481 +0,0 @@ -// tslint:disable -// TODO: enable when you figure out how to automatically fix trailing commas - -// TODO: maybe we should get rid of these tests entirely, and move them to expressApollo.test.ts - -// TODO: wherever possible the tests should be rewritten to make them easily work with Hapi, express, Koa etc. - -/* - * Below are the HTTP tests from express-graphql. We're using them here to make - * sure apolloServer still works if used in the place of express-graphql. - */ - -import { graphqlExpress } from './expressApollo'; - -/** - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -import { expect } from 'chai'; -import * as zlib from 'zlib'; -import * as multer from 'multer'; -import * as bodyParser from 'body-parser'; -import * as request from 'supertest'; -import * as express4 from 'express'; // modern -import { - GraphQLSchema, - GraphQLObjectType, - GraphQLNonNull, - GraphQLString, - GraphQLScalarType, - GraphQLError, - BREAK, -} from 'graphql'; - -const QueryRootType = new GraphQLObjectType({ - name: 'QueryRoot', - fields: { - test: { - type: GraphQLString, - args: { - who: { - type: GraphQLString, - }, - }, - resolve: (_, args) => 'Hello ' + (args['who'] || 'World'), - }, - thrower: { - type: new GraphQLNonNull(GraphQLString), - resolve: () => { - throw new Error('Throws!'); - }, - }, - custom: { - type: GraphQLString, - args: { - foo: { - type: new GraphQLScalarType({ - name: 'Foo', - serialize: v => v, - parseValue: () => { - throw new Error('Something bad happened'); - }, - parseLiteral: () => { - throw new Error('Something bad happened'); - }, - }), - }, - }, - }, - context: { - type: GraphQLString, - resolve: (_obj, _args, context) => context, - }, - }, -}); - -const TestSchema = new GraphQLSchema({ - query: QueryRootType, - mutation: new GraphQLObjectType({ - name: 'MutationRoot', - fields: { - writeTest: { - type: QueryRootType, - resolve: () => ({}), - }, - }, - }), -}); - -function catchError(p) { - return p.then( - res => { - // workaround for unknown issues with testing against npm package of express-graphql. - // the same code works when testing against the source, I'm not sure why. - if (res && res.error) { - return { response: res }; - } - throw new Error('Expected to catch error.'); - }, - error => { - if (!(error instanceof Error)) { - throw new Error('Expected error to be instanceof Error.'); - } - return error; - }, - ); -} - -function promiseTo(fn) { - return new Promise((resolve, reject) => { - fn((error, result) => (error ? reject(error) : resolve(result))); - }); -} - -describe('test harness', () => { - it('expects to catch errors', async () => { - let caught; - try { - await catchError(Promise.resolve()); - } catch (error) { - caught = error; - } - expect(caught && caught.message).to.equal('Expected to catch error.'); - }); - - it('expects to catch actual errors', async () => { - let caught; - try { - await catchError(Promise.reject('not a real error')); - } catch (error) { - caught = error; - } - expect(caught && caught.message).to.equal( - 'Expected error to be instanceof Error.', - ); - }); - - it('resolves callback promises', async () => { - const resolveValue = {}; - const result = await promiseTo(cb => cb(null, resolveValue)); - expect(result).to.equal(resolveValue); - }); - - it('rejects callback promises with errors', async () => { - const rejectError = new Error(); - let caught; - try { - await promiseTo(cb => cb(rejectError)); - } catch (error) { - caught = error; - } - expect(caught).to.equal(rejectError); - }); -}); - -const express = express4; -const version = 'modern'; -describe(`GraphQL-HTTP (apolloServer) tests for ${version} express`, () => { - describe('POST functionality', () => { - it('allows gzipped POST bodies', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use( - '/graphql', - graphqlExpress(() => ({ - schema: TestSchema, - })), - ); - - const data = { query: '{ test(who: "World") }' }; - const json = JSON.stringify(data); - // TODO had to write "as any as Buffer" to make tsc accept it. Does it matter? - const gzippedJson = await promiseTo(cb => - zlib.gzip((json as any) as Buffer, cb), - ); - - const req = request(app) - .post('/graphql') - .set('Content-Type', 'application/json') - .set('Content-Encoding', 'gzip'); - req.write(gzippedJson); - const response = await req; - - expect(JSON.parse(response.text)).to.deep.equal({ - data: { - test: 'Hello World', - }, - }); - }); - - it('allows deflated POST bodies', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use( - '/graphql', - graphqlExpress(() => ({ - schema: TestSchema, - })), - ); - - const data = { query: '{ test(who: "World") }' }; - const json = JSON.stringify(data); - // TODO had to write "as any as Buffer" to make tsc accept it. Does it matter? - const deflatedJson = await promiseTo(cb => - zlib.deflate((json as any) as Buffer, cb), - ); - - const req = request(app) - .post('/graphql') - .set('Content-Type', 'application/json') - .set('Content-Encoding', 'deflate'); - req.write(deflatedJson); - const response = await req; - - expect(JSON.parse(response.text)).to.deep.equal({ - data: { - test: 'Hello World', - }, - }); - }); - - it('allows for pre-parsed POST bodies', () => { - // Note: this is not the only way to handle file uploads with GraphQL, - // but it is terse and illustrative of using express-graphql and multer - // together. - - // A simple schema which includes a mutation. - const UploadedFileType = new GraphQLObjectType({ - name: 'UploadedFile', - fields: { - originalname: { type: GraphQLString }, - mimetype: { type: GraphQLString }, - }, - }); - - const TestMutationSchema = new GraphQLSchema({ - query: new GraphQLObjectType({ - name: 'QueryRoot', - fields: { - test: { type: GraphQLString }, - }, - }), - mutation: new GraphQLObjectType({ - name: 'MutationRoot', - fields: { - uploadFile: { - type: UploadedFileType, - resolve(rootValue) { - // For this test demo, we're just returning the uploaded - // file directly, but presumably you might return a Promise - // to go store the file somewhere first. - return rootValue.request.file; - }, - }, - }, - }), - }); - - const app = express(); - - // Multer provides multipart form data parsing. - const storage = multer.memoryStorage(); - app.use('/graphql', multer({ storage }).single('file')); - - // Providing the request as part of `rootValue` allows it to - // be accessible from within Schema resolve functions. - app.use( - '/graphql', - graphqlExpress(req => { - return { - schema: TestMutationSchema, - rootValue: { request: req }, - }; - }), - ); - - const req = request(app) - .post('/graphql') - .field( - 'query', - `mutation TestMutation { - uploadFile { originalname, mimetype } - }`, - ) - .attach('file', __filename); - - return req.then(response => { - expect(JSON.parse(response.text)).to.deep.equal({ - data: { - uploadFile: { - originalname: 'apolloServerHttp.test.js', - mimetype: 'application/javascript', - }, - }, - }); - }); - }); - }); - - describe('Error handling functionality', () => { - it('handles field errors caught by GraphQL', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use( - '/graphql', - graphqlExpress({ - schema: TestSchema, - }), - ); - - const response = await request(app) - .post('/graphql') - .send({ - query: '{thrower}', - }); - - expect(response.status).to.equal(200); - expect(JSON.parse(response.text)).to.deep.equal({ - data: null, - errors: [ - { - extensions: { - code: 'INTERNAL_SERVER_ERROR', - }, - message: 'Throws!', - locations: [{ line: 1, column: 2 }], - path: ['thrower'], - }, - ], - }); - }); - - it('handles type validation', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use( - '/graphql', - graphqlExpress({ - schema: TestSchema, - }), - ); - - const response = await request(app) - .post('/graphql') - .send({ - query: '{notExists}', - }); - - expect(response.status).to.equal(400); - expect(JSON.parse(response.text)).to.deep.equal({ - errors: [ - { - extensions: { - code: 'GRAPHQL_VALIDATION_FAILED', - }, - message: 'Cannot query field "notExists" on type "QueryRoot".', - locations: [{ line: 1, column: 2 }], - }, - ], - }); - }); - - it('handles type validation (GET)', async () => { - const app = express(); - - app.use( - '/graphql', - graphqlExpress({ - schema: TestSchema, - }), - ); - - const response = await request(app) - .get('/graphql') - .query({ query: '{notExists}' }); - - expect(response.status).to.equal(400); - expect(JSON.parse(response.text)).to.deep.equal({ - errors: [ - { - extensions: { - code: 'GRAPHQL_VALIDATION_FAILED', - }, - message: 'Cannot query field "notExists" on type "QueryRoot".', - locations: [{ line: 1, column: 2 }], - }, - ], - }); - }); - - it('handles errors thrown during custom graphql type handling', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use( - '/graphql', - graphqlExpress({ - schema: TestSchema, - }), - ); - - const response = await request(app) - .post('/graphql') - .send({ - query: '{custom(foo: 123)}', - }); - - expect(response.status).to.equal(400); - }); - - it('handles unsupported HTTP methods', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use('/graphql', graphqlExpress({ schema: TestSchema })); - - const response = await request(app) - .put('/graphql') - .query({ query: '{test}' }); - - expect(response.status).to.equal(405); - expect(response.headers.allow).to.equal('GET, POST'); - expect(response.text).to.contain( - 'Apollo Server supports only GET/POST requests.', - ); - }); - }); - - describe('Custom validation rules', () => { - const AlwaysInvalidRule = function(context) { - return { - enter() { - context.reportError( - new GraphQLError('AlwaysInvalidRule was really invalid!'), - ); - return BREAK; - }, - }; - }; - - it('Do not execute a query if it do not pass the custom validation.', async () => { - const app = express(); - - app.use('/graphql', bodyParser.json()); - app.use( - '/graphql', - graphqlExpress({ - schema: TestSchema, - validationRules: [AlwaysInvalidRule], - }), - ); - - const response = await request(app) - .post('/graphql') - .send({ - query: '{thrower}', - }); - - expect(response.status).to.equal(400); - expect(JSON.parse(response.text)).to.deep.equal({ - errors: [ - { - extensions: { - code: 'GRAPHQL_VALIDATION_FAILED', - }, - message: 'AlwaysInvalidRule was really invalid!', - }, - ], - }); - }); - }); -}); diff --git a/packages/apollo-server-hapi/src/hapiApollo.ts b/packages/apollo-server-hapi/src/hapiApollo.ts index 1197a0c3..08af7f0c 100644 --- a/packages/apollo-server-hapi/src/hapiApollo.ts +++ b/packages/apollo-server-hapi/src/hapiApollo.ts @@ -7,7 +7,7 @@ import { } from 'apollo-server-core'; export interface IRegister { - (server: Server, options: any): void; + (server: Server, options: any, next?: Function): void; } export interface IPlugin { @@ -29,7 +29,7 @@ export interface HapiPluginOptions { const graphqlHapi: IPlugin = { name: 'graphql', - register: (server: Server, options: HapiPluginOptions) => { + register: (server: Server, options: HapiPluginOptions, next?: Function) => { if (!options || !options.graphqlOptions) { throw new Error('Apollo Server requires options.'); } @@ -83,7 +83,9 @@ const graphqlHapi: IPlugin = { } }, }); + + if (next) { + next(); + } }, }; - -export { graphqlHapi }; diff --git a/packages/apollo-server-koa/src/apolloServerHttp.test.ts b/packages/apollo-server-koa/src/apolloServerHttp.test.ts deleted file mode 100644 index 3335e72b..00000000 --- a/packages/apollo-server-koa/src/apolloServerHttp.test.ts +++ /dev/null @@ -1,507 +0,0 @@ -// tslint:disable -// TODO: enable when you figure out how to automatically fix trailing commas - -// TODO: maybe we should get rid of these tests entirely, and move them to expressApollo.test.ts - -// TODO: wherever possible the tests should be rewritten to make them easily work with Hapi, express, Koa etc. - -/* - * Below are the HTTP tests from koa-graphql. We're using them here to make - * sure apolloServer still works if used in the place of koa-graphql. - */ - -import { graphqlKoa } from './koaApollo'; - -/** - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -import { expect } from 'chai'; -import * as zlib from 'zlib'; -import * as multer from 'koa-multer'; -import * as bodyParser from 'koa-bodyparser'; -import * as KoaRouter from 'koa-router'; -const request = require('supertest'); -const Koa = require('koa'); -import { - GraphQLSchema, - GraphQLObjectType, - GraphQLNonNull, - GraphQLString, - GraphQLScalarType, - GraphQLError, - BREAK, -} from 'graphql'; - -const QueryRootType = new GraphQLObjectType({ - name: 'QueryRoot', - fields: { - test: { - type: GraphQLString, - args: { - who: { - type: GraphQLString, - }, - }, - resolve: (_, args) => 'Hello ' + (args['who'] || 'World'), - }, - thrower: { - type: new GraphQLNonNull(GraphQLString), - resolve: () => { - throw new Error('Throws!'); - }, - }, - custom: { - type: GraphQLString, - args: { - foo: { - type: new GraphQLScalarType({ - name: 'Foo', - serialize: v => v, - parseValue: () => { - throw new Error('Something bad happened'); - }, - parseLiteral: () => { - throw new Error('Something bad happened'); - }, - }), - }, - }, - }, - context: { - type: GraphQLString, - resolve: (_obj, _args, context) => context, - }, - }, -}); - -const TestSchema = new GraphQLSchema({ - query: QueryRootType, - mutation: new GraphQLObjectType({ - name: 'MutationRoot', - fields: { - writeTest: { - type: QueryRootType, - resolve: () => ({}), - }, - }, - }), -}); - -function catchError(p) { - return p.then( - res => { - // workaround for unknown issues with testing against npm package of koa-graphql. - // the same code works when testing against the source, I'm not sure why. - if (res && res.error) { - return { response: res }; - } - throw new Error('Expected to catch error.'); - }, - error => { - if (!(error instanceof Error)) { - throw new Error('Expected error to be instanceof Error.'); - } - return error; - }, - ); -} - -function promiseTo(fn) { - return new Promise((resolve, reject) => { - fn((error, result) => (error ? reject(error) : resolve(result))); - }); -} - -describe('test harness', () => { - it('expects to catch errors', async () => { - let caught; - try { - await catchError(Promise.resolve()); - } catch (error) { - caught = error; - } - expect(caught && caught.message).to.equal('Expected to catch error.'); - }); - - it('expects to catch actual errors', async () => { - let caught; - try { - await catchError(Promise.reject('not a real error')); - } catch (error) { - caught = error; - } - expect(caught && caught.message).to.equal( - 'Expected error to be instanceof Error.', - ); - }); - - it('resolves callback promises', async () => { - const resolveValue = {}; - const result = await promiseTo(cb => cb(null, resolveValue)); - expect(result).to.equal(resolveValue); - }); - - it('rejects callback promises with errors', async () => { - const rejectError = new Error(); - let caught; - try { - await promiseTo(cb => cb(rejectError)); - } catch (error) { - caught = error; - } - expect(caught).to.equal(rejectError); - }); -}); - -describe(`GraphQL-HTTP (apolloServer) tests for koa`, () => { - describe('POST functionality', () => { - it('allows gzipped POST bodies', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all( - '/graphql', - graphqlKoa(() => ({ - schema: TestSchema, - })), - ); - - app.use(router.routes()); - - const data = { query: '{ test(who: "World") }' }; - const json = JSON.stringify(data); - // TODO had to write "as any as Buffer" to make tsc accept it. Does it matter? - const gzippedJson = await promiseTo(cb => - zlib.gzip((json as any) as Buffer, cb), - ); - - const req = request(app.callback()) - .post('/graphql') - .set('Content-Type', 'application/json') - .set('Content-Encoding', 'gzip'); - req.write(gzippedJson); - const response = await req; - - expect(JSON.parse(response.text)).to.deep.equal({ - data: { - test: 'Hello World', - }, - }); - }); - - it('allows deflated POST bodies', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all( - '/graphql', - graphqlKoa(() => ({ - schema: TestSchema, - })), - ); - - app.use(router.routes()); - - const data = { query: '{ test(who: "World") }' }; - const json = JSON.stringify(data); - // TODO had to write "as any as Buffer" to make tsc accept it. Does it matter? - const deflatedJson = await promiseTo(cb => - zlib.deflate((json as any) as Buffer, cb), - ); - - const req = request(app.callback()) - .post('/graphql') - .set('Content-Type', 'application/json') - .set('Content-Encoding', 'deflate'); - req.write(deflatedJson); - const response = await req; - - expect(JSON.parse(response.text)).to.deep.equal({ - data: { - test: 'Hello World', - }, - }); - }); - - it('allows for pre-parsed POST bodies', () => { - // Note: this is not the only way to handle file uploads with GraphQL, - // but it is terse and illustrative of using koa-graphql and multer - // together. - - // A simple schema which includes a mutation. - const UploadedFileType = new GraphQLObjectType({ - name: 'UploadedFile', - fields: { - originalname: { type: GraphQLString }, - mimetype: { type: GraphQLString }, - }, - }); - - const TestMutationSchema = new GraphQLSchema({ - query: new GraphQLObjectType({ - name: 'QueryRoot', - fields: { - test: { type: GraphQLString }, - }, - }), - mutation: new GraphQLObjectType({ - name: 'MutationRoot', - fields: { - uploadFile: { - type: UploadedFileType, - resolve(rootValue) { - // For this test demo, we're just returning the uploaded - // file directly, but presumably you might return a Promise - // to go store the file somewhere first. - return rootValue.request.file; - }, - }, - }, - }), - }); - - const app = new Koa(); - const router = new KoaRouter(); - - // Multer provides multipart form data parsing. - const storage = multer.memoryStorage(); - router.use('/graphql', multer({ storage }).single('file')); - - // Providing the request as part of `rootValue` allows it to - // be accessible from within Schema resolve functions. - router.all( - '/graphql', - graphqlKoa(ctx => { - return { - schema: TestMutationSchema, - rootValue: { request: ctx.req }, - }; - }), - ); - - app.use(router.routes()); - - const req = request(app.callback()) - .post('/graphql') - .field( - 'query', - `mutation TestMutation { - uploadFile { originalname, mimetype } - }`, - ) - .attach('file', __filename); - - return req.then(response => { - expect(JSON.parse(response.text)).to.deep.equal({ - data: { - uploadFile: { - originalname: 'apolloServerHttp.test.js', - mimetype: 'application/javascript', - }, - }, - }); - }); - }); - }); - - describe('Error handling functionality', () => { - it('handles field errors caught by GraphQL', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all( - '/graphql', - graphqlKoa({ - schema: TestSchema, - }), - ); - - app.use(router.routes()); - - const response = await request(app.callback()) - .post('/graphql') - .send({ - query: '{thrower}', - }); - - expect(response.status).to.equal(200); - expect(JSON.parse(response.text)).to.deep.equal({ - data: null, - errors: [ - { - extensions: { - code: 'INTERNAL_SERVER_ERROR', - }, - message: 'Throws!', - locations: [{ line: 1, column: 2 }], - path: ['thrower'], - }, - ], - }); - }); - - it('handles type validation', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all( - '/graphql', - graphqlKoa({ - schema: TestSchema, - }), - ); - - app.use(router.routes()); - - const response = await request(app.callback()) - .post('/graphql') - .send({ - query: '{notExists}', - }); - - expect(response.status).to.equal(400); - expect(JSON.parse(response.text)).to.deep.equal({ - errors: [ - { - extensions: { - code: 'GRAPHQL_VALIDATION_FAILED', - }, - message: 'Cannot query field "notExists" on type "QueryRoot".', - locations: [{ line: 1, column: 2 }], - }, - ], - }); - }); - - it('handles type validation (GET)', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.all( - '/graphql', - graphqlKoa({ - schema: TestSchema, - }), - ); - - app.use(router.routes()); - - const response = await request(app.callback()) - .get('/graphql') - .query({ query: '{notExists}' }); - - expect(response.status).to.equal(400); - expect(JSON.parse(response.text)).to.deep.equal({ - errors: [ - { - extensions: { - code: 'GRAPHQL_VALIDATION_FAILED', - }, - message: 'Cannot query field "notExists" on type "QueryRoot".', - locations: [{ line: 1, column: 2 }], - }, - ], - }); - }); - - it('handles errors thrown during custom graphql type handling', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all( - '/graphql', - graphqlKoa({ - schema: TestSchema, - }), - ); - - app.use(router.routes()); - - const response = await request(app.callback()) - .post('/graphql') - .send({ - query: '{custom(foo: 123)}', - }); - - expect(response.status).to.equal(400); - }); - - it('handles unsupported HTTP methods', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all('/graphql', graphqlKoa({ schema: TestSchema })); - - app.use(router.routes()); - - const response = await request(app.callback()) - .put('/graphql') - .query({ query: '{test}' }); - - expect(response.status).to.equal(405); - expect(response.headers.allow).to.equal('GET, POST'); - expect(response.text).to.contain( - 'Apollo Server supports only GET/POST requests.', - ); - }); - }); - - describe('Custom validation rules', () => { - const AlwaysInvalidRule = function(context) { - return { - enter() { - context.reportError( - new GraphQLError('AlwaysInvalidRule was really invalid!'), - ); - return BREAK; - }, - }; - }; - - it('Do not execute a query if it do not pass the custom validation.', async () => { - const app = new Koa(); - const router = new KoaRouter(); - - router.use('/graphql', bodyParser()); - router.all( - '/graphql', - graphqlKoa({ - schema: TestSchema, - validationRules: [AlwaysInvalidRule], - }), - ); - - app.use(router.routes()); - - const response = await request(app.callback()) - .post('/graphql') - .send({ - query: '{thrower}', - }); - - expect(response.status).to.equal(400); - expect(JSON.parse(response.text)).to.deep.equal({ - errors: [ - { - extensions: { - code: 'GRAPHQL_VALIDATION_FAILED', - }, - message: 'AlwaysInvalidRule was really invalid!', - }, - ], - }); - }); - }); -}); diff --git a/renovate.json b/renovate.json index d9beaf86..3b7c0b69 100644 --- a/renovate.json +++ b/renovate.json @@ -2,7 +2,7 @@ "extends": [":pinOnlyDevDependencies"], "semanticCommits": true, "timezone": "America/Los_Angeles", - "schedule": ["after 10pm and before 5am on every weekday"], + "schedule": ["after 6pm and before 8am on every weekday"], "rebaseStalePrs": true, "prCreation": "not-pending", "automerge": "minor", @@ -10,7 +10,11 @@ "pathRules": [ { "paths": ["docs/package.json"], - "extends": ["apollo-docs"] + "extends": ["apollo-docs"], + "baseBranches": [ + "master", + "version-2" + ] } ], "packageRules": [