Skip to content

Commit 2c58b26

Browse files
IvanGoncharovleebyron
authored andcommitted
Make 'ASTDefinitionBuilder' responsible only for build types from AST (#1230)
* Make 'ASTDefinitionBuilder' responsible only for build types from AST * Address review comments. * Simplify extendType() * Fix flow errors
1 parent 65afbaf commit 2c58b26

File tree

2 files changed

+58
-64
lines changed

2 files changed

+58
-64
lines changed

src/utilities/buildASTSchema.js

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -170,22 +170,20 @@ export function buildASTSchema(
170170
const operationTypes = schemaDef
171171
? getOperationTypes(schemaDef)
172172
: {
173-
query: nodeMap.Query ? 'Query' : null,
174-
mutation: nodeMap.Mutation ? 'Mutation' : null,
175-
subscription: nodeMap.Subscription ? 'Subscription' : null,
173+
query: nodeMap.Query,
174+
mutation: nodeMap.Mutation,
175+
subscription: nodeMap.Subscription,
176176
};
177177

178178
const definitionBuilder = new ASTDefinitionBuilder(
179179
nodeMap,
180180
options,
181-
typeName => {
182-
throw new Error(`Type "${typeName}" not found in document.`);
181+
typeRef => {
182+
throw new Error(`Type "${typeRef.name.value}" not found in document.`);
183183
},
184184
);
185185

186-
const types = typeDefs.map(def =>
187-
definitionBuilder.buildType(def.name.value),
188-
);
186+
const types = typeDefs.map(def => definitionBuilder.buildType(def));
189187

190188
const directives = directiveDefs.map(def =>
191189
definitionBuilder.buildDirective(def),
@@ -237,17 +235,14 @@ export function buildASTSchema(
237235
`Specified ${operation} type "${typeName}" not found in document.`,
238236
);
239237
}
240-
opTypes[operation] = typeName;
238+
opTypes[operation] = operationType.type;
241239
});
242240
return opTypes;
243241
}
244242
}
245243

246244
type TypeDefinitionsMap = ObjMap<TypeDefinitionNode>;
247-
type TypeResolver = (
248-
typeName: string,
249-
node?: ?NamedTypeNode,
250-
) => GraphQLNamedType;
245+
type TypeResolver = (typeRef: NamedTypeNode) => GraphQLNamedType;
251246

252247
export class ASTDefinitionBuilder {
253248
_typeDefinitionsMap: TypeDefinitionsMap;
@@ -270,25 +265,21 @@ export class ASTDefinitionBuilder {
270265
);
271266
}
272267

