UNPKG

@subsquid/apollo-server-core

Version:
62 lines (55 loc) 2.96 kB
import { parse } from 'graphql/language'; import { execute, ExecutionResult } from 'graphql/execution'; import { getIntrospectionQuery, IntrospectionQuery } from 'graphql/utilities'; import stableStringify from 'fast-json-stable-stringify'; import type { GraphQLSchema } from 'graphql/type'; import createSHA from './createSHA'; import type { SchemaHash } from 'apollo-server-types'; /* * This function returns a not particularly stable schema hash derived from a * GraphQLSchema object. It works by running the `graphql-js` default * introspection query against the schema and taking a SHA of a JSON encoding of * the result. It is dependent on the precise introspection query returned by * `graphql-js` and some of the details of how that library returns its data, so * upgrading `graphql-js` can change its value. It was created for use in * apollo-server-plugin-operation-registry (now @apollo/server-plugin-operation-registry) * but it is no longer used there. It is *not* the same as the hash used in * schema and usage reporting, which is just a hash of the schema SDL document. * * For backwards-compatibility reasons, it is still calculated and passed to all * plugin hooks, but it is not a good idea to use it for anything. */ export function generateSchemaHash(schema: GraphQLSchema): SchemaHash { const introspectionQuery = getIntrospectionQuery(); const document = parse(introspectionQuery); const result = execute({ schema, document, }) as ExecutionResult<IntrospectionQuery>; // 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 ( result && typeof (result as PromiseLike<typeof result>).then === 'function' ) { throw new Error( [ 'The introspection query is resolving asynchronously; execution of an introspection query is not expected to return a `Promise`.', '', 'Wrapped type resolvers should maintain the existing execution dynamics of the resolvers they wrap (i.e. async vs sync) or introspection types should be excluded from wrapping by checking them with `graphql/type`s, `isIntrospectionType` predicate function prior to wrapping.', ].join('\n'), ); } if (!result || !result.data || !result.data.__schema) { throw new Error('Unable to generate server introspection document.'); } const introspectionSchema = result.data.__schema; // It's important that we perform a deterministic stringification here // since, depending on changes in the underlying `graphql-js` execution // layer, varying orders of the properties in the introspection const stringifiedSchema = stableStringify(introspectionSchema); return createSHA('sha512') .update(stringifiedSchema) .digest('hex') as SchemaHash; }