From 7d6aaa5029b77f751eb02b4a34153714685c6d9d Mon Sep 17 00:00:00 2001 From: schrockn Date: Mon, 27 Jul 2015 21:36:48 -0700 Subject: [PATCH] Schema DSL Language Reorgnization This PR does a few things. 1) Moves the Schema DSL printer from type/printer to language/schema/printer 2) Renames functions in order to make it more clear where they are in the pipeline in the various representations of schema: -- parseSchema --> parseSchemaIntoAST -- materializeSchema --> materializeSchemaAST -- new fn createSchemaFromDSL that goes from DSL --> in-mem GraphQLSchema 3) Creates flow types for the introspection query 4) Uses those flow types in the printer --- src/index.js | 2 + src/language/schema/__tests__/materializer.js | 71 +++---- src/language/schema/__tests__/parser.js | 38 ++-- .../schema}/__tests__/printer.js | 106 +++++------ src/language/schema/index.js | 29 +++ src/language/schema/materializer.js | 3 +- src/language/schema/parser.js | 2 +- src/language/schema/printer.js | 177 +++++++++++++++++- src/type/introspectionQuery.js | 44 +++++ src/type/printer.js | 155 --------------- 10 files changed, 352 insertions(+), 275 deletions(-) rename src/{type => language/schema}/__tests__/printer.js (78%) create mode 100644 src/language/schema/index.js delete mode 100644 src/type/printer.js diff --git a/src/index.js b/src/index.js index 3839bfb3af..6e7ecc8aab 100644 --- a/src/index.js +++ b/src/index.js @@ -36,3 +36,5 @@ export { // Produce and format errors. export { GraphQLError, formatError } from './error'; + +export { printSchema } from './language/schema/printer'; diff --git a/src/language/schema/__tests__/materializer.js b/src/language/schema/__tests__/materializer.js index 5a0aba5078..57d452bbfc 100644 --- a/src/language/schema/__tests__/materializer.js +++ b/src/language/schema/__tests__/materializer.js @@ -9,21 +9,24 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import { parseSchema } from '../parser'; -import { materializeSchema } from '../materializer'; -import { printSchema } from '../../../type/printer'; -import { getIntrospectionResult } from '../printer'; +import { parseSchemaIntoAST } from '../parser'; +import { printSchema } from '../'; +import { materializeSchemaAST } from '../materializer'; +import { createSchemaFromDSL } from '../'; // 80+ char lines are useful in describe/it, so ignore in this file. /*eslint-disable max-len */ -function printForTest(result) { - return '\n' + printSchema(result) + '\n'; -} - -async function getOutput(body, queryType) { - var result = await getIntrospectionResult(body, queryType); - return await printForTest(result); +/** + * This function does a full cycle of going from a + * string with the contents of the DSL, parsed + * in a schema AST, materializing that schema ast + * into an in-memory GraphQLSchema, and then finally + * printing that GraphQL into the DSL + */ +async function cycleOutput(body, queryType) { + var schema = await createSchemaFromDSL(body, queryType); + return '\n' + await printSchema(schema) + '\n'; } describe('Schema Materializer', () => { @@ -35,7 +38,7 @@ type HelloScalars { bool: Boolean } `; - var output = await getOutput(body, 'HelloScalars'); + var output = await cycleOutput(body, 'HelloScalars'); expect(output).to.equal(body); }); @@ -49,7 +52,7 @@ type HelloScalars { nonNullListOfNonNullStrs: [String!]! } `; - var output = await getOutput(body, 'HelloScalars'); + var output = await cycleOutput(body, 'HelloScalars'); expect(output).to.equal(body); }); @@ -61,7 +64,7 @@ type Recurse { recurse: Recurse } `; - var output = await getOutput(body, 'Recurse'); + var output = await cycleOutput(body, 'Recurse'); expect(output).to.equal(body); }); @@ -77,7 +80,7 @@ type TypeTwo { typeOne: TypeOne } `; - var output = await getOutput(body, 'TypeOne'); + var output = await cycleOutput(body, 'TypeOne'); expect(output).to.equal(body); }); @@ -87,7 +90,7 @@ type Hello { str(int: Int): String } `; - var output = await getOutput(body, 'Hello'); + var output = await cycleOutput(body, 'Hello'); expect(output).to.equal(body); }); @@ -97,7 +100,7 @@ type Hello { str(int: Int, bool: Boolean): String } `; - var output = await getOutput(body, 'Hello'); + var output = await cycleOutput(body, 'Hello'); expect(output).to.equal(body); }); @@ -111,7 +114,7 @@ interface WorldInterface { str: String } `; - var output = await getOutput(body, 'HelloInterface'); + var output = await cycleOutput(body, 'HelloInterface'); expect(output).to.equal(body); }); @@ -125,7 +128,7 @@ type OutputEnumRoot { hello: Hello } `; - var output = await getOutput(body, 'OutputEnumRoot'); + var output = await cycleOutput(body, 'OutputEnumRoot'); expect(output).to.equal(body); }); @@ -139,7 +142,7 @@ type InputEnumRoot { str(hello: Hello): String } `; - var output = await getOutput(body, 'InputEnumRoot'); + var output = await cycleOutput(body, 'InputEnumRoot'); expect(output).to.equal(body); }); @@ -154,7 +157,7 @@ type OutputEnumRoot { hello: Hello } `; - var output = await getOutput(body, 'OutputEnumRoot'); + var output = await cycleOutput(body, 'OutputEnumRoot'); expect(output).to.equal(body); }); @@ -170,7 +173,7 @@ type World { str: String } `; - var output = await getOutput(body, 'Root'); + var output = await cycleOutput(body, 'Root'); expect(output).to.equal(body); }); @@ -190,7 +193,7 @@ type WorldTwo { str: String } `; - var output = await getOutput(body, 'Root'); + var output = await cycleOutput(body, 'Root'); expect(output).to.equal(body); }); @@ -203,7 +206,7 @@ type Root { } `; - var output = await getOutput(body, 'Root'); + var output = await cycleOutput(body, 'Root'); expect(output).to.equal(body); }); @@ -218,7 +221,7 @@ type Root { } `; - var output = await getOutput(body, 'Root'); + var output = await cycleOutput(body, 'Root'); expect(output).to.equal(body); }); @@ -228,7 +231,7 @@ type Hello { str(int: Int = 2): String } `; - var output = await getOutput(body, 'Hello'); + var output = await cycleOutput(body, 'Hello'); expect(output).to.equal(body); }); }); @@ -240,16 +243,16 @@ type Hello { bar: Bar } `; - var doc = parseSchema(body); - expect(() => materializeSchema(doc, 'Hello')).to.throw('Type Bar not found in document'); + var doc = parseSchemaIntoAST(body); + expect(() => materializeSchemaAST(doc, 'Hello')).to.throw('Type Bar not found in document'); }); it('Unknown type in interface list', () => { var body = ` type Hello implements Bar { } `; - var doc = parseSchema(body); - expect(() => materializeSchema(doc, 'Hello')).to.throw('Type Bar not found in document'); + var doc = parseSchemaIntoAST(body); + expect(() => materializeSchemaAST(doc, 'Hello')).to.throw('Type Bar not found in document'); }); it('Unknown type in union list', () => { @@ -257,8 +260,8 @@ type Hello implements Bar { } union TestUnion = Bar type Hello { testUnion: TestUnion } `; - var doc = parseSchema(body); - expect(() => materializeSchema(doc, 'Hello')).to.throw('Type Bar not found in document'); + var doc = parseSchemaIntoAST(body); + expect(() => materializeSchemaAST(doc, 'Hello')).to.throw('Type Bar not found in document'); }); @@ -268,7 +271,7 @@ type Hello { str: String } `; - var doc = parseSchema(body); - expect(() => materializeSchema(doc, 'Wat')).to.throw('Specified query type Wat not found in document'); + var doc = parseSchemaIntoAST(body); + expect(() => materializeSchemaAST(doc, 'Wat')).to.throw('Specified query type Wat not found in document'); }); }); diff --git a/src/language/schema/__tests__/parser.js b/src/language/schema/__tests__/parser.js index 7521696536..5ad1b49c43 100644 --- a/src/language/schema/__tests__/parser.js +++ b/src/language/schema/__tests__/parser.js @@ -9,7 +9,7 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import { parseSchema } from '../parser'; +import { parseSchemaIntoAST } from '../parser'; function createLocFn(body) { return (start, end) => ({ @@ -79,7 +79,7 @@ describe('Schema Parser', () => { type Hello { world: String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -109,7 +109,7 @@ type Hello { world: String! }`; var loc = createLocFn(body); - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var expected = { kind: 'SchemaDocument', definitions: [ @@ -140,7 +140,7 @@ type Hello { it('Simple type inheriting interface', () => { var body = `type Hello implements World { }`; var loc = createLocFn(body); - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var expected = { kind: 'SchemaDocument', definitions: [ @@ -160,7 +160,7 @@ type Hello { it('Simple type inheriting multiple interfaces', () => { var body = `type Hello implements Wo, rld { }`; var loc = createLocFn(body); - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var expected = { kind: 'SchemaDocument', definitions: [ @@ -183,7 +183,7 @@ type Hello { it('Single value enum', () => { var body = `enum Hello { WORLD }`; var loc = createLocFn(body); - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var expected = { kind: 'SchemaDocument', definitions: [ @@ -202,7 +202,7 @@ type Hello { it('Double value enum', () => { var body = `enum Hello { WO, RLD }`; var loc = createLocFn(body); - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var expected = { kind: 'SchemaDocument', definitions: [ @@ -226,7 +226,7 @@ type Hello { interface Hello { world: String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -254,7 +254,7 @@ interface Hello { type Hello { world(flag: Boolean): String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -292,7 +292,7 @@ type Hello { type Hello { world(flag: Boolean = true): String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -334,7 +334,7 @@ type Hello { type Hello { world(things: [String]): String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -376,7 +376,7 @@ type Hello { type Hello { world(argOne: Boolean, argTwo: Int): String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -418,7 +418,7 @@ type Hello { it('Simple union', () => { var body = `union Hello = World`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -437,7 +437,7 @@ type Hello { it('Union with two types', () => { var body = `union Hello = Wo | Rld`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -459,7 +459,7 @@ type Hello { it('Scalar', () => { var body = `scalar Hello`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -480,7 +480,7 @@ type Hello { input Hello { world: String }`; - var doc = parseSchema(body); + var doc = parseSchemaIntoAST(body); var loc = createLocFn(body); var expected = { kind: 'SchemaDocument', @@ -508,16 +508,16 @@ input Hello { input Hello { world(foo: Int): String }`; - expect(() => parseSchema(body)).to.throw('Error'); + expect(() => parseSchemaIntoAST(body)).to.throw('Error'); }); it('Reject query keywords', () => { var body = `query Foo { field }`; - expect(() => parseSchema(body)).to.throw('Error'); + expect(() => parseSchemaIntoAST(body)).to.throw('Error'); }); it('Reject query shorthand', () => { var body = `{ field }`; - expect(() => parseSchema(body)).to.throw('Error'); + expect(() => parseSchemaIntoAST(body)).to.throw('Error'); }); }); diff --git a/src/type/__tests__/printer.js b/src/language/schema/__tests__/printer.js similarity index 78% rename from src/type/__tests__/printer.js rename to src/language/schema/__tests__/printer.js index 20e4c04fbd..a17bcfd71e 100644 --- a/src/type/__tests__/printer.js +++ b/src/language/schema/__tests__/printer.js @@ -1,6 +1,6 @@ /** * Copyright (c) 2015, Facebook, Inc. - All rights reserved. + * 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 @@ -10,8 +10,6 @@ import { describe, it } from 'mocha'; import { expect } from 'chai'; import { printSchema, printIntrospectionSchema } from '../printer'; -import { introspectionQuery } from '../introspectionQuery'; -import { graphql } from '../../'; import { GraphQLSchema, @@ -26,13 +24,13 @@ import { GraphQLBoolean, GraphQLList, GraphQLNonNull, -} from '../'; +} from '../../../type'; // 80+ char lines are useful in describe/it, so ignore in this file. /*eslint-disable max-len */ -function printForTest(result) { - return '\n' + printSchema(result) + '\n'; +async function printForTest(schema) { + return '\n' + await printSchema(schema) + '\n'; } async function printSingleFieldSchema(fieldConfig) { @@ -40,9 +38,7 @@ async function printSingleFieldSchema(fieldConfig) { name: 'Root', fields: { singleField: fieldConfig }, }); - var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - return printForTest(result); + return await printForTest(new GraphQLSchema({query: Root})); } function listOf(type) { @@ -55,8 +51,8 @@ function nonNull(type) { describe('Type System Printer', () => { it('Prints String Field', async () => { - var schema = await printSingleFieldSchema({type: GraphQLString}); - expect(schema).to.equal(` + var output = await printSingleFieldSchema({type: GraphQLString}); + expect(output).to.equal(` type Root { singleField: String } @@ -65,8 +61,8 @@ type Root { }); it('Prints [String] Field', async () => { - var schema = await printSingleFieldSchema({type: listOf(GraphQLString)}); - expect(schema).to.equal(` + var output = await printSingleFieldSchema({type: listOf(GraphQLString)}); + expect(output).to.equal(` type Root { singleField: [String] } @@ -75,8 +71,8 @@ type Root { }); it('Prints String! Field', async () => { - var schema = await printSingleFieldSchema({type: nonNull(GraphQLString)}); - expect(schema).to.equal(` + var output = await printSingleFieldSchema({type: nonNull(GraphQLString)}); + expect(output).to.equal(` type Root { singleField: String! } @@ -85,8 +81,8 @@ type Root { }); it('Prints [String]! Field', async () => { - var schema = await printSingleFieldSchema({type: nonNull(listOf(GraphQLString))}); - expect(schema).to.equal(` + var output = await printSingleFieldSchema({type: nonNull(listOf(GraphQLString))}); + expect(output).to.equal(` type Root { singleField: [String]! } @@ -95,8 +91,8 @@ type Root { }); it('Prints [String!] Field', async () => { - var schema = await printSingleFieldSchema({type: listOf(nonNull(GraphQLString))}); - expect(schema).to.equal(` + var output = await printSingleFieldSchema({type: listOf(nonNull(GraphQLString))}); + expect(output).to.equal(` type Root { singleField: [String!] } @@ -105,8 +101,8 @@ type Root { }); it('Prints [String!]! Field', async () => { - var schema = await printSingleFieldSchema({type: nonNull(listOf(nonNull(GraphQLString)))}); - expect(schema).to.equal(` + var output = await printSingleFieldSchema({type: nonNull(listOf(nonNull(GraphQLString)))}); + expect(output).to.equal(` type Root { singleField: [String!]! } @@ -126,9 +122,8 @@ type Root { }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` type Foo { str: String } @@ -141,13 +136,13 @@ type Root { }); it('Prints String Field With Int Arg', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { argOne: { type: GraphQLInt } }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int): String } @@ -156,13 +151,13 @@ type Root { }); it('Prints String Field With Int Arg With Default', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { argOne: { type: GraphQLInt, defaultValue: 2 } }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int = 2): String } @@ -171,13 +166,13 @@ type Root { }); it('Prints String Field With Int! Arg', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { argOne: { type: nonNull(GraphQLInt) } }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int!): String } @@ -186,7 +181,7 @@ type Root { }); it('Prints String Field With Multiple Args', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { @@ -195,7 +190,7 @@ type Root { }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int, argTwo: String): String } @@ -204,7 +199,7 @@ type Root { }); it('Prints String Field With Multiple Args, First is Default', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { @@ -214,7 +209,7 @@ type Root { }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int = 1, argTwo: String, argThree: Boolean): String } @@ -223,7 +218,7 @@ type Root { }); it('Prints String Field With Multiple Args, Second is Default', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { @@ -233,7 +228,7 @@ type Root { }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int, argTwo: String = "foo", argThree: Boolean): String } @@ -242,7 +237,7 @@ type Root { }); it('Prints String Field With Multiple Args, Last is Default', async () => { - var schema = await printSingleFieldSchema( + var output = await printSingleFieldSchema( { type: GraphQLString, args: { @@ -252,7 +247,7 @@ type Root { }, } ); - expect(schema).to.equal(` + expect(output).to.equal(` type Root { singleField(argOne: Int, argTwo: String, argThree: Boolean = false): String } @@ -278,9 +273,8 @@ type Root { }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` type Bar implements Foo { str: String } @@ -322,9 +316,8 @@ type Root { }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` interface Baaz { int: Int } @@ -379,9 +372,8 @@ type Root { }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` type Bar { str: String } @@ -421,9 +413,8 @@ union SingleUnion = Foo }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` input InputType { int: Int } @@ -450,9 +441,8 @@ type Root { }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` scalar Odd type Root { @@ -480,9 +470,8 @@ type Root { }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = printForTest(result); - expect(schema).to.equal(` + var output = await printForTest(Schema); + expect(output).to.equal(` enum RGB { RED GREEN @@ -501,8 +490,7 @@ type Root { fields: {}, }); var Schema = new GraphQLSchema({query: Root}); - var result = await graphql(Schema, introspectionQuery); - var schema = '\n' + printIntrospectionSchema(result) + '\n'; + var output = '\n' + await printIntrospectionSchema(Schema) + '\n'; var introspectionSchema = ` type __Directive { name: String! @@ -566,6 +554,6 @@ enum __TypeKind { NON_NULL } `; - expect(schema).to.equal(introspectionSchema); + expect(output).to.equal(introspectionSchema); }); }); diff --git a/src/language/schema/index.js b/src/language/schema/index.js new file mode 100644 index 0000000000..32452232fe --- /dev/null +++ b/src/language/schema/index.js @@ -0,0 +1,29 @@ +/** + * 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 { GraphQLSchema } from '../../type'; +import { parseSchemaIntoAST } from './parser'; +import { materializeSchemaAST } from './materializer'; + +export { + // GraphQL Schema Instance --> DSL + printSchema, + printIntrospectionSchema, + // Introspection Result --> DSL + printSchemaFromResult, +} from './printer'; + +// DSL --> Schema +export async function createSchemaFromDSL( + schemaDSL: string, + queryType: string +) : GraphQLSchema { + var doc = parseSchemaIntoAST(schemaDSL); + return await materializeSchemaAST(doc, queryType); +} diff --git a/src/language/schema/materializer.js b/src/language/schema/materializer.js index 73c2d20e85..93f95eaa4c 100644 --- a/src/language/schema/materializer.js +++ b/src/language/schema/materializer.js @@ -89,6 +89,7 @@ function getInnerTypeName(typeAST) { return typeAST.name.value; } + /** * This takes the ast of a schema document produced by parseSchema in * src/language/schema/parser.js. @@ -97,7 +98,7 @@ function getInnerTypeName(typeAST) { * they are not particularly useful for non-introspection queries * since they have no resolve methods. */ -export function materializeSchema( +export function materializeSchemaAST( ast: SchemaDocument, queryTypeName: string, mutationTypeName: ?string): GraphQLSchema { diff --git a/src/language/schema/parser.js b/src/language/schema/parser.js index 12ad7afc7c..5beba96e14 100644 --- a/src/language/schema/parser.js +++ b/src/language/schema/parser.js @@ -64,7 +64,7 @@ import { INPUT_FIELD_DEFINITION, } from './kinds'; -export function parseSchema( +export function parseSchemaIntoAST( source: Source | string, options?: ParseOptions ): SchemaDocument { diff --git a/src/language/schema/printer.js b/src/language/schema/printer.js index 217257a9be..c4149e309c 100644 --- a/src/language/schema/printer.js +++ b/src/language/schema/printer.js @@ -1,3 +1,4 @@ +/* @flow */ /** * Copyright (c) 2015, Facebook, Inc. * All rights reserved. @@ -7,13 +8,177 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -import { parseSchema } from './parser'; -import { materializeSchema } from './materializer'; import { introspectionQuery } from './../../type/introspectionQuery'; import { graphql } from '../../'; -export async function getIntrospectionResult(body, queryType) { - var doc = parseSchema(body); - var schema = materializeSchema(doc, queryType); - return await graphql(schema, introspectionQuery); +import type { + IntrospectionQueryResult, + FullType, + InputField, + TypeRef, + FieldResult, + ArgResult, + EnumValue, +} from '../../type/introspectionQuery'; + +import type { + GraphQLSchema +} from '../../type'; + +var TypeKind = { + SCALAR: 'SCALAR', + OBJECT: 'OBJECT', + INTERFACE: 'INTERFACE', + UNION: 'UNION', + ENUM: 'ENUM', + INPUT_OBJECT: 'INPUT_OBJECT', + LIST: 'LIST', + NON_NULL: 'NON_NULL', +}; + +// e.g Int, Int!, [Int!]! +function printTypeDecl(type: TypeRef): string { + if (type.kind === TypeKind.LIST) { + return '[' + printTypeDecl(type.ofType) + ']'; + } else if (type.kind === TypeKind.NON_NULL) { + return printTypeDecl(type.ofType) + '!'; + } else { + return type.name; + } +} + +function printField(field: FieldResult): string { + return `${field.name}${printArgs(field)}: ${printTypeDecl(field.type)}`; +} + +function printArg(arg: ArgResult): string { + var argDecl = `${arg.name}: ${printTypeDecl(arg.type)}`; + if (arg.defaultValue !== null) { + argDecl += ` = ${arg.defaultValue}`; + } + return argDecl; +} + +function printArgs(field: FieldResult): string { + if (field.args.length === 0) { + return ''; + } + return '(' + field.args.map(printArg).join(', ') + ')'; +} + +function printImplementedInterfaces(type: FullType) { + if (type.interfaces.length === 0) { + return ''; + } + return ' implements ' + type.interfaces.map(i => i.name).join(', '); +} + +function printObject(type: FullType) { + return `type ${type.name}${printImplementedInterfaces(type)} {` + '\n' + + printFields(type.fields) + '\n' + + '}'; +} + +function printInterface(type: FullType) { + return `interface ${type.name} {` + '\n' + + printFields(type.fields) + '\n' + + '}'; +} + +function printInputObject(type: FullType) { + return `input ${type.name} {` + '\n' + + printInputFields(type.inputFields) + '\n' + + '}'; +} + +function printFields(fields: Array) { + return fields.map(f => ' ' + printField(f)).join('\n'); +} + +function printInputFields(fields: Array) { + return fields.map(f => ' ' + printInputField(f)).join('\n'); +} + +function printInputField(field: InputField) { + return `${field.name}: ${printTypeDecl(field.type)}`; +} + +function printUnion(type: FullType) { + var typeList = type.possibleTypes.map(t => t.name).join(' | '); + return `union ${type.name} = ${typeList}`; } + +function printScalar(type: FullType) { + return `scalar ${type.name}`; +} + +function printEnumValues(values: Array) { + return values.map(v => ' ' + v.name).join('\n'); +} + +function printEnum(type: FullType) { + return `enum ${type.name} { +${printEnumValues(type.enumValues)} +}`; +} + +function printType(type: FullType) { + switch (type.kind) { + case TypeKind.OBJECT: + return printObject(type); + case TypeKind.UNION: + return printUnion(type); + case TypeKind.INTERFACE: + return printInterface(type); + case TypeKind.INPUT_OBJECT: + return printInputObject(type); + case TypeKind.SCALAR: + return printScalar(type); + case TypeKind.ENUM: + return printEnum(type); + default: + throw new Error('Invalid kind: ' + type.kind); + } +} + +function isBuiltInScalar(type) { + return type.name === 'String' || + type.name === 'Boolean' || + type.name === 'Int' || + type.name === 'Float' || + type.name === 'ID'; +} + +function isIntrospectionType(type) { + return type.name.startsWith('__'); +} + +function isBuiltIn(type) { + return isIntrospectionType(type) || isBuiltInScalar(type); +} + +export async function printSchema(schema: GraphQLSchema): Promise { + return await printFilteredSchema(schema, t => !isBuiltIn(t)); +} + +export async function printIntrospectionSchema( + schema: GraphQLSchema): Promise { + return await printFilteredSchema(schema, isIntrospectionType); +} + +export function printSchemaFromResult(result: any): string { + return printFilteredSchemaFromResult(result, t => !isBuiltIn(t)); +} + +function printFilteredSchemaFromResult(result, typeFilter) { + var schemaResult: IntrospectionQueryResult = result.data.__schema; + var types = schemaResult.types.filter(typeFilter); + types = types.sort((t1, t2) => t1.name.localeCompare(t2.name)); + return types.map(printType).join('\n\n'); +} + +async function printFilteredSchema(schema, typeFilter) { + var result = await graphql(schema, introspectionQuery); + return printFilteredSchemaFromResult(result, typeFilter); +} + diff --git a/src/type/introspectionQuery.js b/src/type/introspectionQuery.js index 70e27c0e7b..d4540aec5c 100644 --- a/src/type/introspectionQuery.js +++ b/src/type/introspectionQuery.js @@ -81,3 +81,47 @@ export var introspectionQuery = ` } `; +export type IntrospectionQueryResult = { + types: Array; +} + +export type FullType = { + kind: string; + name: string; + fields: Array; + enumValues: Array; + inputFields: Array; + possibleTypes: Array; + interfaces: Array; +} + +export type InputField = { + name: string; + type: TypeRef; + defaultValue: string; +} + +export type TypeRef = { + kind: string; + name: string; + ofType: TypeRef; +} + +export type FieldResult = { + name: string; + args: Array; + type: TypeRef; +} + +export type ArgResult = { + name: string; + type: TypeRef; + defaultValue: string; +} + +export type EnumValue = { + name: string; + isDeprecated: boolean; + deprecationReason: string; +} + diff --git a/src/type/printer.js b/src/type/printer.js deleted file mode 100644 index 2c159f0e8d..0000000000 --- a/src/type/printer.js +++ /dev/null @@ -1,155 +0,0 @@ -/** - * 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. - */ - -var TypeKind = { - SCALAR: 'SCALAR', - OBJECT: 'OBJECT', - INTERFACE: 'INTERFACE', - UNION: 'UNION', - ENUM: 'ENUM', - INPUT_OBJECT: 'INPUT_OBJECT', - LIST: 'LIST', - NON_NULL: 'NON_NULL', -}; - -// e.g Int, Int!, [Int!]! -function printTypeDecl(type) { - if (type.kind === TypeKind.LIST) { - return '[' + printTypeDecl(type.ofType) + ']'; - } else if (type.kind === TypeKind.NON_NULL) { - return printTypeDecl(type.ofType) + '!'; - } else { - return type.name; - } -} - -function printField(field) { - return `${field.name}${printArgs(field)}: ${printTypeDecl(field.type)}`; -} - -function printArg(arg) { - var argDecl = `${arg.name}: ${printTypeDecl(arg.type)}`; - if (arg.defaultValue !== null) { - argDecl += ` = ${arg.defaultValue}`; - } - return argDecl; -} - -function printArgs(field) { - if (field.args.length === 0) { - return ''; - } - return '(' + field.args.map(printArg).join(', ') + ')'; -} - -function printImplementedInterfaces(type) { - if (type.interfaces.length === 0) { - return ''; - } - return ' implements ' + type.interfaces.map(i => i.name).join(', '); -} - -function printObject(type) { - return `type ${type.name}${printImplementedInterfaces(type)} {` + '\n' + - printFields(type.fields) + '\n' + - '}'; -} - -function printInterface(type) { - return `interface ${type.name} {` + '\n' + - printFields(type.fields) + '\n' + - '}'; -} - -function printInputObject(type) { - return `input ${type.name} {` + '\n' + - printInputFields(type.inputFields) + '\n' + - '}'; -} - -function printFields(fields) { - return fields.map(f => ' ' + printField(f)).join('\n'); -} - -function printInputFields(fields) { - return fields.map(f => ' ' + printInputField(f)).join('\n'); -} - -function printInputField(field) { - return `${field.name}: ${printTypeDecl(field.type)}`; -} - - -function printUnion(type) { - var typeList = type.possibleTypes.map(t => t.name).join(' | '); - return `union ${type.name} = ${typeList}`; -} - -function printScalar(type) { - return `scalar ${type.name}`; -} - -function printEnumValues(values) { - return values.map(v => ' ' + v.name).join('\n'); -} - -function printEnum(type) { - return `enum ${type.name} { -${printEnumValues(type.enumValues)} -}`; -} - -function printType(type) { - switch (type.kind) { - case TypeKind.OBJECT: - return printObject(type); - case TypeKind.UNION: - return printUnion(type); - case TypeKind.INTERFACE: - return printInterface(type); - case TypeKind.INPUT_OBJECT: - return printInputObject(type); - case TypeKind.SCALAR: - return printScalar(type); - case TypeKind.ENUM: - return printEnum(type); - default: - throw new Error('Invalid kind: ' + type.kind); - } -} - -function isBuiltInScalar(type) { - return type.name === 'String' || - type.name === 'Boolean' || - type.name === 'Int' || - type.name === 'Float' || - type.name === 'ID'; -} - -function isIntrospectionType(type) { - return type.name.startsWith('__'); -} - -function isBuiltIn(type) { - return isIntrospectionType(type) || isBuiltInScalar(type); -} - -export function printSchema(introspectionResult) { - var schema = introspectionResult.data.__schema; - var types = schema.types.filter(t => !isBuiltIn(t)); - types = types.sort((t1, t2) => t1.name.localeCompare(t2.name)); - return types.map(printType).join('\n\n'); -} - -export function printIntrospectionSchema(introspectionResult) { - var schema = introspectionResult.data.__schema; - var types = schema.types.filter(t => isIntrospectionType(t)); - types = types.sort((t1, t2) => t1.name.localeCompare(t2.name)); - return types.map(printType).join('\n\n'); -}