diff --git a/src/execution/__tests__/abstract-test.js b/src/execution/__tests__/abstract-test.js index 38d177c914..020d935719 100644 --- a/src/execution/__tests__/abstract-test.js +++ b/src/execution/__tests__/abstract-test.js @@ -5,6 +5,8 @@ import { describe, it } from 'mocha'; import invariant from '../../jsutils/invariant'; +import { parse } from '../../language/parser'; + import { GraphQLSchema } from '../../type/schema'; import { GraphQLString, GraphQLBoolean } from '../../type/scalars'; import { @@ -14,7 +16,7 @@ import { GraphQLUnionType, } from '../../type/definition'; -import { graphqlSync } from '../../graphql'; +import { executeSync } from '../execute'; class Dog { name: string; @@ -88,7 +90,7 @@ describe('Execute: Handles execution of abstract types', () => { types: [CatType, DogType], }); - const query = ` + const document = parse(` { pets { name @@ -100,11 +102,9 @@ describe('Execute: Handles execution of abstract types', () => { } } } - `; - - const result = graphqlSync({ schema, source: query }); + `); - expect(result).to.deep.equal({ + expect(executeSync({ schema, document })).to.deep.equal({ data: { pets: [ { @@ -158,7 +158,7 @@ describe('Execute: Handles execution of abstract types', () => { }), }); - const query = `{ + const document = parse(`{ pets { ... on Dog { name @@ -169,11 +169,9 @@ describe('Execute: Handles execution of abstract types', () => { meows } } - }`; + }`); - const result = graphqlSync({ schema, source: query }); - - expect(result).to.deep.equal({ + expect(executeSync({ schema, document })).to.deep.equal({ data: { pets: [ { @@ -256,7 +254,7 @@ describe('Execute: Handles execution of abstract types', () => { types: [CatType, DogType], }); - const query = ` + const document = parse(` { pets { name @@ -268,9 +266,9 @@ describe('Execute: Handles execution of abstract types', () => { } } } - `; + `); - const result = graphqlSync({ schema, source: query }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: { @@ -359,7 +357,7 @@ describe('Execute: Handles execution of abstract types', () => { }), }); - const query = ` + const document = parse(` { pets { ... on Dog { @@ -372,9 +370,9 @@ describe('Execute: Handles execution of abstract types', () => { } } } - `; + `); - const result = graphqlSync({ schema, source: query }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: { @@ -430,9 +428,9 @@ describe('Execute: Handles execution of abstract types', () => { types: [fooObject], }); - const result = graphqlSync({ schema, source: '{ foo { bar } }' }); + const document = parse('{ foo { bar } }'); - expect(result).to.deep.equal({ + expect(executeSync({ schema, document })).to.deep.equal({ data: { foo: null }, errors: [ { @@ -470,9 +468,9 @@ describe('Execute: Handles execution of abstract types', () => { types: [fooObject], }); - const result = graphqlSync({ schema, source: '{ foo { bar } }' }); + const document = parse('{ foo { bar } }'); - expect(result).to.deep.equal({ + expect(executeSync({ schema, document })).to.deep.equal({ data: { foo: null }, errors: [ { @@ -538,7 +536,7 @@ describe('Execute: Handles execution of abstract types', () => { types: [CatType, DogType], }); - const query = ` + const document = parse(` { pets { name @@ -550,11 +548,9 @@ describe('Execute: Handles execution of abstract types', () => { } } } - `; + `); - const result = graphqlSync({ schema, source: query }); - - expect(result).to.deep.equal({ + expect(executeSync({ schema, document })).to.deep.equal({ data: { pets: [ { diff --git a/src/execution/__tests__/directives-test.js b/src/execution/__tests__/directives-test.js index 1f1f148d8b..52251a0fcc 100644 --- a/src/execution/__tests__/directives-test.js +++ b/src/execution/__tests__/directives-test.js @@ -9,7 +9,8 @@ import { GraphQLSchema } from '../../type/schema'; import { GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; -import { execute } from '../execute'; +import type { ExecutionResult } from '../execute'; +import { executeSync } from '../execute'; const schema = new GraphQLSchema({ query: new GraphQLObjectType({ @@ -30,9 +31,9 @@ const rootValue = { }, }; -function executeTestQuery(query: string) { +function executeTestQuery(query: string): ExecutionResult { const document = parse(query); - return execute({ schema, document, rootValue }); + return executeSync({ schema, document, rootValue }); } describe('Execute: handles directives', () => { diff --git a/src/execution/__tests__/executor-test.js b/src/execution/__tests__/executor-test.js index c6684372e7..e37c561ec9 100644 --- a/src/execution/__tests__/executor-test.js +++ b/src/execution/__tests__/executor-test.js @@ -19,7 +19,7 @@ import { GraphQLObjectType, } from '../../type/definition'; -import { execute } from '../execute'; +import { execute, executeSync } from '../execute'; describe('Execute: Handles basic execution tasks', () => { it('throws if no document is provided', () => { @@ -33,14 +33,14 @@ describe('Execute: Handles basic execution tasks', () => { }); // $FlowExpectedError - expect(() => execute({ schema })).to.throw('Must provide document.'); + expect(() => executeSync({ schema })).to.throw('Must provide document.'); }); it('throws if no schema is provided', () => { const document = parse('{ field }'); // $FlowExpectedError - expect(() => execute({ document })).to.throw( + expect(() => executeSync({ document })).to.throw( 'Expected undefined to be a GraphQL schema.', ); }); @@ -65,7 +65,7 @@ describe('Execute: Handles basic execution tasks', () => { const variableValues = '{ "a": 1 }'; // $FlowExpectedError - expect(() => execute({ schema, document, variableValues })).to.throw( + expect(() => executeSync({ schema, document, variableValues })).to.throw( 'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.', ); }); @@ -239,7 +239,7 @@ describe('Execute: Handles basic execution tasks', () => { } `); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: { a: 'Apple', @@ -276,7 +276,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { root: 'val' }; const variableValues = { var: 'abc' }; - execute({ schema, document, rootValue, variableValues }); + executeSync({ schema, document, rootValue, variableValues }); expect(resolvedInfo).to.have.all.keys( 'fieldName', @@ -330,7 +330,7 @@ describe('Execute: Handles basic execution tasks', () => { const document = parse('query Example { a }'); const rootValue = { contextThing: 'thing' }; - execute({ schema, document, rootValue }); + executeSync({ schema, document, rootValue }); expect(resolvedRootValue).to.equal(rootValue); }); @@ -360,7 +360,7 @@ describe('Execute: Handles basic execution tasks', () => { } `); - execute({ schema, document }); + executeSync({ schema, document }); expect(resolvedArgs).to.deep.equal({ numArg: 123, stringArg: 'foo' }); }); @@ -638,7 +638,7 @@ describe('Execute: Handles basic execution tasks', () => { } `); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: { nullableA: { @@ -667,7 +667,7 @@ describe('Execute: Handles basic execution tasks', () => { const document = parse('{ a }'); const rootValue = { a: 'b' }; - const result = execute({ schema, document, rootValue }); + const result = executeSync({ schema, document, rootValue }); expect(result).to.deep.equal({ data: { a: 'b' } }); }); @@ -683,7 +683,7 @@ describe('Execute: Handles basic execution tasks', () => { const document = parse('query Example { a }'); const rootValue = { a: 'b' }; - const result = execute({ schema, document, rootValue }); + const result = executeSync({ schema, document, rootValue }); expect(result).to.deep.equal({ data: { a: 'b' } }); }); @@ -704,7 +704,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { a: 'b' }; const operationName = 'OtherExample'; - const result = execute({ schema, document, rootValue, operationName }); + const result = executeSync({ schema, document, rootValue, operationName }); expect(result).to.deep.equal({ data: { second: 'b' } }); }); @@ -720,7 +720,7 @@ describe('Execute: Handles basic execution tasks', () => { const document = parse('fragment Example on Type { a }'); const rootValue = { a: 'b' }; - const result = execute({ schema, document, rootValue }); + const result = executeSync({ schema, document, rootValue }); expect(result).to.deep.equal({ errors: [{ message: 'Must provide an operation.' }], }); @@ -740,7 +740,7 @@ describe('Execute: Handles basic execution tasks', () => { query OtherExample { a } `); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ errors: [ { @@ -766,7 +766,7 @@ describe('Execute: Handles basic execution tasks', () => { `); const operationName = 'UnknownExample'; - const result = execute({ schema, document, operationName }); + const result = executeSync({ schema, document, operationName }); expect(result).to.deep.equal({ errors: [{ message: 'Unknown operation named "UnknownExample".' }], }); @@ -784,7 +784,7 @@ describe('Execute: Handles basic execution tasks', () => { const document = parse('{ a }'); const operationName = ''; - const result = execute({ schema, document, operationName }); + const result = executeSync({ schema, document, operationName }); expect(result).to.deep.equal({ errors: [{ message: 'Unknown operation named "".' }], }); @@ -819,7 +819,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { a: 'b', c: 'd' }; const operationName = 'Q'; - const result = execute({ schema, document, rootValue, operationName }); + const result = executeSync({ schema, document, rootValue, operationName }); expect(result).to.deep.equal({ data: { a: 'b' } }); }); @@ -845,7 +845,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { a: 'b', c: 'd' }; const operationName = 'M'; - const result = execute({ schema, document, rootValue, operationName }); + const result = executeSync({ schema, document, rootValue, operationName }); expect(result).to.deep.equal({ data: { c: 'd' } }); }); @@ -871,7 +871,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { a: 'b', c: 'd' }; const operationName = 'S'; - const result = execute({ schema, document, rootValue, operationName }); + const result = executeSync({ schema, document, rootValue, operationName }); expect(result).to.deep.equal({ data: { a: 'b' } }); }); @@ -926,7 +926,7 @@ describe('Execute: Handles basic execution tasks', () => { `); const rootValue = { a: 'b' }; - const result = execute({ schema, document, rootValue }); + const result = executeSync({ schema, document, rootValue }); expect(result).to.deep.equal({ data: { a: 'b' }, }); @@ -950,7 +950,7 @@ describe('Execute: Handles basic execution tasks', () => { const document = parse('{ a }'); const rootValue = { a: { b: 'c' } }; - const result = execute({ schema, document, rootValue }); + const result = executeSync({ schema, document, rootValue }); expect(result).to.deep.equal({ data: { a: {} }, }); @@ -967,7 +967,7 @@ describe('Execute: Handles basic execution tasks', () => { }); const document = parse('{ thisIsIllegalDoNotIncludeMe }'); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: {}, }); @@ -994,7 +994,7 @@ describe('Execute: Handles basic execution tasks', () => { }); const document = parse('{ field(a: true, c: false, e: 0) }'); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: { field: '{ a: true, c: false, e: 0 }', @@ -1042,7 +1042,7 @@ describe('Execute: Handles basic execution tasks', () => { specials: [new Special('foo'), new NotSpecial('bar')], }; - const result = execute({ schema, document, rootValue }); + const result = executeSync({ schema, document, rootValue }); expect(result).to.deep.equal({ data: { specials: [{ value: 'foo' }, null], @@ -1086,7 +1086,7 @@ describe('Execute: Handles basic execution tasks', () => { }), }); - const result = execute({ schema, document: parse('{ customScalar }') }); + const result = executeSync({ schema, document: parse('{ customScalar }') }); expect(result).to.deep.equal({ data: { customScalar: null }, errors: [ @@ -1116,7 +1116,7 @@ describe('Execute: Handles basic execution tasks', () => { type Query { bar: String } `); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: { foo: null } }); }); @@ -1131,7 +1131,7 @@ describe('Execute: Handles basic execution tasks', () => { }); const document = parse('{ foo }'); - const result = execute({ + const result = executeSync({ schema, document, fieldResolver(_source, _args, _context, info) { @@ -1174,7 +1174,7 @@ describe('Execute: Handles basic execution tasks', () => { const rootValue = { foo: { bar: 'bar' } }; let possibleTypes; - const result = execute({ + const result = executeSync({ schema, document, rootValue, diff --git a/src/execution/__tests__/mutations-test.js b/src/execution/__tests__/mutations-test.js index 63b2411394..c134bb16ed 100644 --- a/src/execution/__tests__/mutations-test.js +++ b/src/execution/__tests__/mutations-test.js @@ -9,7 +9,7 @@ import { GraphQLInt } from '../../type/scalars'; import { GraphQLSchema } from '../../type/schema'; import { GraphQLObjectType } from '../../type/definition'; -import { execute } from '../execute'; +import { execute, executeSync } from '../execute'; class NumberHolder { theNumber: number; @@ -140,7 +140,7 @@ describe('Execute: Handles mutation execution ordering', () => { it('does not include illegal mutation fields in output', () => { const document = parse('mutation { thisIsIllegalDoNotIncludeMe }'); - const result = execute({ schema, document }); + const result = executeSync({ schema, document }); expect(result).to.deep.equal({ data: {}, }); diff --git a/src/execution/__tests__/nonnull-test.js b/src/execution/__tests__/nonnull-test.js index 41f5ec34b7..19233016a8 100644 --- a/src/execution/__tests__/nonnull-test.js +++ b/src/execution/__tests__/nonnull-test.js @@ -3,8 +3,6 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; -import invariant from '../../jsutils/invariant'; - import { parse } from '../../language/parser'; import { GraphQLSchema } from '../../type/schema'; @@ -14,7 +12,7 @@ import { GraphQLNonNull, GraphQLObjectType } from '../../type/definition'; import { buildSchema } from '../../utilities/buildASTSchema'; import type { ExecutionResult } from '../execute'; -import { execute } from '../execute'; +import { execute, executeSync } from '../execute'; const syncError = new Error('sync'); const syncNonNullError = new Error('syncNonNull'); @@ -127,10 +125,12 @@ function patchData(data: ExecutionResult): ExecutionResult { } async function executeSyncAndAsync(query: string, rootValue: mixed) { - const syncResult = await executeQuery(query, rootValue); - invariant(!(syncResult instanceof Promise)); - - const asyncResult = await executeQuery(patch(query), rootValue); + const syncResult = executeSync({ schema, document: parse(query), rootValue }); + const asyncResult = await execute({ + schema, + document: parse(patch(query)), + rootValue, + }); expect(asyncResult).to.deep.equal(patchData(syncResult)); return syncResult; @@ -166,7 +166,7 @@ describe('Execute: handles non-nullable types', () => { }); }); - describe('nulls a synchronously returned object that contains a non-nullable field', () => { + describe('nulls a returned object that contains a non-nullable field', () => { const query = ` { syncNest { @@ -205,45 +205,6 @@ describe('Execute: handles non-nullable types', () => { }); }); - describe('nulls an object returned in a promise that contains a non-nullable field', () => { - const query = ` - { - promiseNest { - syncNonNull, - } - } - `; - - it('that returns null', async () => { - const result = await executeSyncAndAsync(query, nullingData); - expect(result).to.deep.equal({ - data: { promiseNest: null }, - errors: [ - { - message: - 'Cannot return null for non-nullable field DataType.syncNonNull.', - path: ['promiseNest', 'syncNonNull'], - locations: [{ line: 4, column: 11 }], - }, - ], - }); - }); - - it('that throws', async () => { - const result = await executeSyncAndAsync(query, throwingData); - expect(result).to.deep.equal({ - data: { promiseNest: null }, - errors: [ - { - message: syncNonNullError.message, - path: ['promiseNest', 'syncNonNull'], - locations: [{ line: 4, column: 11 }], - }, - ], - }); - }); - }); - describe('nulls a complex tree of nullable fields, each', () => { const query = ` { @@ -582,7 +543,7 @@ describe('Execute: handles non-nullable types', () => { }); it('succeeds when passed non-null literal value', () => { - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query { @@ -599,7 +560,7 @@ describe('Execute: handles non-nullable types', () => { }); it('succeeds when passed non-null variable value', () => { - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query ($testVar: String!) { @@ -619,7 +580,7 @@ describe('Execute: handles non-nullable types', () => { }); it('succeeds when missing variable has default value', () => { - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query ($testVar: String = "default value") { @@ -641,7 +602,7 @@ describe('Execute: handles non-nullable types', () => { it('field error when missing non-null arg', () => { // Note: validation should identify this issue first (missing args rule) // however execution should still protect against this. - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query { @@ -668,7 +629,7 @@ describe('Execute: handles non-nullable types', () => { it('field error when non-null arg provided null', () => { // Note: validation should identify this issue first (values of correct // type rule) however execution should still protect against this. - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query { @@ -695,7 +656,7 @@ describe('Execute: handles non-nullable types', () => { it('field error when non-null arg not provided variable value', () => { // Note: validation should identify this issue first (variables in allowed // position rule) however execution should still protect against this. - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query ($testVar: String) { @@ -723,7 +684,7 @@ describe('Execute: handles non-nullable types', () => { }); it('field error when non-null arg provided variable with explicit null value', () => { - const result = execute({ + const result = executeSync({ schema: schemaWithNonNullArg, document: parse(` query ($testVar: String = "default value") { diff --git a/src/execution/__tests__/resolve-test.js b/src/execution/__tests__/resolve-test.js index c7d7b01a6e..22b37f7214 100644 --- a/src/execution/__tests__/resolve-test.js +++ b/src/execution/__tests__/resolve-test.js @@ -3,12 +3,15 @@ import { expect } from 'chai'; import { describe, it } from 'mocha'; +import { parse } from '../../language/parser'; + import type { GraphQLFieldConfig } from '../../type/definition'; import { GraphQLSchema } from '../../type/schema'; import { GraphQLInt, GraphQLString } from '../../type/scalars'; import { GraphQLObjectType } from '../../type/definition'; -import { graphqlSync } from '../../graphql'; +import type { ExecutionResult } from '../execute'; +import { executeSync } from '../execute'; describe('Execute: resolve function', () => { function testSchema(testField: GraphQLFieldConfig) { @@ -23,9 +26,9 @@ describe('Execute: resolve function', () => { } it('default function accesses properties', () => { - const result = graphqlSync({ + const result = executeSync({ schema: testSchema({ type: GraphQLString }), - source: '{ test }', + document: parse('{ test }'), rootValue: { test: 'testValue' }, }); @@ -44,9 +47,9 @@ describe('Execute: resolve function', () => { }, }; - const result = graphqlSync({ + const result = executeSync({ schema: testSchema({ type: GraphQLString }), - source: '{ test }', + document: parse('{ test }'), rootValue, }); expect(result).to.deep.equal({ @@ -77,9 +80,9 @@ describe('Execute: resolve function', () => { }, }); const contextValue = { addend2: 9 }; - const source = '{ test(addend1: 80) }'; + const document = parse('{ test(addend1: 80) }'); - const result = graphqlSync({ schema, source, rootValue, contextValue }); + const result = executeSync({ schema, document, rootValue, contextValue }); expect(result).to.deep.equal({ data: { test: 789 }, }); @@ -95,30 +98,31 @@ describe('Execute: resolve function', () => { resolve: (source, args) => JSON.stringify([source, args]), }); - function execute(source: string, rootValue?: mixed, contextValue?: mixed) { - return graphqlSync({ schema, source, rootValue, contextValue }); + function executeQuery(query: string, rootValue?: mixed): ExecutionResult { + const document = parse(query); + return executeSync({ schema, document, rootValue }); } - expect(execute('{ test }')).to.deep.equal({ + expect(executeQuery('{ test }')).to.deep.equal({ data: { test: '[null,{}]', }, }); - expect(execute('{ test }', 'Source!')).to.deep.equal({ + expect(executeQuery('{ test }', 'Source!')).to.deep.equal({ data: { test: '["Source!",{}]', }, }); - expect(execute('{ test(aStr: "String!") }', 'Source!')).to.deep.equal({ + expect(executeQuery('{ test(aStr: "String!") }', 'Source!')).to.deep.equal({ data: { test: '["Source!",{"aStr":"String!"}]', }, }); expect( - execute('{ test(aInt: -123, aStr: "String!") }', 'Source!'), + executeQuery('{ test(aInt: -123, aStr: "String!") }', 'Source!'), ).to.deep.equal({ data: { test: '["Source!",{"aStr":"String!","aInt":-123}]', diff --git a/src/execution/__tests__/schema-test.js b/src/execution/__tests__/schema-test.js index 866e6b4299..d625e33b53 100644 --- a/src/execution/__tests__/schema-test.js +++ b/src/execution/__tests__/schema-test.js @@ -18,7 +18,7 @@ import { GraphQLBoolean, } from '../../type/scalars'; -import { execute } from '../execute'; +import { executeSync } from '../execute'; describe('Execute: Handles execution with a complex schema', () => { it('executes using a schema', () => { @@ -148,7 +148,7 @@ describe('Execute: Handles execution with a complex schema', () => { // Note: this is intentionally not validating to ensure appropriate // behavior occurs when executing an invalid query. - expect(execute({ schema: BlogSchema, document })).to.deep.equal({ + expect(executeSync({ schema: BlogSchema, document })).to.deep.equal({ data: { feed: [ { id: '1', title: 'My Article 1' }, diff --git a/src/execution/__tests__/sync-test.js b/src/execution/__tests__/sync-test.js index 4c12f1fb24..4610246c0a 100644 --- a/src/execution/__tests__/sync-test.js +++ b/src/execution/__tests__/sync-test.js @@ -13,7 +13,7 @@ import { GraphQLObjectType } from '../../type/definition'; import { graphqlSync } from '../../graphql'; -import { execute } from '../execute'; +import { execute, executeSync } from '../execute'; describe('Execute: synchronously when possible', () => { const schema = new GraphQLSchema({ @@ -92,6 +92,29 @@ describe('Execute: synchronously when possible', () => { }); }); + describe('executeSync', () => { + it('does not return a Promise for sync execution', () => { + const doc = 'query Example { syncField }'; + const result = executeSync({ + schema, + document: parse(doc), + rootValue: 'rootValue', + }); + expect(result).to.deep.equal({ data: { syncField: 'rootValue' } }); + }); + + it('throws if encountering async execution', () => { + const doc = 'query Example { syncField, asyncField }'; + expect(() => { + executeSync({ + schema, + document: parse(doc), + rootValue: 'rootValue', + }); + }).to.throw('GraphQL execution failed to complete synchronously.'); + }); + }); + describe('graphqlSync', () => { it('report errors raised during schema validation', () => { const badSchema = new GraphQLSchema({}); diff --git a/src/execution/__tests__/union-interface-test.js b/src/execution/__tests__/union-interface-test.js index 3c664e8875..3ba4d222fb 100644 --- a/src/execution/__tests__/union-interface-test.js +++ b/src/execution/__tests__/union-interface-test.js @@ -16,7 +16,7 @@ import { GraphQLUnionType, } from '../../type/definition'; -import { execute } from '../execute'; +import { executeSync } from '../execute'; class Dog { name: string; @@ -193,7 +193,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document })).to.deep.equal({ + expect(executeSync({ schema, document })).to.deep.equal({ data: { Named: { kind: 'INTERFACE', @@ -241,7 +241,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document, rootValue: john })).to.deep.equal({ + expect(executeSync({ schema, document, rootValue: john })).to.deep.equal({ data: { __typename: 'Person', name: 'John', @@ -281,7 +281,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document, rootValue: john })).to.deep.equal({ + expect(executeSync({ schema, document, rootValue: john })).to.deep.equal({ data: { __typename: 'Person', name: 'John', @@ -316,7 +316,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document, rootValue: john })).to.deep.equal({ + expect(executeSync({ schema, document, rootValue: john })).to.deep.equal({ data: { __typename: 'Person', name: 'John', @@ -361,7 +361,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document, rootValue: john })).to.deep.equal({ + expect(executeSync({ schema, document, rootValue: john })).to.deep.equal({ data: { __typename: 'Person', name: 'John', @@ -404,7 +404,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document, rootValue: john })).to.deep.equal({ + expect(executeSync({ schema, document, rootValue: john })).to.deep.equal({ data: { __typename: 'Person', name: 'John', @@ -469,7 +469,7 @@ describe('Execute: Union and intersection types', () => { } `); - expect(execute({ schema, document, rootValue: john })).to.deep.equal({ + expect(executeSync({ schema, document, rootValue: john })).to.deep.equal({ data: { __typename: 'Person', name: 'John', @@ -533,7 +533,7 @@ describe('Execute: Union and intersection types', () => { const rootValue = new Person('John', [], [liz]); const contextValue = { authToken: '123abc' }; - const result = execute({ + const result = executeSync({ schema: schema2, document, rootValue, diff --git a/src/execution/__tests__/variables-test.js b/src/execution/__tests__/variables-test.js index f0ce6253a5..2e88257fb7 100644 --- a/src/execution/__tests__/variables-test.js +++ b/src/execution/__tests__/variables-test.js @@ -20,7 +20,7 @@ import { GraphQLEnumType, } from '../../type/definition'; -import { execute } from '../execute'; +import { executeSync } from '../execute'; import { getVariableValues } from '../values'; const TestComplexScalar = new GraphQLScalarType({ @@ -118,7 +118,7 @@ const schema = new GraphQLSchema({ query: TestType }); function executeQuery(query, variableValues) { const document = parse(query); - return execute({ schema, document, variableValues }); + return executeSync({ schema, document, variableValues }); } describe('Execute: Handles inputs', () => { diff --git a/src/execution/execute.d.ts b/src/execution/execute.d.ts index 4b16654cbd..a879b538d9 100644 --- a/src/execution/execute.d.ts +++ b/src/execution/execute.d.ts @@ -90,6 +90,13 @@ export function execute( typeResolver?: Maybe>, ): PromiseOrValue; +/** + * Also implements the "Evaluating requests" section of the GraphQL specification. + * However, it guarantees to complete synchronously (or throw an error) assuming + * that all field resolvers are also synchronous. + */ +export function executeSync(args: ExecutionArgs): ExecutionResult; + /** * Essential assertions before executing to provide developer feedback for * improper use of the GraphQL library. diff --git a/src/execution/execute.js b/src/execution/execute.js index 550dfeab4a..33f2892a93 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -184,6 +184,22 @@ export function execute( }); } +/** + * Also implements the "Evaluating requests" section of the GraphQL specification. + * However, it guarantees to complete synchronously (or throw an error) assuming + * that all field resolvers are also synchronous. + */ +export function executeSync(args: ExecutionArgs): ExecutionResult { + const result = executeImpl(args); + + // Assert that the execution was synchronous. + if (isPromise(result)) { + throw new Error('GraphQL execution failed to complete synchronously.'); + } + + return result; +} + function executeImpl(args: ExecutionArgs): PromiseOrValue { const { schema, diff --git a/src/execution/index.d.ts b/src/execution/index.d.ts index ed0f8f1808..043e061982 100644 --- a/src/execution/index.d.ts +++ b/src/execution/index.d.ts @@ -2,6 +2,7 @@ export { pathToArray as responsePathAsArray } from '../jsutils/Path'; export { execute, + executeSync, defaultFieldResolver, defaultTypeResolver, ExecutionArgs, diff --git a/src/execution/index.js b/src/execution/index.js index c7ffa5cdef..cccb9f0a73 100644 --- a/src/execution/index.js +++ b/src/execution/index.js @@ -2,7 +2,12 @@ export { pathToArray as responsePathAsArray } from '../jsutils/Path'; -export { execute, defaultFieldResolver, defaultTypeResolver } from './execute'; +export { + execute, + executeSync, + defaultFieldResolver, + defaultTypeResolver, +} from './execute'; export type { ExecutionArgs, ExecutionResult } from './execute'; export { getDirectiveValues } from './values'; diff --git a/src/index.d.ts b/src/index.d.ts index af81042c68..5c14390edd 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -280,6 +280,7 @@ export { // Execute GraphQL queries. export { execute, + executeSync, defaultFieldResolver, defaultTypeResolver, responsePathAsArray, diff --git a/src/index.js b/src/index.js index 432e16aefe..ff46b68ad9 100644 --- a/src/index.js +++ b/src/index.js @@ -281,6 +281,7 @@ export type { // Execute GraphQL queries. export { execute, + executeSync, defaultFieldResolver, defaultTypeResolver, responsePathAsArray, diff --git a/src/utilities/__tests__/introspectionFromSchema-benchmark.js b/src/utilities/__tests__/introspectionFromSchema-benchmark.js index 87a48f52f8..f27599c6a1 100644 --- a/src/utilities/__tests__/introspectionFromSchema-benchmark.js +++ b/src/utilities/__tests__/introspectionFromSchema-benchmark.js @@ -1,7 +1,7 @@ // @flow strict import { parse } from '../../language/parser'; -import { execute } from '../../execution/execute'; +import { executeSync } from '../../execution/execute'; import { buildSchema } from '../buildASTSchema'; import { getIntrospectionQuery } from '../getIntrospectionQuery'; @@ -14,5 +14,5 @@ const document = parse(getIntrospectionQuery()); export const name = 'Execute Introspection Query'; export const count = 10; export function measure() { - execute({ schema, document }); + executeSync({ schema, document }); } diff --git a/src/utilities/introspectionFromSchema.js b/src/utilities/introspectionFromSchema.js index 6cfb645aa7..fee1281650 100644 --- a/src/utilities/introspectionFromSchema.js +++ b/src/utilities/introspectionFromSchema.js @@ -1,13 +1,12 @@ // @flow strict import invariant from '../jsutils/invariant'; -import isPromise from '../jsutils/isPromise'; import { parse } from '../language/parser'; import type { GraphQLSchema } from '../type/schema'; -import { execute } from '../execution/execute'; +import { executeSync } from '../execution/execute'; import type { IntrospectionQuery, @@ -35,7 +34,7 @@ export function introspectionFromSchema( }; const document = parse(getIntrospectionQuery(optionsWithDefaults)); - const result = execute({ schema, document }); - invariant(!isPromise(result) && !result.errors && result.data); + const result = executeSync({ schema, document }); + invariant(!result.errors && result.data); return (result.data: any); }