@wundergraph/composition 0.0.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +197 -14
- package/README.md +140 -1
- package/dist/ast/ast.d.ts +2 -0
- package/dist/ast/ast.js.map +1 -1
- package/dist/ast/utils.d.ts +7 -7
- package/dist/ast/utils.js +30 -16
- package/dist/ast/utils.js.map +1 -1
- package/dist/errors/errors.d.ts +8 -4
- package/dist/errors/errors.js +67 -10
- package/dist/errors/errors.js.map +1 -1
- package/dist/federation/federation-factory.d.ts +6 -5
- package/dist/federation/federation-factory.js +105 -30
- package/dist/federation/federation-factory.js.map +1 -1
- package/dist/federation/federation-result.d.ts +2 -2
- package/dist/federation/federation-result.js +0 -7
- package/dist/federation/federation-result.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/normalization/normalization-factory.d.ts +9 -3
- package/dist/normalization/normalization-factory.js +235 -105
- package/dist/normalization/normalization-factory.js.map +1 -1
- package/dist/normalization/utils.d.ts +7 -3
- package/dist/normalization/utils.js +0 -1
- package/dist/normalization/utils.js.map +1 -1
- package/dist/subgraph/field-configuration.d.ts +7 -0
- package/dist/subgraph/field-configuration.js +3 -0
- package/dist/subgraph/field-configuration.js.map +1 -0
- package/dist/{federation → subgraph}/subgraph.d.ts +1 -1
- package/dist/{federation → subgraph}/subgraph.js +8 -28
- package/dist/subgraph/subgraph.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/constants.js +8 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/string-constants.d.ts +1 -0
- package/dist/utils/string-constants.js +2 -1
- package/dist/utils/string-constants.js.map +1 -1
- package/dist/utils/utils.d.ts +22 -1
- package/dist/utils/utils.js +65 -3
- package/dist/utils/utils.js.map +1 -1
- package/package.json +11 -10
- package/dist/federation/subgraph.js.map +0 -1
|
@@ -10,6 +10,7 @@ const utils_3 = require("../utils/utils");
|
|
|
10
10
|
const errors_1 = require("../errors/errors");
|
|
11
11
|
const string_constants_1 = require("../utils/string-constants");
|
|
12
12
|
const buildASTSchema_1 = require("../buildASTSchema/buildASTSchema");
|
|
13
|
+
const merge_1 = require("@graphql-tools/merge");
|
|
13
14
|
function normalizeSubgraphFromString(subgraph) {
|
|
14
15
|
let document;
|
|
15
16
|
try {
|
|
@@ -53,12 +54,30 @@ class NormalizationFactory {
|
|
|
53
54
|
operationTypes: new Map(),
|
|
54
55
|
};
|
|
55
56
|
}
|
|
57
|
+
extractArguments(node, map) {
|
|
58
|
+
if (!node.arguments) {
|
|
59
|
+
return map;
|
|
60
|
+
}
|
|
61
|
+
for (const argumentNode of node.arguments) {
|
|
62
|
+
const argumentName = argumentNode.name.value;
|
|
63
|
+
if (map.has(argumentName)) {
|
|
64
|
+
// TODO
|
|
65
|
+
this.errors.push(new Error('duplicate argument'));
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
map.set(argumentName, argumentNode);
|
|
69
|
+
}
|
|
70
|
+
return map;
|
|
71
|
+
}
|
|
56
72
|
extractDirectives(node, map) {
|
|
57
73
|
if (!node.directives) {
|
|
58
74
|
return map;
|
|
59
75
|
}
|
|
60
76
|
for (const directive of node.directives) {
|
|
61
77
|
const directiveName = directive.name.value;
|
|
78
|
+
if (directiveName === string_constants_1.EXTENDS) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
62
81
|
const existingDirectives = map.get(directiveName);
|
|
63
82
|
if (existingDirectives) {
|
|
64
83
|
existingDirectives.push(directive);
|
|
@@ -68,20 +87,6 @@ class NormalizationFactory {
|
|
|
68
87
|
}
|
|
69
88
|
return map;
|
|
70
89
|
}
|
|
71
|
-
extractUniqueInterfaces(node, interfaces) {
|
|
72
|
-
if (!node.interfaces) {
|
|
73
|
-
return interfaces;
|
|
74
|
-
}
|
|
75
|
-
for (const face of node.interfaces) {
|
|
76
|
-
const name = face.name.value;
|
|
77
|
-
if (interfaces.has(name)) {
|
|
78
|
-
this.errors.push(new Error(`Interface "${name}" can only be defined on type "${this.parentTypeName}" once.`)); // TODO
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
interfaces.add(name);
|
|
82
|
-
}
|
|
83
|
-
return interfaces;
|
|
84
|
-
}
|
|
85
90
|
extractUniqueUnionMembers(members, map) {
|
|
86
91
|
for (const member of members) {
|
|
87
92
|
const name = member.name.value;
|
|
@@ -102,7 +107,7 @@ class NormalizationFactory {
|
|
|
102
107
|
interfaces.add(interfaceName);
|
|
103
108
|
continue;
|
|
104
109
|
}
|
|
105
|
-
this.errors.push((0, errors_1.
|
|
110
|
+
this.errors.push((0, errors_1.duplicateInterfaceExtensionError)(interfaceName, typeName));
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
mergeUniqueUnionMembers(baseUnion, extensionUnion) {
|
|
@@ -139,7 +144,7 @@ class NormalizationFactory {
|
|
|
139
144
|
for (const [directiveName, directives] of parent.directives) {
|
|
140
145
|
const definition = this.allDirectiveDefinitions.get(directiveName);
|
|
141
146
|
if (!definition) {
|
|
142
|
-
this.errors.push((0, errors_1.undefinedDirectiveError)(
|
|
147
|
+
this.errors.push((0, errors_1.undefinedDirectiveError)(directiveName, parentTypeName));
|
|
143
148
|
continue;
|
|
144
149
|
}
|
|
145
150
|
const allArguments = new Set();
|
|
@@ -165,7 +170,7 @@ class NormalizationFactory {
|
|
|
165
170
|
}
|
|
166
171
|
if (!directive.arguments || directive.arguments.length < 1) {
|
|
167
172
|
if (requiredArguments.size > 0) {
|
|
168
|
-
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName,
|
|
173
|
+
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName, [...requiredArguments]));
|
|
169
174
|
}
|
|
170
175
|
else {
|
|
171
176
|
normalizedDirectives.push(directive);
|
|
@@ -173,9 +178,9 @@ class NormalizationFactory {
|
|
|
173
178
|
continue;
|
|
174
179
|
}
|
|
175
180
|
const definedArguments = (0, utils_2.getDefinedArgumentsForDirective)(directive.arguments, allArguments, directiveName, parentTypeName, errorMessages);
|
|
176
|
-
const missingRequiredArguments = (0, utils_3.
|
|
181
|
+
const missingRequiredArguments = (0, utils_3.getEntriesNotInHashSet)(requiredArguments, definedArguments);
|
|
177
182
|
if (missingRequiredArguments.length > 0) {
|
|
178
|
-
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName,
|
|
183
|
+
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName, [...requiredArguments], missingRequiredArguments));
|
|
179
184
|
}
|
|
180
185
|
// Only add unique entity keys
|
|
181
186
|
if (directiveName === string_constants_1.KEY) {
|
|
@@ -198,12 +203,59 @@ class NormalizationFactory {
|
|
|
198
203
|
}
|
|
199
204
|
return normalizedDirectives;
|
|
200
205
|
}
|
|
206
|
+
convertKindForExtension(node) {
|
|
207
|
+
switch (node.kind) {
|
|
208
|
+
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
209
|
+
return graphql_1.Kind.INTERFACE_TYPE_EXTENSION;
|
|
210
|
+
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
211
|
+
return graphql_1.Kind.OBJECT_TYPE_EXTENSION;
|
|
212
|
+
default:
|
|
213
|
+
return node.kind;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
handleObjectLikeExtension(node) {
|
|
217
|
+
this.isCurrentParentExtension = true;
|
|
218
|
+
const extension = this.extensions.get(this.parentTypeName);
|
|
219
|
+
const convertedKind = this.convertKindForExtension(node);
|
|
220
|
+
if (extension) {
|
|
221
|
+
if (extension.kind !== convertedKind) {
|
|
222
|
+
this.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
this.extractDirectives(node, extension.directives);
|
|
226
|
+
(0, utils_1.extractInterfaces)(node, extension.interfaces, this.errors);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const isEntity = (0, utils_1.isObjectLikeNodeEntity)(node);
|
|
230
|
+
const interfaces = new Set();
|
|
231
|
+
this.extensions.set(this.parentTypeName, {
|
|
232
|
+
directives: this.extractDirectives(node, new Map()),
|
|
233
|
+
fields: new Map(),
|
|
234
|
+
interfaces: (0, utils_1.extractInterfaces)(node, interfaces, this.errors),
|
|
235
|
+
isEntity,
|
|
236
|
+
kind: convertedKind,
|
|
237
|
+
name: node.name,
|
|
238
|
+
});
|
|
239
|
+
if (node.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION
|
|
240
|
+
|| node.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION
|
|
241
|
+
|| !isEntity) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const existingEntityKeyMap = this.entityMap.get(this.parentTypeName);
|
|
245
|
+
const { entityKeyMap, errors } = (0, utils_1.getEntityKeyExtractionResults)(node, existingEntityKeyMap || new Map());
|
|
246
|
+
if (errors.length > 0) {
|
|
247
|
+
this.errors.push(...errors);
|
|
248
|
+
}
|
|
249
|
+
if (!existingEntityKeyMap) {
|
|
250
|
+
this.entityMap.set(this.parentTypeName, entityKeyMap);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
201
253
|
validateChildDirectives(child, hostPath) {
|
|
202
254
|
const childKind = child.node.kind;
|
|
203
255
|
for (const [directiveName, directives] of child.directives) {
|
|
204
256
|
const definition = this.allDirectiveDefinitions.get(directiveName);
|
|
205
257
|
if (!definition) {
|
|
206
|
-
this.errors.push((0, errors_1.undefinedDirectiveError)(
|
|
258
|
+
this.errors.push((0, errors_1.undefinedDirectiveError)(directiveName, hostPath));
|
|
207
259
|
continue;
|
|
208
260
|
}
|
|
209
261
|
const allArguments = new Set();
|
|
@@ -225,14 +277,14 @@ class NormalizationFactory {
|
|
|
225
277
|
}
|
|
226
278
|
if (!directive.arguments || directive.arguments.length < 1) {
|
|
227
279
|
if (requiredArguments.size > 0) {
|
|
228
|
-
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath,
|
|
280
|
+
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath, [...requiredArguments]));
|
|
229
281
|
}
|
|
230
282
|
continue;
|
|
231
283
|
}
|
|
232
284
|
const definedArguments = (0, utils_2.getDefinedArgumentsForDirective)(directive.arguments, allArguments, directiveName, hostPath, errorMessages);
|
|
233
|
-
const missingRequiredArguments = (0, utils_3.
|
|
285
|
+
const missingRequiredArguments = (0, utils_3.getEntriesNotInHashSet)(requiredArguments, definedArguments);
|
|
234
286
|
if (missingRequiredArguments.length > 0) {
|
|
235
|
-
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath,
|
|
287
|
+
errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath, [...requiredArguments], missingRequiredArguments));
|
|
236
288
|
}
|
|
237
289
|
}
|
|
238
290
|
if (errorMessages.length > 0) {
|
|
@@ -240,6 +292,85 @@ class NormalizationFactory {
|
|
|
240
292
|
}
|
|
241
293
|
}
|
|
242
294
|
}
|
|
295
|
+
validateInterfaceImplementations(container) {
|
|
296
|
+
if (container.interfaces.size < 1) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const implementationErrorsMap = new Map();
|
|
300
|
+
for (const interfaceName of container.interfaces) {
|
|
301
|
+
const interfaceContainer = (0, utils_3.getOrThrowError)(this.parents, interfaceName);
|
|
302
|
+
if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
|
|
303
|
+
throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
|
|
304
|
+
}
|
|
305
|
+
const implementationErrors = {
|
|
306
|
+
invalidFieldImplementations: new Map(),
|
|
307
|
+
unimplementedFields: [],
|
|
308
|
+
};
|
|
309
|
+
let hasErrors = false;
|
|
310
|
+
for (const [fieldName, interfaceField] of interfaceContainer.fields) {
|
|
311
|
+
let hasNestedErrors = false;
|
|
312
|
+
const containerField = container.fields.get(fieldName);
|
|
313
|
+
if (!containerField) {
|
|
314
|
+
hasErrors = true;
|
|
315
|
+
implementationErrors.unimplementedFields.push(fieldName);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
const invalidFieldImplementation = {
|
|
319
|
+
invalidAdditionalArguments: new Set(),
|
|
320
|
+
invalidImplementedArguments: [],
|
|
321
|
+
originalResponseType: (0, merge_1.printTypeNode)(interfaceField.node.type),
|
|
322
|
+
unimplementedArguments: new Set(),
|
|
323
|
+
};
|
|
324
|
+
// The implemented field type must be equally or more restrictive than the original interface field type
|
|
325
|
+
if (!(0, utils_3.isTypeValidImplementation)(interfaceField.node.type, containerField.node.type)) {
|
|
326
|
+
hasErrors = true;
|
|
327
|
+
hasNestedErrors = true;
|
|
328
|
+
invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
|
|
329
|
+
}
|
|
330
|
+
const handledArguments = new Set();
|
|
331
|
+
for (const [argumentName, interfaceArgument] of interfaceField.arguments) {
|
|
332
|
+
handledArguments.add(argumentName);
|
|
333
|
+
const containerArgument = containerField.arguments.get(argumentName);
|
|
334
|
+
// The type implementing the interface must include all arguments with no variation for that argument
|
|
335
|
+
if (!containerArgument) {
|
|
336
|
+
hasErrors = true;
|
|
337
|
+
hasNestedErrors = true;
|
|
338
|
+
invalidFieldImplementation.unimplementedArguments.add(argumentName);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
// Implemented arguments should be the exact same type
|
|
342
|
+
const actualType = (0, merge_1.printTypeNode)(containerArgument.type);
|
|
343
|
+
const expectedType = (0, merge_1.printTypeNode)(interfaceArgument.type);
|
|
344
|
+
if (expectedType !== actualType) {
|
|
345
|
+
hasErrors = true;
|
|
346
|
+
hasNestedErrors = true;
|
|
347
|
+
invalidFieldImplementation.invalidImplementedArguments.push({ actualType, argumentName, expectedType });
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Additional arguments must be optional (nullable)
|
|
351
|
+
for (const [argumentName, argumentNode] of containerField.arguments) {
|
|
352
|
+
if (handledArguments.has(argumentName)) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (argumentNode.type.kind !== graphql_1.Kind.NON_NULL_TYPE) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
hasErrors = true;
|
|
359
|
+
hasNestedErrors = true;
|
|
360
|
+
invalidFieldImplementation.invalidAdditionalArguments.add(argumentName);
|
|
361
|
+
}
|
|
362
|
+
if (hasNestedErrors) {
|
|
363
|
+
implementationErrors.invalidFieldImplementations.set(fieldName, invalidFieldImplementation);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (hasErrors) {
|
|
367
|
+
implementationErrorsMap.set(interfaceName, implementationErrors);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (implementationErrorsMap.size) {
|
|
371
|
+
this.errors.push((0, errors_1.unimplementedInterfaceFieldsError)(container.name.value, (0, utils_3.kindToTypeString)(container.kind), implementationErrorsMap));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
243
374
|
normalize(document) {
|
|
244
375
|
const factory = this;
|
|
245
376
|
(0, graphql_1.visit)(document, {
|
|
@@ -282,7 +413,7 @@ class NormalizationFactory {
|
|
|
282
413
|
enter(node) {
|
|
283
414
|
const name = node.name.value;
|
|
284
415
|
if (factory.parents.has(name)) {
|
|
285
|
-
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)(
|
|
416
|
+
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
|
|
286
417
|
return false;
|
|
287
418
|
}
|
|
288
419
|
factory.parentTypeName = name;
|
|
@@ -331,7 +462,7 @@ class NormalizationFactory {
|
|
|
331
462
|
? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
|
|
332
463
|
: (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
|
|
333
464
|
if (parent.kind !== graphql_1.Kind.ENUM_TYPE_DEFINITION && parent.kind !== graphql_1.Kind.ENUM_TYPE_EXTENSION) {
|
|
334
|
-
throw
|
|
465
|
+
throw (0, errors_1.unexpectedKindFatalError)(name);
|
|
335
466
|
}
|
|
336
467
|
if (parent.values.has(name)) {
|
|
337
468
|
const error = factory.isCurrentParentExtension
|
|
@@ -363,7 +494,7 @@ class NormalizationFactory {
|
|
|
363
494
|
parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION &&
|
|
364
495
|
parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION &&
|
|
365
496
|
parent.kind !== graphql_1.Kind.INTERFACE_TYPE_EXTENSION) {
|
|
366
|
-
throw
|
|
497
|
+
throw (0, errors_1.unexpectedKindFatalError)(factory.parentTypeName);
|
|
367
498
|
}
|
|
368
499
|
if (parent.fields.has(name)) {
|
|
369
500
|
const error = factory.isCurrentParentExtension
|
|
@@ -373,6 +504,7 @@ class NormalizationFactory {
|
|
|
373
504
|
return;
|
|
374
505
|
}
|
|
375
506
|
parent.fields.set(name, {
|
|
507
|
+
arguments: factory.extractArguments(node, new Map),
|
|
376
508
|
directives: factory.extractDirectives(node, new Map()),
|
|
377
509
|
name,
|
|
378
510
|
node,
|
|
@@ -386,7 +518,7 @@ class NormalizationFactory {
|
|
|
386
518
|
enter(node) {
|
|
387
519
|
const name = node.name.value;
|
|
388
520
|
if (factory.parents.has(name)) {
|
|
389
|
-
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)(
|
|
521
|
+
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
|
|
390
522
|
return false;
|
|
391
523
|
}
|
|
392
524
|
factory.parentTypeName = name;
|
|
@@ -438,7 +570,7 @@ class NormalizationFactory {
|
|
|
438
570
|
? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
|
|
439
571
|
: (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
|
|
440
572
|
if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION && parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION) {
|
|
441
|
-
throw
|
|
573
|
+
throw (0, errors_1.unexpectedKindFatalError)(factory.parentTypeName);
|
|
442
574
|
}
|
|
443
575
|
if (parent.fields.has(name)) {
|
|
444
576
|
factory.errors.push((0, errors_1.duplicateValueExtensionError)('input', factory.parentTypeName, name));
|
|
@@ -454,47 +586,32 @@ class NormalizationFactory {
|
|
|
454
586
|
InterfaceTypeDefinition: {
|
|
455
587
|
enter(node) {
|
|
456
588
|
const name = node.name.value;
|
|
589
|
+
factory.parentTypeName = name;
|
|
590
|
+
if ((0, utils_1.isNodeExtension)(node)) {
|
|
591
|
+
return factory.handleObjectLikeExtension(node);
|
|
592
|
+
}
|
|
457
593
|
if (factory.parents.has(name)) {
|
|
458
|
-
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)(
|
|
594
|
+
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
|
|
459
595
|
return false;
|
|
460
596
|
}
|
|
461
|
-
factory.parentTypeName = name;
|
|
462
597
|
factory.parents.set(name, {
|
|
463
598
|
description: node.description,
|
|
464
599
|
directives: factory.extractDirectives(node, new Map()),
|
|
465
600
|
fields: new Map(),
|
|
466
|
-
interfaces: (0, utils_1.extractInterfaces)(node, new Set()),
|
|
601
|
+
interfaces: (0, utils_1.extractInterfaces)(node, new Set(), factory.errors),
|
|
467
602
|
kind: node.kind,
|
|
468
603
|
name: node.name,
|
|
469
604
|
});
|
|
470
605
|
},
|
|
471
606
|
leave() {
|
|
607
|
+
factory.isCurrentParentExtension = false;
|
|
472
608
|
factory.parentTypeName = '';
|
|
473
609
|
},
|
|
474
610
|
},
|
|
475
611
|
InterfaceTypeExtension: {
|
|
476
612
|
enter(node) {
|
|
477
|
-
|
|
478
|
-
factory.
|
|
479
|
-
factory.isCurrentParentExtension = true;
|
|
480
|
-
const extension = factory.extensions.get(factory.parentTypeName);
|
|
481
|
-
if (extension) {
|
|
482
|
-
if (extension.kind !== graphql_1.Kind.INTERFACE_TYPE_EXTENSION) {
|
|
483
|
-
factory.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
|
|
484
|
-
return false;
|
|
485
|
-
}
|
|
486
|
-
factory.extractDirectives(node, extension.directives);
|
|
487
|
-
factory.extractUniqueInterfaces(node, extension.interfaces);
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
const interfaces = new Set();
|
|
491
|
-
factory.extensions.set(name, {
|
|
492
|
-
directives: factory.extractDirectives(node, new Map()),
|
|
493
|
-
fields: new Map(),
|
|
494
|
-
interfaces: (0, utils_1.extractInterfaces)(node, interfaces),
|
|
495
|
-
kind: node.kind,
|
|
496
|
-
name: node.name,
|
|
497
|
-
});
|
|
613
|
+
factory.parentTypeName = node.name.value;
|
|
614
|
+
return factory.handleObjectLikeExtension(node);
|
|
498
615
|
},
|
|
499
616
|
leave() {
|
|
500
617
|
factory.isCurrentParentExtension = false;
|
|
@@ -504,20 +621,26 @@ class NormalizationFactory {
|
|
|
504
621
|
ObjectTypeDefinition: {
|
|
505
622
|
enter(node) {
|
|
506
623
|
const name = node.name.value;
|
|
624
|
+
factory.parentTypeName = name;
|
|
625
|
+
// handling for @extends directive
|
|
626
|
+
if ((0, utils_1.isNodeExtension)(node)) {
|
|
627
|
+
return factory.handleObjectLikeExtension(node);
|
|
628
|
+
}
|
|
507
629
|
if (factory.parents.has(name)) {
|
|
508
|
-
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)(
|
|
630
|
+
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
|
|
509
631
|
return false;
|
|
510
632
|
}
|
|
511
|
-
|
|
633
|
+
const isEntity = (0, utils_1.isObjectLikeNodeEntity)(node);
|
|
512
634
|
factory.parents.set(name, {
|
|
513
635
|
description: node.description,
|
|
514
636
|
directives: factory.extractDirectives(node, new Map()),
|
|
515
637
|
fields: new Map(),
|
|
516
|
-
interfaces:
|
|
638
|
+
interfaces: (0, utils_1.extractInterfaces)(node, new Set(), factory.errors),
|
|
639
|
+
isEntity,
|
|
517
640
|
kind: node.kind,
|
|
518
641
|
name: node.name,
|
|
519
642
|
});
|
|
520
|
-
if (!
|
|
643
|
+
if (!isEntity) {
|
|
521
644
|
return;
|
|
522
645
|
}
|
|
523
646
|
const existingEntityKeyMap = factory.entityMap.get(name);
|
|
@@ -530,43 +653,14 @@ class NormalizationFactory {
|
|
|
530
653
|
}
|
|
531
654
|
},
|
|
532
655
|
leave() {
|
|
656
|
+
factory.isCurrentParentExtension = false;
|
|
533
657
|
factory.parentTypeName = '';
|
|
534
658
|
},
|
|
535
659
|
},
|
|
536
660
|
ObjectTypeExtension: {
|
|
537
661
|
enter(node) {
|
|
538
|
-
|
|
539
|
-
factory.
|
|
540
|
-
factory.isCurrentParentExtension = true;
|
|
541
|
-
const extension = factory.extensions.get(factory.parentTypeName);
|
|
542
|
-
if (extension) {
|
|
543
|
-
if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
544
|
-
factory.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
|
|
545
|
-
return false;
|
|
546
|
-
}
|
|
547
|
-
factory.extractDirectives(node, extension.directives);
|
|
548
|
-
factory.extractUniqueInterfaces(node, extension.interfaces);
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
const interfaces = new Set();
|
|
552
|
-
factory.extensions.set(name, {
|
|
553
|
-
directives: factory.extractDirectives(node, new Map()),
|
|
554
|
-
fields: new Map(),
|
|
555
|
-
interfaces: (0, utils_1.extractInterfaces)(node, interfaces),
|
|
556
|
-
kind: node.kind,
|
|
557
|
-
name: node.name,
|
|
558
|
-
});
|
|
559
|
-
if (!(0, utils_1.isObjectNodeEntity)(node)) {
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
const existingEntityKeyMap = factory.entityMap.get(name);
|
|
563
|
-
const { entityKeyMap, errors } = (0, utils_1.getEntityKeyExtractionResults)(node, existingEntityKeyMap || new Map());
|
|
564
|
-
if (errors.length > 0) {
|
|
565
|
-
factory.errors.push(...errors);
|
|
566
|
-
}
|
|
567
|
-
if (!existingEntityKeyMap) {
|
|
568
|
-
factory.entityMap.set(name, entityKeyMap);
|
|
569
|
-
}
|
|
662
|
+
factory.parentTypeName = node.name.value;
|
|
663
|
+
return factory.handleObjectLikeExtension(node);
|
|
570
664
|
},
|
|
571
665
|
leave() {
|
|
572
666
|
factory.isCurrentParentExtension = false;
|
|
@@ -599,7 +693,7 @@ class NormalizationFactory {
|
|
|
599
693
|
const name = node.name.value;
|
|
600
694
|
const parent = factory.parents.get(name);
|
|
601
695
|
if (parent) {
|
|
602
|
-
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)(
|
|
696
|
+
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
|
|
603
697
|
return false;
|
|
604
698
|
}
|
|
605
699
|
factory.parents.set(name, {
|
|
@@ -633,7 +727,6 @@ class NormalizationFactory {
|
|
|
633
727
|
},
|
|
634
728
|
SchemaDefinition: {
|
|
635
729
|
enter(node) {
|
|
636
|
-
// Apollo allows multiple schema definitions
|
|
637
730
|
factory.extractDirectives(node, factory.schemaDefinition.directives);
|
|
638
731
|
factory.schemaDefinition.description = factory.schemaDefinition.description || node.description;
|
|
639
732
|
},
|
|
@@ -649,7 +742,7 @@ class NormalizationFactory {
|
|
|
649
742
|
factory.parentTypeName = name;
|
|
650
743
|
const parent = factory.parents.get(name);
|
|
651
744
|
if (parent) {
|
|
652
|
-
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)(
|
|
745
|
+
factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
|
|
653
746
|
return false;
|
|
654
747
|
}
|
|
655
748
|
if (!node.types) {
|
|
@@ -711,9 +804,23 @@ class NormalizationFactory {
|
|
|
711
804
|
if (this.schemaDefinition.operationTypes.size > 0) {
|
|
712
805
|
definitions.push((0, utils_2.schemaContainerToNode)(this, this.schemaDefinition));
|
|
713
806
|
}
|
|
807
|
+
// configurationDataMap is map of ConfigurationData per type name.
|
|
808
|
+
// It is an Intermediate configuration object that will be converted to an engine configuration in the router
|
|
809
|
+
const configurationDataMap = new Map();
|
|
714
810
|
const validExtensionOrphans = new Set();
|
|
715
811
|
const parentsToIgnore = new Set();
|
|
716
812
|
for (const [typeName, extension] of this.extensions) {
|
|
813
|
+
const entity = this.entityMap.get(typeName);
|
|
814
|
+
const configurationData = {
|
|
815
|
+
fieldNames: new Set(),
|
|
816
|
+
isRootNode: !!entity,
|
|
817
|
+
selectionSets: entity ? [...entity.keys()] : [],
|
|
818
|
+
typeName,
|
|
819
|
+
};
|
|
820
|
+
if (extension.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
821
|
+
(0, utils_3.addIterableValuesToSet)(extension.fields.keys(), configurationData.fieldNames);
|
|
822
|
+
configurationDataMap.set(typeName, configurationData);
|
|
823
|
+
}
|
|
717
824
|
const baseType = this.parents.get(typeName);
|
|
718
825
|
if (!baseType) {
|
|
719
826
|
if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
@@ -721,14 +828,14 @@ class NormalizationFactory {
|
|
|
721
828
|
}
|
|
722
829
|
else {
|
|
723
830
|
(0, utils_2.validateEntityKeys)(this, typeName, true);
|
|
831
|
+
this.validateInterfaceImplementations(extension);
|
|
724
832
|
validExtensionOrphans.add(typeName);
|
|
725
833
|
definitions.push((0, utils_2.objectLikeContainerToNode)(this, extension));
|
|
726
834
|
}
|
|
727
835
|
continue;
|
|
728
836
|
}
|
|
729
|
-
if (!(0, utils_1.areBaseAndExtensionKindsCompatible)(baseType.kind, extension.kind)) {
|
|
730
|
-
this.errors.push(
|
|
731
|
-
`"${typeName}" is type "${baseType.kind}", but an extension of the same name is type "${extension.kind}.`));
|
|
837
|
+
if (!(0, utils_1.areBaseAndExtensionKindsCompatible)(baseType.kind, extension.kind, typeName)) {
|
|
838
|
+
this.errors.push((0, errors_1.incompatibleExtensionError)(typeName, baseType.kind, extension.kind));
|
|
732
839
|
continue;
|
|
733
840
|
}
|
|
734
841
|
switch (baseType.kind) {
|
|
@@ -757,17 +864,20 @@ class NormalizationFactory {
|
|
|
757
864
|
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
758
865
|
// intentional fallthrough
|
|
759
866
|
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
760
|
-
const
|
|
761
|
-
for (const [fieldName,
|
|
762
|
-
if (
|
|
763
|
-
|
|
867
|
+
const objectLikeExtension = extension;
|
|
868
|
+
for (const [fieldName, fieldContainer] of objectLikeExtension.fields) {
|
|
869
|
+
if (baseType.fields.has(fieldName)) {
|
|
870
|
+
this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, typeName));
|
|
764
871
|
continue;
|
|
765
872
|
}
|
|
766
|
-
|
|
873
|
+
baseType.fields.set(fieldName, fieldContainer);
|
|
874
|
+
configurationData.fieldNames.add(fieldName);
|
|
767
875
|
}
|
|
768
876
|
(0, utils_2.validateEntityKeys)(this, typeName);
|
|
769
|
-
this.mergeUniqueInterfaces(
|
|
770
|
-
|
|
877
|
+
this.mergeUniqueInterfaces(objectLikeExtension.interfaces, baseType.interfaces, typeName);
|
|
878
|
+
this.validateInterfaceImplementations(baseType);
|
|
879
|
+
configurationDataMap.set(typeName, configurationData);
|
|
880
|
+
definitions.push((0, utils_2.objectLikeContainerToNode)(this, baseType, objectLikeExtension));
|
|
771
881
|
break;
|
|
772
882
|
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
773
883
|
definitions.push((0, utils_2.scalarContainerToNode)(this, baseType, extension));
|
|
@@ -777,7 +887,7 @@ class NormalizationFactory {
|
|
|
777
887
|
definitions.push((0, utils_2.unionContainerToNode)(this, baseType, unionExtension));
|
|
778
888
|
break;
|
|
779
889
|
default:
|
|
780
|
-
throw
|
|
890
|
+
throw (0, errors_1.unexpectedKindFatalError)(typeName);
|
|
781
891
|
}
|
|
782
892
|
// At this point, the base type has been dealt with, so it doesn't need to be dealt with again
|
|
783
893
|
parentsToIgnore.add(typeName);
|
|
@@ -794,9 +904,19 @@ class NormalizationFactory {
|
|
|
794
904
|
definitions.push((0, utils_2.inputObjectContainerToNode)(this, parentContainer));
|
|
795
905
|
break;
|
|
796
906
|
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
797
|
-
//
|
|
907
|
+
// intentional fallthrough
|
|
798
908
|
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
909
|
+
const entity = this.entityMap.get(typeName);
|
|
910
|
+
const configurationData = {
|
|
911
|
+
fieldNames: new Set(),
|
|
912
|
+
isRootNode: !!entity,
|
|
913
|
+
selectionSets: entity ? [...entity.keys()] : [],
|
|
914
|
+
typeName,
|
|
915
|
+
};
|
|
916
|
+
(0, utils_3.addIterableValuesToSet)(parentContainer.fields.keys(), configurationData.fieldNames);
|
|
799
917
|
(0, utils_2.validateEntityKeys)(this, typeName);
|
|
918
|
+
this.validateInterfaceImplementations(parentContainer);
|
|
919
|
+
configurationDataMap.set(typeName, configurationData);
|
|
800
920
|
definitions.push((0, utils_2.objectLikeContainerToNode)(this, parentContainer));
|
|
801
921
|
break;
|
|
802
922
|
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
@@ -823,7 +943,7 @@ class NormalizationFactory {
|
|
|
823
943
|
}
|
|
824
944
|
const object = this.parents.get(operationTypeName);
|
|
825
945
|
const extension = this.extensions.get(operationTypeName);
|
|
826
|
-
// Node is truthy
|
|
946
|
+
// Node is truthy if an operation type was explicitly declared
|
|
827
947
|
if (node) {
|
|
828
948
|
// If the type is not defined in the schema, it's always an error
|
|
829
949
|
if (!object && !extension) {
|
|
@@ -833,6 +953,14 @@ class NormalizationFactory {
|
|
|
833
953
|
// Add the explicitly defined type to the map for the federation-factory
|
|
834
954
|
this.operationTypeNames.set(operationTypeName, operationType);
|
|
835
955
|
}
|
|
956
|
+
if (!object && !extension) {
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
const rootNode = configurationDataMap.get(operationTypeName);
|
|
960
|
+
if (rootNode) {
|
|
961
|
+
rootNode.isRootNode = true;
|
|
962
|
+
rootNode.typeName = defaultTypeName;
|
|
963
|
+
}
|
|
836
964
|
const containers = [object, extension];
|
|
837
965
|
for (const container of containers) {
|
|
838
966
|
if (!container) {
|
|
@@ -845,7 +973,8 @@ class NormalizationFactory {
|
|
|
845
973
|
// Operations whose response type is an extension orphan could be valid through a federated graph
|
|
846
974
|
// However, the field would have to be shareable to ever be valid TODO
|
|
847
975
|
for (const fieldContainer of container.fields.values()) {
|
|
848
|
-
const
|
|
976
|
+
const fieldName = fieldContainer.name;
|
|
977
|
+
const fieldPath = `${operationTypeName}.${fieldName}`;
|
|
849
978
|
const fieldTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, fieldContainer.node.type);
|
|
850
979
|
if (!constants_1.BASE_SCALARS.has(fieldTypeName) &&
|
|
851
980
|
!this.parents.has(fieldTypeName) &&
|
|
@@ -869,6 +998,7 @@ class NormalizationFactory {
|
|
|
869
998
|
};
|
|
870
999
|
return {
|
|
871
1000
|
normalizationResult: {
|
|
1001
|
+
configurationDataMap,
|
|
872
1002
|
isVersionTwo: this.isSubgraphVersionTwo,
|
|
873
1003
|
operationTypes: this.operationTypeNames,
|
|
874
1004
|
subgraphAST: newAST,
|