Skip to content

Commit a62eea8

Browse files
IvanGoncharovleebyron
authored andcommitted
Unify extending of type system (#1261)
1 parent 261b99b commit a62eea8

File tree

3 files changed

+44
-69
lines changed

3 files changed

+44
-69
lines changed

src/utilities/__tests__/extendSchema-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ describe('extendSchema', () => {
794794
}
795795
`);
796796
expect(() => extendSchema(testSchema, ast)).to.throw(
797-
'Cannot extend interface "UnknownInterfaceType" because it does not ' +
797+
'Cannot extend type "UnknownInterfaceType" because it does not ' +
798798
'exist in the existing schema.',
799799
);
800800
});

src/utilities/buildASTSchema.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,7 @@ export function buildASTSchema(
184184
},
185185
);
186186

187-
const types = typeDefs.map(def => definitionBuilder.buildType(def));
188-
187+
const types = definitionBuilder.buildTypes(typeDefs);
189188
const directives = directiveDefs.map(def =>
190189
definitionBuilder.buildDirective(def),
191190
);
@@ -266,6 +265,12 @@ export class ASTDefinitionBuilder {
266265
);
267266
}
268267

268+
buildTypes(
269+
nodes: $ReadOnlyArray<NamedTypeNode | TypeDefinitionNode>,
270+
): Array<GraphQLNamedType> {
271+
return nodes.map(node => this.buildType(node));
272+
}
273+
269274
buildType(node: NamedTypeNode | TypeDefinitionNode): GraphQLNamedType {
270275
const typeName = node.name.value;
271276
if (!this._cache[typeName]) {
@@ -334,11 +339,15 @@ export class ASTDefinitionBuilder {
334339

335340
_makeTypeDef(def: ObjectTypeDefinitionNode) {
336341
const typeName = def.name.value;
342+
const interfaces = def.interfaces;
337343
return new GraphQLObjectType({
338344
name: typeName,
339345
description: getDescription(def, this._options),
340346
fields: () => this._makeFieldDefMap(def),
341-
interfaces: () => this._makeImplementedInterfaces(def),
347+
// Note: While this could make early assertions to get the correctly
348+
// typed values, that would throw immediately while type system
349+
// validation with validateSchema() will produce more actionable results.
350+
interfaces: interfaces ? () => (this.buildTypes(interfaces): any) : [],
342351
astNode: def,
343352
});
344353
}
@@ -355,16 +364,6 @@ export class ASTDefinitionBuilder {
355364
: {};
356365
}
357366

358-
_makeImplementedInterfaces(def: ObjectTypeDefinitionNode) {
359-
return (
360-
def.interfaces &&
361-
// Note: While this could make early assertions to get the correctly
362-
// typed values, that would throw immediately while type system
363-
// validation with validateSchema() will produce more actionable results.
364-
def.interfaces.map(iface => (this.buildType(iface): any))
365-
);
366-
}
367-
368367
_makeInputValues(values: $ReadOnlyArray<InputValueDefinitionNode>) {
369368
return keyValMap(
370369
values,
@@ -419,7 +418,7 @@ export class ASTDefinitionBuilder {
419418
// Note: While this could make assertions to get the correctly typed
420419
// values below, that would throw immediately while type system
421420
// validation with validateSchema() will produce more actionable results.
422-
types: def.types ? def.types.map(t => (this.buildType(t): any)) : [],
421+
types: def.types ? (this.buildTypes(def.types): any) : [],
423422
astNode: def,
424423
});
425424
}

src/utilities/extendSchema.js

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ import { GraphQLDirective } from '../type/directives';
3434
import { Kind } from '../language/kinds';
3535

3636
import type { GraphQLType, GraphQLNamedType } from '../type/definition';
37-
38-
import type {
39-
DocumentNode,
40-
DirectiveDefinitionNode,
41-
TypeExtensionNode,
42-
} from '../language/ast';
37+
import type { DocumentNode, DirectiveDefinitionNode } from '../language/ast';
4338

4439
type Options = {|
4540
...GraphQLSchemaValidationOptions,
@@ -114,6 +109,7 @@ export function extendSchema(
114109
typeDefinitionMap[typeName] = def;
115110
break;
116111
case Kind.OBJECT_TYPE_EXTENSION:
112+
case Kind.INTERFACE_TYPE_EXTENSION:
117113
// Sanity check that this type extension exists within the
118114
// schema's existing types.
119115
const extendedTypeName = def.name.value;
@@ -125,39 +121,12 @@ export function extendSchema(
125121
[def],
126122
);
127123
}
128-
if (!isObjectType(existingType)) {
129-
throw new GraphQLError(
130-
`Cannot extend non-object type "${extendedTypeName}".`,
131-
[def],
132-
);
133-
}
134-
typeExtensionsMap[extendedTypeName] = appendExtensionToTypeExtensions(
135-
def,
136-
typeExtensionsMap[extendedTypeName],
137-
);
138-
break;
139-
case Kind.INTERFACE_TYPE_EXTENSION:
140-
const extendedInterfaceTypeName = def.name.value;
141-
const existingInterfaceType = schema.getType(extendedInterfaceTypeName);
142-
if (!existingInterfaceType) {
143-
throw new GraphQLError(
144-
`Cannot extend interface "${extendedInterfaceTypeName}" because ` +
145-
'it does not exist in the existing schema.',
146-
[def],
147-
);
148-
}
149-
if (!isInterfaceType(existingInterfaceType)) {
150-
throw new GraphQLError(
151-
`Cannot extend non-interface type "${extendedInterfaceTypeName}".`,
152-
[def],
153-
);
154-
}
155-
typeExtensionsMap[
156-
extendedInterfaceTypeName
157-
] = appendExtensionToTypeExtensions(
158-
def,
159-
typeExtensionsMap[extendedInterfaceTypeName],
160-
);
124+
checkExtensionNode(existingType, def);
125+
126+
const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
127+
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
128+
? existingTypeExtensions.concat([def])
129+
: [def];
161130
break;
162131
case Kind.DIRECTIVE_DEFINITION:
163132
const directiveName = def.name.value;
@@ -212,9 +181,6 @@ export function extendSchema(
212181
const extendTypeCache = Object.create(null);
213182

214183
// Get the root Query, Mutation, and Subscription object types.
215-
// Note: While this could make early assertions to get the correctly
216-
// typed values below, that would throw immediately while type system
217-
// validation with validateSchema() will produce more actionable results.
218184
const existingQueryType = schema.getQueryType();
219185
const queryType = existingQueryType
220186
? getExtendedType(existingQueryType)
@@ -235,7 +201,7 @@ export function extendSchema(
235201
// that any type not directly referenced by a field will get created.
236202
...objectValues(schema.getTypeMap()).map(type => getExtendedType(type)),
237203
// Do the same with new types.
238-
...objectValues(typeDefinitionMap).map(type => astBuilder.buildType(type)),
204+
...astBuilder.buildTypes(objectValues(typeDefinitionMap)),
239205
];
240206

241207
// Support both original legacy names and extended legacy names.
@@ -257,17 +223,6 @@ export function extendSchema(
257223
allowedLegacyNames,
258224
});
259225

260-
function appendExtensionToTypeExtensions(
261-
extension: TypeExtensionNode,
262-
existingTypeExtensions: ?Array<TypeExtensionNode>,
263-
): Array<TypeExtensionNode> {
264-
if (!existingTypeExtensions) {
265-
return [extension];
266-
}
267-
existingTypeExtensions.push(extension);
268-
return existingTypeExtensions;
269-
}
270-
271226
// Below are functions used for producing this schema that have closed over
272227
// this scope and have access to the schema, cache, and newly defined types.
273228

@@ -420,3 +375,24 @@ export function extendSchema(
420375
return getExtendedType(typeDef);
421376
}
422377
}
378+
379+
function checkExtensionNode(type, node) {
380+
switch (node.kind) {
381+
case Kind.OBJECT_TYPE_EXTENSION:
382+
if (!isObjectType(type)) {
383+
throw new GraphQLError(
384+
`Cannot extend non-object type "${type.name}".`,
385+
[node],
386+
);
387+
}
388+
break;
389+
case Kind.INTERFACE_TYPE_EXTENSION:
390+
if (!isInterfaceType(type)) {
391+
throw new GraphQLError(
392+
`Cannot extend non-interface type "${type.name}".`,
393+
[node],
394+
);
395+
}
396+
break;
397+
}
398+
}

0 commit comments

Comments
 (0)