273-
_buildType(typeName: string, typeNode?: ?NamedTypeNode): GraphQLNamedType {
268+
buildType(node: NamedTypeNode | TypeDefinitionNode): GraphQLNamedType {
269+
const typeName = node.name.value;
274270
if (!this._cache[typeName]) {
275-
const defNode = this._typeDefinitionsMap[typeName];
276-
if (defNode) {
277-
this._cache[typeName] = this._makeSchemaDef(defNode);
271+
if (node.kind === Kind.NAMED_TYPE) {
272+
const defNode = this._typeDefinitionsMap[typeName];
273+
this._cache[typeName] = defNode
274+
? this._makeSchemaDef(defNode)
275+
: this._resolveType(node);
278276
} else {
279-
this._cache[typeName] = this._resolveType(typeName, typeNode);
277+
this._cache[typeName] = this._makeSchemaDef(node);
280278
}
281279
}
282280
return this._cache[typeName];
283281
}
284282

285-
buildType(ref: string | NamedTypeNode): GraphQLNamedType {
286-
if (typeof ref === 'string') {
287-
return this._buildType(ref);
288-
}
289-
return this._buildType(ref.name.value, ref);
290-
}
291-
292283
_buildWrappedType(typeNode: TypeNode): GraphQLType {
293284
const typeDef = this.buildType(getNamedTypeNode(typeNode));
294285
return buildWrappedType(typeDef, typeNode);

src/utilities/extendSchema.js

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99

1010
import invariant from '../jsutils/invariant';
1111
import keyMap from '../jsutils/keyMap';
12+
import objectValues from '../jsutils/objectValues';
1213
import { ASTDefinitionBuilder } from './buildASTSchema';
1314
import { GraphQLError } from '../error/GraphQLError';
1415
import { isSchema, GraphQLSchema } from '../type/schema';
16+
import { isIntrospectionType } from '../type/introspection';
1517

1618
import type { GraphQLSchemaValidationOptions } from '../type/schema';
1719

@@ -189,56 +191,52 @@ export function extendSchema(
189191
return schema;
190192
}
191193

192-
const definitionBuilder = new ASTDefinitionBuilder(
194+
const astBuilder = new ASTDefinitionBuilder(
193195
typeDefinitionMap,
194196
options,
195-
(typeName, node) => {
197+
typeRef => {
198+
const typeName = typeRef.name.value;
196199
const existingType = schema.getType(typeName);
197200
if (existingType) {
198-
return extendType(existingType);
201+
return getExtendedType(existingType);
199202
}
200203

201-
if (node) {
202-
throw new GraphQLError(
203-
`Unknown type: "${typeName}". Ensure that this type exists ` +
204-
'either in the original schema, or is added in a type definition.',
205-
[node],
206-
);
207-
}
208-
throw GraphQLError('Missing type from schema');
204+
throw new GraphQLError(
205+
`Unknown type: "${typeName}". Ensure that this type exists ` +
206+
'either in the original schema, or is added in a type definition.',
207+
[typeRef],
208+
);
209209
},
210210
);
211211

212+
const extendTypeCache = Object.create(null);
213+
212214
// Get the root Query, Mutation, and Subscription object types.
213215
// Note: While this could make early assertions to get the correctly
214216
// typed values below, that would throw immediately while type system
215217
// validation with validateSchema() will produce more actionable results.
216218
const existingQueryType = schema.getQueryType();
217219
const queryType = existingQueryType
218-
? (definitionBuilder.buildType(existingQueryType.name): any)
220+
? getExtendedType(existingQueryType)
219221
: null;
220222

221223
const existingMutationType = schema.getMutationType();
222224
const mutationType = existingMutationType
223-
? (definitionBuilder.buildType(existingMutationType.name): any)
225+
? getExtendedType(existingMutationType)
224226
: null;
225227

226228
const existingSubscriptionType = schema.getSubscriptionType();
227229
const subscriptionType = existingSubscriptionType
228-
? (definitionBuilder.buildType(existingSubscriptionType.name): any)
230+
? getExtendedType(existingSubscriptionType)
229231
: null;
230232

231-
// Iterate through all types, getting the type definition for each, ensuring
232-
// that any type not directly referenced by a field will get created.
233-
const typeMap = schema.getTypeMap();
234-
const types = Object.keys(typeMap).map(typeName =>
235-
definitionBuilder.buildType(typeName),
236-
);
237-
238-
// Do the same with new types, appending to the list of defined types.
239-
Object.keys(typeDefinitionMap).forEach(typeName => {
240-
types.push(definitionBuilder.buildType(typeName));
241-
});
233+
const types = [
234+
// Iterate through all types, getting the type definition for each, ensuring
235+
// that any type not directly referenced by a field will get created.
236+
...objectValues(schema.getTypeMap()).map(type => getExtendedType(type)),
237+
// Do the same with new types.
238+
...objectValues(typeDefinitionMap).map(type => astBuilder.buildType(type)),
239+
];
242240

243241
// Support both original legacy names and extended legacy names.
244242
const schemaAllowedLegacyNames = schema.__allowedLegacyNames;
@@ -277,20 +275,24 @@ export function extendSchema(
277275
const existingDirectives = schema.getDirectives();
278276
invariant(existingDirectives, 'schema must have default directives');
279277

280-
const newDirectives = directiveDefinitions.map(directiveNode =>
281-
definitionBuilder.buildDirective(directiveNode),
278+
return existingDirectives.concat(
279+
directiveDefinitions.map(node => astBuilder.buildDirective(node)),
282280
);
283-
return existingDirectives.concat(newDirectives);
284281
}
285282

286-
function getTypeFromDef<T: GraphQLNamedType>(typeDef: T): T {
287-
const type = definitionBuilder.buildType(typeDef.name);
288-
return (type: any);
283+
function getExtendedType<T: GraphQLNamedType>(type: T): T {
284+
if (!extendTypeCache[type.name]) {
285+
extendTypeCache[type.name] = extendType(type);
286+
}
287+
return (extendTypeCache[type.name]: any);
289288
}
290289

291-
// Given a type's introspection result, construct the correct
292-
// GraphQLType instance.
293-
function extendType(type: GraphQLNamedType): GraphQLNamedType {
290+
// To be called at most once per type. Only getExtendedType should call this.
291+
function extendType(type) {
292+
if (isIntrospectionType(type)) {
293+
// Introspection types are not extended.
294+
return type;
295+
}
294296
if (isObjectType(type)) {
295297
return extendObjectType(type);
296298
}
@@ -300,6 +302,7 @@ export function extendSchema(
300302
if (isUnionType(type)) {
301303
return extendUnionType(type);
302304
}
305+
// This type is not yet extendable.
303306
return type;
304307
}
305308

@@ -344,7 +347,7 @@ export function extendSchema(
344347
return new GraphQLUnionType({
345348
name: type.name,
346349
description: type.description,
347-
types: type.getTypes().map(getTypeFromDef),
350+
types: type.getTypes().map(getExtendedType),
348351
astNode: type.astNode,
349352
resolveType: type.resolveType,
350353
});
@@ -353,7 +356,7 @@ export function extendSchema(
353356
function extendImplementedInterfaces(
354357
type: GraphQLObjectType,
355358
): Array<GraphQLInterfaceType> {
356-
const interfaces = type.getInterfaces().map(getTypeFromDef);
359+
const interfaces = type.getInterfaces().map(getExtendedType);
357360

358361
// If there are any extensions to the interfaces, apply those here.
359362
const extensions = typeExtensionsMap[type.name];
@@ -363,7 +366,7 @@ export function extendSchema(
363366
// Note: While this could make early assertions to get the correctly
364367
// typed values, that would throw immediately while type system
365368
// validation with validateSchema() will produce more actionable results.
366-
interfaces.push((definitionBuilder.buildType(namedType): any));
369+
interfaces.push((astBuilder.buildType(namedType): any));
367370
});
368371
});
369372
}
@@ -399,7 +402,7 @@ export function extendSchema(
399402
[field],
400403
);
401404
}
402-
newFieldMap[fieldName] = definitionBuilder.buildField(field);
405+
newFieldMap[fieldName] = astBuilder.buildField(field);
403406
});
404407
});
405408
}
@@ -414,6 +417,6 @@ export function extendSchema(
414417
if (isNonNullType(typeDef)) {
415418
return (GraphQLNonNull(extendFieldType(typeDef.ofType)): any);
416419
}
417-
return getTypeFromDef(typeDef);
420+
return getExtendedType(typeDef);
418421
}
419422
}

0 commit comments

Comments
 (0)