Generate schemaHash from schema

This commit adds a new field to the GraphQLServiceContext which is
a hashed representation of the schema. This is useful as a key value
to represent the schema and is sent as part of reporting to engine
when enabled
This commit is contained in:
James Baxley 2018-10-25 11:46:51 -04:00 committed by Jesse Rosenberger
parent 40d21f7d6a
commit d10f961646
4 changed files with 40 additions and 0 deletions

View file

@ -38,11 +38,15 @@
"graphql-subscriptions": "^1.0.0",
"graphql-tag": "^2.9.2",
"graphql-tools": "^4.0.0",
"json-stable-stringify": "^1.0.1",
"lodash": "^4.17.10",
"subscriptions-transport-ws": "^0.9.11",
"ws": "^6.0.0"
},
"peerDependencies": {
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0"
},
"devDependencies": {
"@types/json-stable-stringify": "^1.0.32"
}
}

View file

@ -44,6 +44,8 @@ import {
PlaygroundRenderPageOptions,
} from './playground';
import { generateSchemaHash } from './utils/schemaHash';
const NoIntrospection = (context: ValidationContext) => ({
Field(node: FieldDefinitionNode) {
if (node.name.value === '__schema' || node.name.value === '__type') {
@ -87,6 +89,7 @@ export class ApolloServerBase {
private engineReportingAgent?: EngineReportingAgent;
private engineServiceId?: string;
private extensions: Array<() => GraphQLExtension>;
private schemaHash: string;
protected plugins: ApolloServerPlugin[] = [];
protected schema: GraphQLSchema;
@ -266,6 +269,10 @@ export class ApolloServerBase {
});
}
// The schema hash is a string representation of the shape of the schema
// it is used for reporting and can be used for a cache key if needed
this.schemaHash = generateSchemaHash(this.schema);
// Note: doRunQuery will add its own extensions if you set tracing,
// or cacheControl.
this.extensions = [];
@ -341,6 +348,7 @@ export class ApolloServerBase {
plugin.serverWillStart &&
plugin.serverWillStart({
schema: this.schema,
schemaHash: this.schemaHash,
engine: {
serviceID: this.engineServiceId,
},

View file

@ -15,6 +15,7 @@ import { KeyValueCache } from 'apollo-server-caching';
export interface GraphQLServiceContext {
schema: GraphQLSchema;
schemaHash: string;
engine: {
serviceID?: string;
};

View file

@ -0,0 +1,27 @@
import { parse } from 'graphql/language';
import { execute, ExecutionResult } from 'graphql/execution';
import { getIntrospectionQuery, IntrospectionSchema } from 'graphql/utilities';
import stableStringify from 'json-stable-stringify';
import { GraphQLSchema } from 'graphql/type';
import { createHash } from 'crypto';
export function generateSchemaHash(schema: GraphQLSchema): string {
const introspectionQuery = getIntrospectionQuery();
const documentAST = parse(introspectionQuery);
const result = execute(schema, documentAST) as ExecutionResult;
if (!result || !result.data || !result.data.__schema) {
throw new Error('Unable to generate server introspection document.');
}
const introspectionSchema: 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 createHash('sha512')
.update(stringifiedSchema)
.digest('hex');
}