@wundergraph/composition 0.14.0 → 0.16.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.
@@ -15,20 +15,21 @@ const merge_1 = require("@graphql-tools/merge");
15
15
  const constants_1 = require("../utils/constants");
16
16
  const normalization_factory_1 = require("../normalization/normalization-factory");
17
17
  const utils_4 = require("../normalization/utils");
18
+ const index_1 = require("graphql/index");
18
19
  class FederationFactory {
19
20
  abstractToConcreteTypeNames = new Map();
20
21
  areFieldsExternal = false;
21
22
  areFieldsShareable = false;
22
23
  argumentTypeNameSet = new Set();
23
24
  argumentConfigurations = [];
24
- entityInterfaceDatasByTypeName;
25
+ entityInterfaceFederationDataByTypeName;
25
26
  executableDirectives = new Set();
26
27
  parentTypeName = '';
27
28
  persistedDirectives = new Set([string_constants_1.DEPRECATED, string_constants_1.INACCESSIBLE, string_constants_1.TAG]);
28
29
  currentSubgraphName = '';
29
30
  childName = '';
30
31
  directiveDefinitions = new Map();
31
- entities = new Map();
32
+ entityContainersByTypeName;
32
33
  errors = [];
33
34
  evaluatedObjectLikesBySubgraph = new Map();
34
35
  extensions = new Map();
@@ -42,40 +43,26 @@ class FederationFactory {
42
43
  isCurrentParentExtensionType = false;
43
44
  isParentRootType = false;
44
45
  isParentInputObject = false;
45
- keyFieldsByParentTypeName = new Map();
46
+ keyFieldNamesByParentTypeName = new Map();
46
47
  outputFieldTypeNameSet = new Set();
47
48
  parents = new Map();
48
49
  rootTypeNames = new Set([string_constants_1.DEFAULT_MUTATION, string_constants_1.DEFAULT_QUERY, string_constants_1.DEFAULT_SUBSCRIPTION]);
49
- subgraphs = [];
50
+ internalSubgraphBySubgraphName;
50
51
  shareableErrorTypeNames = new Map();
51
- subgraphConfigBySubgraphName;
52
52
  warnings;
53
- constructor(subgraphs, subgraphConfigBySubgraphName, entityInterfaceDatasByTypeName, warnings) {
54
- this.subgraphs = subgraphs;
55
- this.subgraphConfigBySubgraphName = subgraphConfigBySubgraphName;
56
- this.entityInterfaceDatasByTypeName = entityInterfaceDatasByTypeName;
53
+ constructor(entityContainersByTypeName, entityInterfaceFederationDataByTypeName, internalSubgraphBySubgraphName, warnings) {
54
+ this.entityContainersByTypeName = entityContainersByTypeName;
55
+ this.entityInterfaceFederationDataByTypeName = entityInterfaceFederationDataByTypeName;
56
+ this.internalSubgraphBySubgraphName = internalSubgraphBySubgraphName;
57
57
  this.warnings = warnings || [];
58
58
  }
59
59
  isObjectRootType(node) {
60
60
  return this.rootTypeNames.has(node.name.value);
61
61
  }
62
- upsertEntity(node) {
63
- const typeName = node.name.value;
64
- const entity = this.entities.get(typeName);
65
- if (entity) {
66
- entity.subgraphs.add(this.currentSubgraphName);
67
- return;
68
- }
69
- this.entities.set(typeName, {
70
- fields: new Set(),
71
- subgraphs: new Set([this.currentSubgraphName]),
72
- });
73
- }
74
62
  populateMultiGraphAndRenameOperations(subgraphs) {
75
- for (const subgraph of subgraphs) {
63
+ for (const subgraph of subgraphs.values()) {
76
64
  this.currentSubgraphName = subgraph.name;
77
65
  (0, subgraph_1.walkSubgraphToCollectObjectLikesAndDirectiveDefinitions)(this, subgraph);
78
- (0, subgraph_1.walkSubgraphToCollectFields)(this, subgraph);
79
66
  }
80
67
  }
81
68
  getEnumMergeMethod(enumName) {
@@ -112,7 +99,7 @@ class FederationFactory {
112
99
  break;
113
100
  case graphql_1.Kind.OBJECT:
114
101
  break;
115
- // BOOLEAN, ENUM, FLOAT, INT, and STRING purposely fall through
102
+ // BOOLEAN, ENUM, FLOAT, INT, and STRING intentionally fall through
116
103
  case graphql_1.Kind.BOOLEAN:
117
104
  case graphql_1.Kind.ENUM:
118
105
  case graphql_1.Kind.FLOAT:
@@ -229,7 +216,7 @@ class FederationFactory {
229
216
  return argumentMap;
230
217
  }
231
218
  isFieldEntityKey() {
232
- const parent = this.keyFieldsByParentTypeName.get(this.parentTypeName);
219
+ const parent = this.keyFieldNamesByParentTypeName.get(this.parentTypeName);
233
220
  if (parent) {
234
221
  return parent.has(this.childName);
235
222
  }
@@ -238,7 +225,7 @@ class FederationFactory {
238
225
  isFieldExternal(node) {
239
226
  return this.areFieldsExternal || (0, utils_1.isNodeExternal)(node);
240
227
  }
241
- isFieldShareable(node, parent) {
228
+ isFieldShareable(node) {
242
229
  return (!this.isCurrentSubgraphVersionTwo || this.areFieldsShareable || this.isFieldEntityKey() || (0, utils_1.isNodeShareable)(node));
243
230
  }
244
231
  getOverrideTargetSubgraphName(node) {
@@ -275,7 +262,7 @@ class FederationFactory {
275
262
  this.upsertArguments(node, directiveDefinition.arguments);
276
263
  (0, utils_1.setLongestDescriptionForNode)(directiveDefinition.node, node.description);
277
264
  directiveDefinition.node.repeatable = directiveDefinition.node.repeatable && node.repeatable;
278
- directiveDefinition.subgraphs.add(this.currentSubgraphName);
265
+ directiveDefinition.subgraphNames.add(this.currentSubgraphName);
279
266
  return;
280
267
  }
281
268
  const executableLocations = (0, utils_1.extractExecutableDirectiveLocations)(node.locations, new Set());
@@ -283,7 +270,7 @@ class FederationFactory {
283
270
  arguments: this.upsertArguments(node, new Map()),
284
271
  executableLocations,
285
272
  node: (0, ast_1.directiveDefinitionNodeToMutable)(node),
286
- subgraphs: new Set([this.currentSubgraphName]),
273
+ subgraphNames: new Set([this.currentSubgraphName]),
287
274
  });
288
275
  if (executableLocations.size > 0) {
289
276
  this.executableDirectives.add(directiveName);
@@ -309,8 +296,8 @@ class FederationFactory {
309
296
  }
310
297
  // shareability doesn't matter if:
311
298
  // the field has only been seen exactly twice—the target override and the source override
312
- if (fieldContainer.subgraphs.size === 2 &&
313
- fieldContainer.subgraphs.has(fieldContainer.overrideTargetSubgraphName)) {
299
+ if (fieldContainer.subgraphNames.size === 2 &&
300
+ fieldContainer.subgraphNames.has(fieldContainer.overrideTargetSubgraphName)) {
314
301
  continue;
315
302
  }
316
303
  unshareableFields += 1;
@@ -331,7 +318,7 @@ class FederationFactory {
331
318
  }
332
319
  const fieldMap = parent.fields;
333
320
  const isFieldExternal = this.isFieldExternal(node);
334
- const isFieldShareable = this.isFieldShareable(node, parent);
321
+ const isFieldShareable = this.isFieldShareable(node);
335
322
  const fieldPath = `${this.parentTypeName}.${this.childName}`;
336
323
  const fieldRootTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
337
324
  const existingFieldContainer = fieldMap.get(this.childName);
@@ -339,7 +326,7 @@ class FederationFactory {
339
326
  if (existingFieldContainer) {
340
327
  this.extractPersistedDirectives(node.directives || [], existingFieldContainer.directives);
341
328
  (0, utils_1.setLongestDescriptionForNode)(existingFieldContainer.node, node.description);
342
- existingFieldContainer.subgraphs.add(this.currentSubgraphName);
329
+ existingFieldContainer.subgraphNames.add(this.currentSubgraphName);
343
330
  existingFieldContainer.overrideTargetSubgraphName = targetSubgraph;
344
331
  existingFieldContainer.subgraphsByShareable.set(this.currentSubgraphName, isFieldShareable);
345
332
  existingFieldContainer.subgraphsByExternal.set(this.currentSubgraphName, isFieldExternal);
@@ -364,7 +351,7 @@ class FederationFactory {
364
351
  isFieldExternal ||
365
352
  (existingFieldContainer.isShareable && isFieldShareable) ||
366
353
  this.isShareabilityOfAllFieldInstancesValid(existingFieldContainer) ||
367
- this.entityInterfaceDatasByTypeName.has(this.parentTypeName) // TODO handle shareability with interfaceObjects
354
+ this.entityInterfaceFederationDataByTypeName.has(this.parentTypeName) // TODO handle shareability with interfaceObjects
368
355
  ) {
369
356
  return;
370
357
  }
@@ -385,7 +372,7 @@ class FederationFactory {
385
372
  node: (0, ast_1.fieldDefinitionNodeToMutable)(node, this.parentTypeName),
386
373
  namedTypeName: fieldRootTypeName,
387
374
  overrideTargetSubgraphName: targetSubgraph,
388
- subgraphs: new Set([this.currentSubgraphName]),
375
+ subgraphNames: new Set([this.currentSubgraphName]),
389
376
  subgraphsByShareable: new Map([[this.currentSubgraphName, isFieldShareable]]),
390
377
  subgraphsByExternal: new Map([[this.currentSubgraphName, isFieldExternal]]),
391
378
  });
@@ -465,7 +452,7 @@ class FederationFactory {
465
452
  (0, utils_1.setLongestDescriptionForNode)(parent.node, node.description);
466
453
  this.extractPersistedDirectives(node.directives || [], parent.directives);
467
454
  (0, utils_1.extractInterfaces)(node, parent.interfaces);
468
- parent.subgraphs.add(this.currentSubgraphName);
455
+ parent.subgraphNames.add(this.currentSubgraphName);
469
456
  return;
470
457
  }
471
458
  this.parents.set(parentTypeName, {
@@ -477,7 +464,7 @@ class FederationFactory {
477
464
  ...node,
478
465
  kind: graphql_1.Kind.INTERFACE_TYPE_DEFINITION,
479
466
  }),
480
- subgraphs: new Set([this.currentSubgraphName]),
467
+ subgraphNames: new Set([this.currentSubgraphName]),
481
468
  });
482
469
  }
483
470
  upsertParentNode(node) {
@@ -526,7 +513,7 @@ class FederationFactory {
526
513
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
527
514
  }
528
515
  (0, utils_1.extractInterfaces)(node, parent.interfaces);
529
- parent.subgraphs.add(this.currentSubgraphName);
516
+ parent.subgraphNames.add(this.currentSubgraphName);
530
517
  return;
531
518
  }
532
519
  this.parents.set(parentTypeName, {
@@ -535,7 +522,7 @@ class FederationFactory {
535
522
  interfaces: (0, utils_1.extractInterfaces)(node, new Set()),
536
523
  kind: node.kind,
537
524
  node: (0, ast_1.interfaceTypeDefinitionNodeToMutable)(node),
538
- subgraphs: new Set([this.currentSubgraphName]),
525
+ subgraphNames: new Set([this.currentSubgraphName]),
539
526
  });
540
527
  return;
541
528
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
@@ -557,7 +544,7 @@ class FederationFactory {
557
544
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
558
545
  }
559
546
  (0, utils_1.extractInterfaces)(node, parent.interfaces);
560
- parent.subgraphs.add(this.currentSubgraphName);
547
+ parent.subgraphNames.add(this.currentSubgraphName);
561
548
  return;
562
549
  }
563
550
  this.parents.set(parentTypeName, {
@@ -567,7 +554,7 @@ class FederationFactory {
567
554
  isRootType: this.isParentRootType,
568
555
  kind: node.kind,
569
556
  node: (0, ast_1.objectTypeDefinitionNodeToMutable)(node),
570
- subgraphs: new Set([this.currentSubgraphName]),
557
+ subgraphNames: new Set([this.currentSubgraphName]),
571
558
  });
572
559
  return;
573
560
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
@@ -597,7 +584,7 @@ class FederationFactory {
597
584
  if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
598
585
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.OBJECT_TYPE_EXTENSION, extension.kind);
599
586
  }
600
- extension.subgraphs.add(this.currentSubgraphName);
587
+ extension.subgraphNames.add(this.currentSubgraphName);
601
588
  (0, utils_1.extractInterfaces)(node, extension.interfaces);
602
589
  this.extractPersistedDirectives(node.directives || [], extension.directives);
603
590
  return;
@@ -611,7 +598,7 @@ class FederationFactory {
611
598
  isRootType: this.isParentRootType,
612
599
  kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
613
600
  node: (0, ast_1.objectTypeExtensionNodeToMutable)(node),
614
- subgraphs: new Set([this.currentSubgraphName]),
601
+ subgraphNames: new Set([this.currentSubgraphName]),
615
602
  });
616
603
  }
617
604
  isTypeValidImplementation(originalType, implementationType) {
@@ -736,7 +723,7 @@ class FederationFactory {
736
723
  }
737
724
  mergeArguments(container, args, errors, argumentNames) {
738
725
  for (const argumentContainer of container.arguments.values()) {
739
- const missingSubgraphs = (0, utils_3.getEntriesNotInHashSet)(container.subgraphs, argumentContainer.subgraphs);
726
+ const missingSubgraphs = (0, utils_3.getEntriesNotInHashSet)(container.subgraphNames, argumentContainer.subgraphs);
740
727
  const argumentName = argumentContainer.node.name.value;
741
728
  if (missingSubgraphs.length > 0) {
742
729
  // Required arguments must be defined in all subgraphs that define the field
@@ -764,7 +751,7 @@ class FederationFactory {
764
751
  if (!this.executableDirectives.has(directiveName)) {
765
752
  return;
766
753
  }
767
- if (this.subgraphs.length !== directiveContainer.subgraphs.size) {
754
+ if (this.internalSubgraphBySubgraphName.size !== directiveContainer.subgraphNames.size) {
768
755
  return;
769
756
  }
770
757
  directiveContainer.node.locations = (0, utils_1.setToNameNodeArray)(directiveContainer.executableLocations);
@@ -883,7 +870,7 @@ class FederationFactory {
883
870
  return true;
884
871
  }
885
872
  if (entityAncestorName === parentTypeName) {
886
- const hasOverlap = (0, utils_3.doSetsHaveAnyOverlap)(fieldSubgraphs, (0, utils_3.getOrThrowError)(this.entities, entityAncestorName, string_constants_1.ENTITIES).subgraphs);
873
+ const hasOverlap = (0, utils_3.doSetsHaveAnyOverlap)(fieldSubgraphs, (0, utils_3.getOrThrowError)(this.entityContainersByTypeName, entityAncestorName, string_constants_1.ENTITIES).subgraphNames);
887
874
  this.graphPaths.set(path, hasOverlap);
888
875
  return hasOverlap;
889
876
  }
@@ -906,7 +893,7 @@ class FederationFactory {
906
893
  return false;
907
894
  }
908
895
  isFieldExternalInAllMutualSubgraphs(subgraphs, fieldContainer) {
909
- const mutualSubgraphs = (0, utils_3.getAllMutualEntries)(subgraphs, fieldContainer.subgraphs);
896
+ const mutualSubgraphs = (0, utils_3.getAllMutualEntries)(subgraphs, fieldContainer.subgraphNames);
910
897
  if (mutualSubgraphs.size < 1) {
911
898
  return false;
912
899
  }
@@ -934,7 +921,7 @@ class FederationFactory {
934
921
  }
935
922
  for (const entityAncestor of entityAncestors) {
936
923
  const entityContainer = (0, utils_3.getOrThrowError)(this.parents, entityAncestor, string_constants_1.PARENTS);
937
- const mutualEntityAncestorRootTypeFieldSubgraphs = (0, utils_3.getAllMutualEntries)(rootTypeFieldSubgraphs, entityContainer.subgraphs);
924
+ const mutualEntityAncestorRootTypeFieldSubgraphs = (0, utils_3.getAllMutualEntries)(rootTypeFieldSubgraphs, entityContainer.subgraphNames);
938
925
  const mutualEntityAncestorSubgraphs = (0, utils_3.getAllMutualEntries)(mutualEntityAncestorRootTypeFieldSubgraphs, objectSubgraphs);
939
926
  for (const mutualSubgraph of mutualEntityAncestorSubgraphs) {
940
927
  const objects = this.evaluatedObjectLikesBySubgraph.get(mutualSubgraph);
@@ -968,12 +955,12 @@ class FederationFactory {
968
955
  if (this.isFieldExternalInAllMutualSubgraphs(rootTypeFieldData.subgraphs, fieldContainer)) {
969
956
  continue;
970
957
  }
971
- this.updateEvaluatedSubgraphOccurrences(rootTypeFieldData.subgraphs, parentContainer.subgraphs, entityAncestors, parentTypeName);
958
+ this.updateEvaluatedSubgraphOccurrences(rootTypeFieldData.subgraphs, parentContainer.subgraphNames, entityAncestors, parentTypeName);
972
959
  evaluatedObjectLikes.add(parentTypeName);
973
- const isFieldResolvable = (0, utils_3.doSetsHaveAnyOverlap)(rootTypeFieldData.subgraphs, fieldContainer.subgraphs) ||
974
- this.isFieldResolvableByEntityAncestor(entityAncestors, fieldContainer.subgraphs, parentTypeName);
960
+ const isFieldResolvable = (0, utils_3.doSetsHaveAnyOverlap)(rootTypeFieldData.subgraphs, fieldContainer.subgraphNames) ||
961
+ this.isFieldResolvableByEntityAncestor(entityAncestors, fieldContainer.subgraphNames, parentTypeName);
975
962
  const newCurrentFieldPath = currentFieldPath + (isParentAbstract ? ' ' : '.') + fieldName;
976
- const entity = this.entities.get(fieldNamedTypeName);
963
+ const entity = this.entityContainersByTypeName.get(fieldNamedTypeName);
977
964
  if (isFieldResolvable) {
978
965
  // The base scalars are not in this.parentMap
979
966
  if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
@@ -991,7 +978,7 @@ class FederationFactory {
991
978
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
992
979
  // intentional fallthrough
993
980
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
994
- this.evaluateResolvabilityOfAbstractType(fieldNamedTypeName, childContainer.kind, rootTypeFieldData, newCurrentFieldPath, evaluatedObjectLikes, entity ? [...entityAncestors, fieldNamedTypeName] : [...entityAncestors], fieldContainer.subgraphs);
981
+ this.evaluateResolvabilityOfAbstractType(fieldNamedTypeName, childContainer.kind, rootTypeFieldData, newCurrentFieldPath, evaluatedObjectLikes, entity ? [...entityAncestors, fieldNamedTypeName] : [...entityAncestors]);
995
982
  continue;
996
983
  default:
997
984
  this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
@@ -999,7 +986,7 @@ class FederationFactory {
999
986
  }
1000
987
  }
1001
988
  if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
1002
- this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath, parentTypeName));
989
+ this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphNames], newCurrentFieldPath, parentTypeName));
1003
990
  continue;
1004
991
  }
1005
992
  const childContainer = (0, utils_3.getOrThrowError)(this.parents, fieldNamedTypeName, string_constants_1.PARENTS);
@@ -1007,21 +994,21 @@ class FederationFactory {
1007
994
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
1008
995
  // intentional fallthrough
1009
996
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
1010
- this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath, parentTypeName));
997
+ this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphNames], newCurrentFieldPath, parentTypeName));
1011
998
  continue;
1012
999
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
1013
1000
  // intentional fallthrough
1014
1001
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
1015
1002
  // intentional fallthrough
1016
1003
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
1017
- this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath + string_constants_1.SELECTION_REPRESENTATION, parentTypeName));
1004
+ this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphNames], newCurrentFieldPath + string_constants_1.SELECTION_REPRESENTATION, parentTypeName));
1018
1005
  continue;
1019
1006
  default:
1020
1007
  this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
1021
1008
  }
1022
1009
  }
1023
1010
  }
1024
- evaluateResolvabilityOfAbstractType(abstractTypeName, abstractKind, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors, parentSubgraphs) {
1011
+ evaluateResolvabilityOfAbstractType(abstractTypeName, abstractKind, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors) {
1025
1012
  if (evaluatedObjectLikes.has(abstractTypeName)) {
1026
1013
  return;
1027
1014
  }
@@ -1040,43 +1027,205 @@ class FederationFactory {
1040
1027
  throw (0, errors_1.unexpectedParentKindErrorMessage)(concreteTypeName, 'Object', (0, utils_3.kindToTypeString)(concreteParentContainer.kind));
1041
1028
  }
1042
1029
  // If the concrete type is unreachable through an inline fragment, it is not an error
1043
- if (!(0, utils_3.doSetsHaveAnyOverlap)(concreteParentContainer.subgraphs, rootTypeFieldData.subgraphs)) {
1030
+ if (!(0, utils_3.doSetsHaveAnyOverlap)(concreteParentContainer.subgraphNames, rootTypeFieldData.subgraphs)) {
1044
1031
  continue;
1045
1032
  }
1046
- const entity = this.entities.get(concreteTypeName);
1033
+ const entity = this.entityContainersByTypeName.get(concreteTypeName);
1047
1034
  this.evaluateResolvabilityOfObject(concreteParentContainer, rootTypeFieldData, currentFieldPath + ` ... on ` + concreteTypeName, evaluatedObjectLikes, entity ? [...entityAncestors, concreteTypeName] : [...entityAncestors], true);
1048
1035
  }
1049
1036
  }
1037
+ validateKeyFieldSetsForImplicitEntity(entityContainer) {
1038
+ const internalSubgraph = (0, utils_3.getOrThrowError)(this.internalSubgraphBySubgraphName, this.currentSubgraphName, 'internalSubgraphBySubgraphName');
1039
+ const parentContainerByTypeName = internalSubgraph.parentContainerByTypeName;
1040
+ const extensionContainerByTypeName = internalSubgraph.extensionContainerByTypeName;
1041
+ const implicitEntityContainer = parentContainerByTypeName.get(entityContainer.typeName) ||
1042
+ extensionContainerByTypeName.get(entityContainer.typeName);
1043
+ if (!implicitEntityContainer ||
1044
+ (implicitEntityContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
1045
+ implicitEntityContainer.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION)) {
1046
+ throw (0, errors_1.incompatibleParentKindFatalError)(entityContainer.typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, implicitEntityContainer?.kind || graphql_1.Kind.NULL);
1047
+ }
1048
+ const configurationData = (0, utils_3.getOrThrowError)(internalSubgraph.configurationDataMap, entityContainer.typeName, 'internalSubgraph.configurationDataMap');
1049
+ const keyFieldNames = new Set();
1050
+ const keys = [];
1051
+ // Any errors in the field sets would be caught when evaluating the explicit entities, so they are ignored here
1052
+ for (const fieldSet of entityContainer.keyFieldSets) {
1053
+ // Create a new selection set so that the value can be parsed as a new DocumentNode
1054
+ const { error, documentNode } = (0, utils_1.safeParse)('{' + fieldSet + '}');
1055
+ if (error || !documentNode) {
1056
+ // This would be caught as an error elsewhere
1057
+ continue;
1058
+ }
1059
+ const parentContainers = [implicitEntityContainer];
1060
+ const definedFields = [];
1061
+ let currentDepth = -1;
1062
+ let shouldDefineSelectionSet = true;
1063
+ let shouldAddKeyFieldSet = true;
1064
+ (0, index_1.visit)(documentNode, {
1065
+ Argument: {
1066
+ enter() {
1067
+ // Fields that define arguments are never allowed in a key FieldSet
1068
+ // However, at this stage, it actually means the argument is undefined on the field
1069
+ shouldAddKeyFieldSet = false;
1070
+ return index_1.BREAK;
1071
+ },
1072
+ },
1073
+ Field: {
1074
+ enter(node) {
1075
+ const parentContainer = parentContainers[currentDepth];
1076
+ const parentTypeName = parentContainer.name.value;
1077
+ // If an object-like was just visited, a selection set should have been entered
1078
+ if (shouldDefineSelectionSet) {
1079
+ shouldAddKeyFieldSet = false;
1080
+ return index_1.BREAK;
1081
+ }
1082
+ const fieldName = node.name.value;
1083
+ const fieldPath = `${parentTypeName}.${fieldName}`;
1084
+ const fieldContainer = parentContainer.fields.get(fieldName);
1085
+ // undefined if the field does not exist on the parent
1086
+ if (!fieldContainer || fieldContainer.arguments.size || definedFields[currentDepth].has(fieldName)) {
1087
+ shouldAddKeyFieldSet = false;
1088
+ return index_1.BREAK;
1089
+ }
1090
+ definedFields[currentDepth].add(fieldName);
1091
+ // Depth 0 is the original parent type
1092
+ // If a field is external, but it's part of a key FieldSet, it will be included in the root configuration
1093
+ if (currentDepth === 0) {
1094
+ keyFieldNames.add(fieldName);
1095
+ }
1096
+ const namedTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, fieldContainer.node.type);
1097
+ // The base scalars are not in the parents map
1098
+ if (constants_1.BASE_SCALARS.has(namedTypeName)) {
1099
+ return;
1100
+ }
1101
+ // The child could itself be a parent and could exist as an object extension
1102
+ const childContainer = parentContainerByTypeName.get(namedTypeName) || extensionContainerByTypeName.get(namedTypeName);
1103
+ if (!childContainer) {
1104
+ shouldAddKeyFieldSet = false;
1105
+ return index_1.BREAK;
1106
+ }
1107
+ if (childContainer.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
1108
+ childContainer.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
1109
+ shouldDefineSelectionSet = true;
1110
+ parentContainers.push(childContainer);
1111
+ return;
1112
+ }
1113
+ // interfaces and unions are invalid in a key directive
1114
+ if ((0, utils_1.isKindAbstract)(childContainer.kind)) {
1115
+ shouldAddKeyFieldSet = false;
1116
+ return index_1.BREAK;
1117
+ }
1118
+ },
1119
+ },
1120
+ InlineFragment: {
1121
+ enter() {
1122
+ shouldAddKeyFieldSet = false;
1123
+ return index_1.BREAK;
1124
+ },
1125
+ },
1126
+ SelectionSet: {
1127
+ enter() {
1128
+ if (!shouldDefineSelectionSet) {
1129
+ shouldAddKeyFieldSet = false;
1130
+ return index_1.BREAK;
1131
+ }
1132
+ currentDepth += 1;
1133
+ shouldDefineSelectionSet = false;
1134
+ if (currentDepth < 0 || currentDepth >= parentContainers.length) {
1135
+ shouldAddKeyFieldSet = false;
1136
+ return index_1.BREAK;
1137
+ }
1138
+ definedFields.push(new Set());
1139
+ },
1140
+ leave() {
1141
+ if (shouldDefineSelectionSet) {
1142
+ shouldAddKeyFieldSet = false;
1143
+ return index_1.BREAK;
1144
+ }
1145
+ // Empty selection sets would be a parse error, so it is unnecessary to handle them
1146
+ currentDepth -= 1;
1147
+ parentContainers.pop();
1148
+ definedFields.pop();
1149
+ },
1150
+ },
1151
+ });
1152
+ if (!shouldAddKeyFieldSet) {
1153
+ continue;
1154
+ }
1155
+ // Add any top-level fields that compose the key in case they are external
1156
+ (0, utils_3.addIterableValuesToSet)(keyFieldNames, configurationData.fieldNames);
1157
+ keys.push({
1158
+ fieldName: '',
1159
+ selectionSet: (0, utils_4.getNormalizedFieldSet)(documentNode),
1160
+ disableEntityResolver: true,
1161
+ });
1162
+ }
1163
+ if (keys.length > 0) {
1164
+ configurationData.isRootNode = true;
1165
+ configurationData.keys = keys;
1166
+ }
1167
+ }
1050
1168
  federate() {
1051
- this.populateMultiGraphAndRenameOperations(this.subgraphs);
1169
+ this.populateMultiGraphAndRenameOperations(this.internalSubgraphBySubgraphName);
1052
1170
  const factory = this;
1053
- for (const subgraph of this.subgraphs) {
1171
+ for (const subgraph of this.internalSubgraphBySubgraphName.values()) {
1054
1172
  this.isCurrentSubgraphVersionTwo = subgraph.isVersionTwo;
1055
1173
  this.currentSubgraphName = subgraph.name;
1056
- this.keyFieldsByParentTypeName = subgraph.keyFieldsByParentTypeName;
1174
+ this.keyFieldNamesByParentTypeName = subgraph.keyFieldNamesByParentTypeName;
1057
1175
  (0, subgraph_1.walkSubgraphToFederate)(subgraph.definitions, subgraph.overriddenFieldNamesByParentTypeName, factory);
1058
1176
  }
1059
- for (const [typeName, entityInterfaceData] of this.entityInterfaceDatasByTypeName) {
1177
+ for (const [typeName, entityInterfaceData] of this.entityInterfaceFederationDataByTypeName) {
1060
1178
  (0, utils_3.subtractSourceSetFromTargetSet)(entityInterfaceData.interfaceFieldNames, entityInterfaceData.interfaceObjectFieldNames);
1061
1179
  const entityInterface = (0, utils_3.getOrThrowError)(this.parents, typeName, 'parents');
1062
1180
  if (entityInterface.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
1063
1181
  // TODO error
1064
1182
  continue;
1065
1183
  }
1066
- const concreteTypes = this.abstractToConcreteTypeNames.get(typeName) || [];
1067
- for (const concreteType of concreteTypes) {
1068
- const parentContainer = (0, utils_3.getOrThrowError)(this.parents, concreteType, 'parents');
1069
- if (parentContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
1184
+ for (const subgraphName of entityInterfaceData.interfaceObjectSubgraphs) {
1185
+ const configurationDataMap = (0, utils_3.getOrThrowError)(this.internalSubgraphBySubgraphName, subgraphName, 'internalSubgraphBySubgraphName').configurationDataMap;
1186
+ const concreteTypeNames = this.abstractToConcreteTypeNames.get(typeName);
1187
+ if (!concreteTypeNames) {
1070
1188
  continue;
1071
1189
  }
1072
- for (const fieldName of entityInterfaceData.interfaceObjectFieldNames) {
1073
- const existingFieldContainer = parentContainer.fields.get(fieldName);
1074
- if (existingFieldContainer) {
1075
- // TODO handle shareability
1190
+ const interfaceObjectConfiguration = (0, utils_3.getOrThrowError)(configurationDataMap, typeName, 'configurationDataMap');
1191
+ const keys = interfaceObjectConfiguration.keys;
1192
+ if (!keys) {
1193
+ // error TODO no keys
1194
+ continue;
1195
+ }
1196
+ interfaceObjectConfiguration.entityInterfaceConcreteTypeNames = entityInterfaceData.concreteTypeNames;
1197
+ const fieldNames = interfaceObjectConfiguration.fieldNames;
1198
+ for (const concreteTypeName of concreteTypeNames) {
1199
+ if (configurationDataMap.has(concreteTypeName)) {
1200
+ // error TODO
1076
1201
  continue;
1077
1202
  }
1078
- const interfaceFieldContainer = (0, utils_3.getOrThrowError)(entityInterface.fields, fieldName, 'entityInterface.fields');
1079
- parentContainer.fields.set(fieldName, { ...interfaceFieldContainer });
1203
+ const concreteTypeContainer = (0, utils_3.getOrThrowError)(this.parents, concreteTypeName, 'parents');
1204
+ if (concreteTypeContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
1205
+ continue;
1206
+ }
1207
+ // The subgraph locations of the interface object must be added to the concrete types that implement it
1208
+ const entity = this.entityContainersByTypeName.get(concreteTypeName);
1209
+ if (entity) {
1210
+ // TODO error if not an entity
1211
+ entity.subgraphNames.add(subgraphName);
1212
+ }
1213
+ const configurationData = {
1214
+ fieldNames,
1215
+ isRootNode: true,
1216
+ keys,
1217
+ typeName: concreteTypeName,
1218
+ };
1219
+ for (const fieldName of entityInterfaceData.interfaceObjectFieldNames) {
1220
+ const existingFieldContainer = concreteTypeContainer.fields.get(fieldName);
1221
+ if (existingFieldContainer) {
1222
+ // TODO handle shareability
1223
+ continue;
1224
+ }
1225
+ const interfaceFieldContainer = (0, utils_3.getOrThrowError)(entityInterface.fields, fieldName, 'entityInterface.fields');
1226
+ concreteTypeContainer.fields.set(fieldName, { ...interfaceFieldContainer });
1227
+ }
1228
+ configurationDataMap.set(concreteTypeName, configurationData);
1080
1229
  }
1081
1230
  }
1082
1231
  }
@@ -1113,7 +1262,7 @@ class FederationFactory {
1113
1262
  this.childName = extensionFieldName;
1114
1263
  this.upsertExtensionFieldArguments(extensionFieldContainer.arguments, baseFieldContainer.arguments);
1115
1264
  (0, utils_1.setLongestDescriptionForNode)(baseFieldContainer.node, extensionFieldContainer.node.description);
1116
- (0, utils_3.addIterableValuesToSet)(extensionFieldContainer.subgraphs, baseFieldContainer.subgraphs);
1265
+ (0, utils_3.addIterableValuesToSet)(extensionFieldContainer.subgraphNames, baseFieldContainer.subgraphNames);
1117
1266
  continue;
1118
1267
  }
1119
1268
  const parent = this.shareableErrorTypeNames.get(typeName);
@@ -1135,8 +1284,7 @@ class FederationFactory {
1135
1284
  this.errors.push((0, errors_1.shareableFieldDefinitionsError)(parent, children));
1136
1285
  }
1137
1286
  const objectLikeContainersWithInterfaces = [];
1138
- for (const parentContainer of this.parents.values()) {
1139
- const parentTypeName = parentContainer.node.name.value;
1287
+ for (const [parentTypeName, parentContainer] of this.parents) {
1140
1288
  switch (parentContainer.kind) {
1141
1289
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
1142
1290
  const values = [];
@@ -1268,7 +1416,7 @@ class FederationFactory {
1268
1416
  if (evaluatedRootScalarsAndEnums.has(rootTypeFieldNamedTypeName)) {
1269
1417
  continue;
1270
1418
  }
1271
- if (!this.shouldEvaluateObjectLike(rootTypeFieldContainer.subgraphs, rootTypeFieldNamedTypeName)) {
1419
+ if (!this.shouldEvaluateObjectLike(rootTypeFieldContainer.subgraphNames, rootTypeFieldNamedTypeName)) {
1272
1420
  continue;
1273
1421
  }
1274
1422
  const childContainer = (0, utils_3.getOrThrowError)(this.parents, rootTypeFieldNamedTypeName, string_constants_1.PARENTS);
@@ -1278,7 +1426,7 @@ class FederationFactory {
1278
1426
  fieldTypeNodeString: (0, merge_1.printTypeNode)(rootTypeFieldContainer.node.type),
1279
1427
  path: fieldPath,
1280
1428
  typeName: rootTypeName,
1281
- subgraphs: rootTypeFieldContainer.subgraphs,
1429
+ subgraphs: rootTypeFieldContainer.subgraphNames,
1282
1430
  };
1283
1431
  switch (childContainer.kind) {
1284
1432
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
@@ -1289,12 +1437,12 @@ class FederationFactory {
1289
1437
  evaluatedRootScalarsAndEnums.add(rootTypeFieldNamedTypeName);
1290
1438
  continue;
1291
1439
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
1292
- this.evaluateResolvabilityOfObject(childContainer, rootTypeFieldData, fieldPath, new Set(), this.entities.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : []);
1440
+ this.evaluateResolvabilityOfObject(childContainer, rootTypeFieldData, fieldPath, new Set(), this.entityContainersByTypeName.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : []);
1293
1441
  continue;
1294
1442
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
1295
1443
  // intentional fallthrough
1296
1444
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
1297
- this.evaluateResolvabilityOfAbstractType(rootTypeFieldNamedTypeName, childContainer.kind, rootTypeFieldData, fieldPath, new Set(), this.entities.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : [], rootTypeFieldContainer.subgraphs);
1445
+ this.evaluateResolvabilityOfAbstractType(rootTypeFieldNamedTypeName, childContainer.kind, rootTypeFieldData, fieldPath, new Set(), this.entityContainersByTypeName.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : []);
1298
1446
  continue;
1299
1447
  default:
1300
1448
  this.errors.push((0, errors_1.unexpectedObjectResponseType)(fieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
@@ -1309,10 +1457,17 @@ class FederationFactory {
1309
1457
  kind: graphql_1.Kind.DOCUMENT,
1310
1458
  definitions,
1311
1459
  };
1460
+ const subgraphConfigBySubgraphName = new Map();
1461
+ for (const subgraph of this.internalSubgraphBySubgraphName.values()) {
1462
+ subgraphConfigBySubgraphName.set(subgraph.name, {
1463
+ configurationDataMap: subgraph.configurationDataMap,
1464
+ schema: subgraph.schema,
1465
+ });
1466
+ }
1312
1467
  return {
1313
1468
  federationResult: {
1314
1469
  argumentConfigurations: this.argumentConfigurations,
1315
- subgraphConfigBySubgraphName: this.subgraphConfigBySubgraphName,
1470
+ subgraphConfigBySubgraphName,
1316
1471
  federatedGraphAST: newAst,
1317
1472
  federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
1318
1473
  },
@@ -1325,31 +1480,46 @@ function federateSubgraphs(subgraphs) {
1325
1480
  if (subgraphs.length < 1) {
1326
1481
  return { errors: [errors_1.minimumSubgraphRequirementError] };
1327
1482
  }
1328
- const { errors, internalSubgraphsBySubgraphName, warnings } = (0, normalization_factory_1.batchNormalize)(subgraphs);
1483
+ const { entityContainerByTypeName, errors, internalSubgraphBySubgraphName, warnings } = (0, normalization_factory_1.batchNormalize)(subgraphs);
1329
1484
  if (errors) {
1330
1485
  return { errors };
1331
1486
  }
1332
- const internalSubgraphs = [];
1333
- const subgraphConfigBySubgraphName = new Map();
1334
- const entityInterfaceDatas = new Map();
1335
- for (const [subgraphName, internalSubgraph] of internalSubgraphsBySubgraphName) {
1336
- internalSubgraphs.push(internalSubgraph);
1337
- subgraphConfigBySubgraphName.set(subgraphName, {
1338
- configurationDataMap: internalSubgraph.configurationDataMap,
1339
- schema: internalSubgraph.schema,
1340
- });
1487
+ const entityInterfaceFederationDataByTypeName = new Map();
1488
+ const invalidEntityInterfacesByTypeName = new Map();
1489
+ const validEntityInterfaceTypeNames = new Set();
1490
+ for (const [subgraphName, internalSubgraph] of internalSubgraphBySubgraphName) {
1341
1491
  for (const [typeName, entityInterfaceData] of internalSubgraph.entityInterfaces) {
1342
- const existingDatas = entityInterfaceDatas.get(typeName);
1343
- if (!existingDatas) {
1344
- entityInterfaceDatas.set(typeName, (0, utils_3.newEntityInterfaceDatas)(entityInterfaceData, subgraphName));
1492
+ // Always add each entity interface to the invalid entity interfaces map
1493
+ // If not, earlier checks would not account for implementations not yet seen
1494
+ const invalidEntityInterfaces = (0, utils_3.getValueOrDefault)(invalidEntityInterfacesByTypeName, typeName, () => []);
1495
+ invalidEntityInterfaces.push({
1496
+ subgraphName,
1497
+ concreteTypeNames: entityInterfaceData.concreteTypeNames || new Set(),
1498
+ });
1499
+ const existingData = entityInterfaceFederationDataByTypeName.get(typeName);
1500
+ if (!existingData) {
1501
+ validEntityInterfaceTypeNames.add(typeName);
1502
+ entityInterfaceFederationDataByTypeName.set(typeName, (0, utils_3.newEntityInterfaceFederationData)(entityInterfaceData, subgraphName));
1503
+ continue;
1345
1504
  }
1346
- else {
1347
- (0, utils_3.upsertEntityInterfaceDatas)(existingDatas, entityInterfaceData, subgraphName);
1505
+ const areAnyImplementationsUndefined = (0, utils_3.upsertEntityInterfaceFederationData)(existingData, entityInterfaceData, subgraphName);
1506
+ if (areAnyImplementationsUndefined) {
1507
+ validEntityInterfaceTypeNames.delete(typeName);
1348
1508
  }
1349
1509
  }
1350
1510
  }
1351
- const federationFactory = new FederationFactory(internalSubgraphs, subgraphConfigBySubgraphName, entityInterfaceDatas, warnings);
1352
- return federationFactory.federate();
1511
+ // Remove the valid entity interfaces type names so only genuinely invalid entity interfaces remain
1512
+ for (const typeName of validEntityInterfaceTypeNames) {
1513
+ invalidEntityInterfacesByTypeName.delete(typeName);
1514
+ }
1515
+ if (invalidEntityInterfacesByTypeName.size > 0) {
1516
+ return {
1517
+ errors: [
1518
+ (0, errors_1.undefinedEntityInterfaceImplementationsError)(invalidEntityInterfacesByTypeName, entityInterfaceFederationDataByTypeName),
1519
+ ],
1520
+ };
1521
+ }
1522
+ return new FederationFactory(entityContainerByTypeName, entityInterfaceFederationDataByTypeName, internalSubgraphBySubgraphName, warnings).federate();
1353
1523
  }
1354
1524
  exports.federateSubgraphs = federateSubgraphs;
1355
1525
  //# sourceMappingURL=federation-factory.js.map