From c82cdec9b5cc52e9f62081e25fc6d89c52f26cec Mon Sep 17 00:00:00 2001 From: Jesse Rosenberger Date: Tue, 13 Nov 2018 12:11:56 +0200 Subject: [PATCH] Throw an error on asynchronous introspection query behavior. (#1955) We expect introspection queries to behave in an synchronous manner since they do not have any resolvers which return Promises. This expectation seems to also be had by `graphql-js` which utilizes `graphqlSync`, rather than `graphql` for execution of introspection queries. In fact, this may be one of the entire reasons that `graphqlSync` exists: to fulfill a contract for synchronous execution of server introspection. The introspection tests within `graphql-js` seem to support this theory[[0]]. Utilities which wrap GraphQL resolvers should take care to maintain the execution dynamics of what they are wrapping, or they should avoid wrapping introspection types entirely by checking the type with the `isIntrospectionType` predicate function from `graphql/type`[[1]]. [0]: https://github.com/graphql/graphql-js/blob/787422956c9554d12d063a41fe35705335ec6290/src/type/__tests__/introspection-test.js [1]: https://github.com/graphql/graphql-js/blob/74d1e941/src/type/introspection.js#L484. Closes: https://github.com/apollographql/apollo-server/issues/1935 --- packages/apollo-server-core/src/utils/schemaHash.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/apollo-server-core/src/utils/schemaHash.ts b/packages/apollo-server-core/src/utils/schemaHash.ts index e1538fb8067..4ed206951ed 100644 --- a/packages/apollo-server-core/src/utils/schemaHash.ts +++ b/packages/apollo-server-core/src/utils/schemaHash.ts @@ -5,11 +5,23 @@ import stableStringify from 'json-stable-stringify'; import { GraphQLSchema } from 'graphql/type'; import { createHash } from 'crypto'; +const hasOwn = Object.prototype.hasOwnProperty; + export function generateSchemaHash(schema: GraphQLSchema): string { const introspectionQuery = getIntrospectionQuery(); const documentAST = parse(introspectionQuery); const result = execute(schema, documentAST) as ExecutionResult; + // If the execution of an introspection query results in a then-able, it + // indicates that one or more of its resolvers is behaving in an asynchronous + // manner. This is not the expected behavior of a introspection query + // which does not have any asynchronous resolvers. + if (hasOwn.call(result, 'then')) { + throw new Error( + 'The introspection query is resolving asynchronously; execution of an introspection query is not expected to return a `Promise`.', + ); + } + if (!result || !result.data || !result.data.__schema) { throw new Error('Unable to generate server introspection document.'); }