From a60b4e66bf5d1444c42478e9fd43419815a6ebf5 Mon Sep 17 00:00:00 2001 From: David Yahalomi Date: Thu, 4 Aug 2016 12:32:26 +0300 Subject: [PATCH 1/3] Added `extend` support to schemaGenerator --- src/schemaGenerator.js | 29 ++++++++++++-- test/testSchemaGenerator.js | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/schemaGenerator.js b/src/schemaGenerator.js index dbf511a8bcf..0906504b517 100644 --- a/src/schemaGenerator.js +++ b/src/schemaGenerator.js @@ -3,9 +3,9 @@ // TODO: document each function clearly in the code: what arguments it accepts // and what it outputs. -import { parse } from 'graphql/language'; +import { parse, Kind } from 'graphql/language'; import uniq from 'lodash.uniq'; -import { buildASTSchema } from 'graphql/utilities'; +import { buildASTSchema, extendSchema } from 'graphql/utilities'; import { GraphQLScalarType, getNamedType, @@ -121,7 +121,30 @@ function buildSchemaFromTypeDefinitions(typeDefinitions) { } myDefinitions = concatenateTypeDefs(myDefinitions); } - return buildASTSchema(parse(myDefinitions)); + + const astDocument = parse(myDefinitions); + let schema = buildASTSchema(astDocument); + + const extensionsAst = extractExtensionDefinitions(astDocument); + if (extensionsAst.definitions.length > 0) { + schema = extendSchema(schema, extensionsAst); + } + + return schema; +} + +function extractExtensionDefinitions(ast) { + if (!ast || ast.kind !== Kind.DOCUMENT) { + return []; + } + + const extensionDefs = + ast.definitions.filter((def) => def.kind === Kind.TYPE_EXTENSION_DEFINITION); + + return { + ...ast, + definitions: extensionDefs, + }; } function forEachField(schema, fn) { diff --git a/test/testSchemaGenerator.js b/test/testSchemaGenerator.js index fef0b3d798c..491660c8e06 100644 --- a/test/testSchemaGenerator.js +++ b/test/testSchemaGenerator.js @@ -12,6 +12,7 @@ import { } from '../src/schemaGenerator.js'; import { assert, expect } from 'chai'; import { graphql, GraphQLInt, GraphQLObjectType, GraphQLSchema } from 'graphql'; +import { printSchema } from 'graphql/utilities'; import { Logger } from '../src/Logger.js'; import TypeA from './circularSchemaA'; @@ -223,6 +224,27 @@ describe('generating schema from shorthand', () => { const jsSchema = makeExecutableSchema({ typeDefs: typeDefAry, resolvers: {} }); expect(jsSchema.getQueryType().name).to.equal('Query'); }); + + it('can generate a schema from an array of types with extensions', () => { + const typeDefAry = [` + type Query { + foo: String + } + `, ` + schema { + query: Query + } + `, ` + extend type Query { + bar: String + } + `]; + + const jsSchema = makeExecutableSchema({ typeDefs: typeDefAry, resolvers: {} }); + expect(jsSchema.getQueryType().name).to.equal('Query'); + expect(jsSchema.getQueryType()._fields).to.have.all.keys('foo', 'bar'); + }); + it('properly deduplicates the array of type definitions', () => { const typeDefAry = [` type Query { @@ -306,6 +328,62 @@ describe('generating schema from shorthand', () => { return resultPromise.then(result => assert.deepEqual(result, solution)); }); + it('can generate a schema with extensions that can use resolvers', () => { + const shorthand = ` + type BirdSpecies { + name: String!, + wingspan: Int + } + type RootQuery { + species(name: String!): [BirdSpecies] + } + schema { + query: RootQuery + } + extend type BirdSpecies { + height: Float + } + `; + + const resolveFunctions = { + RootQuery: { + species: (root, { name }) => [{ + name: `Hello ${name}!`, + wingspan: 200, + height: 30.2, + }], + }, + BirdSpecies: { + name: (bird) => bird.name, + wingspan: (bird) => bird.wingspan, + height: (bird) => bird.height, + }, + }; + + const testQuery = `{ + species(name: "BigBird"){ + name + wingspan + height + } + }`; + + const solution = { + data: { + species: [ + { + name: 'Hello BigBird!', + wingspan: 200, + height: 30.2, + }, + ], + }, + }; + const jsSchema = makeExecutableSchema({ typeDefs: shorthand, resolvers: resolveFunctions }); + const resultPromise = graphql(jsSchema, testQuery); + return resultPromise.then(result => assert.deepEqual(result, solution)); + }); + it('supports resolveType for unions', () => { const shorthand = ` union Searchable = Person | Location From 7da4a961bc7957c282abc31ae0ad9d000fc5a2fc Mon Sep 17 00:00:00 2001 From: David Yahalomi Date: Thu, 4 Aug 2016 12:37:05 +0300 Subject: [PATCH 2/3] Added `extend` support to schemaGenerator * Changelog update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd21a50e2cb..a4894fd8c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +### v0.6.2 +* Added support for `extend` keyword to schemaGenerator https://github.com/apollostack/graphql-tools/pull/90 + ### v0.5.2 * Add addSchemaLevelResolveFunction to exports From c239ddd3a6068dcb863766468909e7011b12d7d5 Mon Sep 17 00:00:00 2001 From: David Yahalomi Date: Thu, 4 Aug 2016 22:37:24 +0300 Subject: [PATCH 3/3] Removed never-called branch of code. This is so due to parse function throwing an error or creating an AST document. * Also changed version on changelog to vNEXT as required --- CHANGELOG.md | 2 +- src/schemaGenerator.js | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4894fd8c28..292823e08d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -### v0.6.2 +### vNEXT * Added support for `extend` keyword to schemaGenerator https://github.com/apollostack/graphql-tools/pull/90 ### v0.5.2 diff --git a/src/schemaGenerator.js b/src/schemaGenerator.js index 0906504b517..74df1bc9fe5 100644 --- a/src/schemaGenerator.js +++ b/src/schemaGenerator.js @@ -134,10 +134,6 @@ function buildSchemaFromTypeDefinitions(typeDefinitions) { } function extractExtensionDefinitions(ast) { - if (!ast || ast.kind !== Kind.DOCUMENT) { - return []; - } - const extensionDefs = ast.definitions.filter((def) => def.kind === Kind.TYPE_EXTENSION_DEFINITION);