@wundergraph/composition 0.37.0 → 0.37.1

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.
Files changed (70) hide show
  1. package/README.md +41 -48
  2. package/dist/ast/utils.js +2 -2
  3. package/dist/ast/utils.js.map +1 -1
  4. package/dist/errors/errors.d.ts +9 -12
  5. package/dist/errors/errors.js +71 -68
  6. package/dist/errors/errors.js.map +1 -1
  7. package/dist/federation/types.d.ts +2 -2
  8. package/dist/index.d.ts +4 -2
  9. package/dist/index.js +4 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/normalization/normalization.d.ts +3 -1
  12. package/dist/normalization/normalization.js +8 -0
  13. package/dist/normalization/normalization.js.map +1 -1
  14. package/dist/normalization/types.d.ts +2 -2
  15. package/dist/resolvability-graph/graph-nodes.d.ts +1 -1
  16. package/dist/resolvability-graph/graph-nodes.js.map +1 -1
  17. package/dist/resolvability-graph/graph.d.ts +1 -2
  18. package/dist/resolvability-graph/graph.js.map +1 -1
  19. package/dist/resolvability-graph/utils.d.ts +1 -1
  20. package/dist/resolvability-graph/utils.js.map +1 -1
  21. package/dist/router-configuration/{router-configuration.d.ts → types.d.ts} +3 -4
  22. package/dist/router-configuration/types.js +3 -0
  23. package/dist/router-configuration/types.js.map +1 -0
  24. package/dist/router-configuration/utils.d.ts +3 -0
  25. package/dist/router-configuration/{router-configuration.js → utils.js} +9 -1
  26. package/dist/router-configuration/utils.js.map +1 -0
  27. package/dist/schema-building/types.d.ts +23 -7
  28. package/dist/schema-building/types.js.map +1 -1
  29. package/dist/schema-building/utils.d.ts +3 -6
  30. package/dist/schema-building/utils.js +11 -8
  31. package/dist/schema-building/utils.js.map +1 -1
  32. package/dist/subgraph/types.d.ts +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/dist/utils/composition-version.js +1 -1
  35. package/dist/utils/string-constants.d.ts +3 -2
  36. package/dist/utils/string-constants.js +5 -4
  37. package/dist/utils/string-constants.js.map +1 -1
  38. package/dist/utils/types.d.ts +38 -0
  39. package/dist/utils/utils.d.ts +0 -48
  40. package/dist/utils/utils.js +0 -10
  41. package/dist/utils/utils.js.map +1 -1
  42. package/dist/v1/federation/federation-factory.d.ts +4 -4
  43. package/dist/v1/federation/federation-factory.js +46 -35
  44. package/dist/v1/federation/federation-factory.js.map +1 -1
  45. package/dist/v1/federation/utils.d.ts +7 -8
  46. package/dist/v1/federation/utils.js +22 -20
  47. package/dist/v1/federation/utils.js.map +1 -1
  48. package/dist/v1/normalization/normalization-factory.d.ts +19 -22
  49. package/dist/v1/normalization/normalization-factory.js +568 -70
  50. package/dist/v1/normalization/normalization-factory.js.map +1 -1
  51. package/dist/v1/normalization/types.d.ts +49 -0
  52. package/dist/v1/normalization/types.js +3 -0
  53. package/dist/v1/normalization/types.js.map +1 -0
  54. package/dist/v1/normalization/utils.d.ts +7 -42
  55. package/dist/v1/normalization/utils.js +59 -382
  56. package/dist/v1/normalization/utils.js.map +1 -1
  57. package/dist/v1/normalization/walkers.js +36 -23
  58. package/dist/v1/normalization/walkers.js.map +1 -1
  59. package/dist/v1/utils/utils.d.ts +15 -12
  60. package/dist/v1/utils/utils.js +53 -47
  61. package/dist/v1/utils/utils.js.map +1 -1
  62. package/dist/v1/warnings/warnings.d.ts +4 -3
  63. package/dist/v1/warnings/warnings.js +47 -17
  64. package/dist/v1/warnings/warnings.js.map +1 -1
  65. package/dist/warnings/{warnings.js → types.js} +1 -1
  66. package/dist/warnings/types.js.map +1 -0
  67. package/package.json +2 -2
  68. package/dist/router-configuration/router-configuration.js.map +0 -1
  69. package/dist/warnings/warnings.js.map +0 -1
  70. /package/dist/warnings/{warnings.d.ts → types.d.ts} +0 -0
@@ -24,6 +24,8 @@ const integer_constants_1 = require("../utils/integer-constants");
24
24
  const string_constants_2 = require("../../utils/string-constants");
25
25
  const integer_constants_2 = require("../../utils/integer-constants");
26
26
  const utils_5 = require("../../utils/utils");
27
+ const index_1 = require("graphql/index");
28
+ const utils_6 = require("../../router-configuration/utils");
27
29
  function normalizeSubgraphFromString(subgraphSDL, noLocation = true) {
28
30
  const { error, documentNode } = (0, utils_1.safeParse)(subgraphSDL, noLocation);
29
31
  if (error || !documentNode) {
@@ -41,8 +43,8 @@ class NormalizationFactory {
41
43
  authorizationDataByParentTypeName = new Map();
42
44
  childName = '';
43
45
  concreteTypeNamesByAbstractTypeName = new Map();
44
- conditionalFieldDataByCoordinates = new Map();
45
- configurationDataByParentTypeName = new Map();
46
+ conditionalFieldDataByCoords = new Map();
47
+ configurationDataByTypeName = new Map();
46
48
  customDirectiveDefinitions = new Map();
47
49
  definedDirectiveNames = new Set();
48
50
  directiveDefinitionByDirectiveName = new Map();
@@ -52,7 +54,8 @@ class NormalizationFactory {
52
54
  entityDataByTypeName = new Map();
53
55
  entityInterfaceDataByTypeName = new Map();
54
56
  eventsConfigurations = new Map();
55
- unvalidatedExternalFieldCoords = new Set();
57
+ fieldSetDataByTypeName = new Map();
58
+ heirFieldAuthorizationDataByTypeName = new Map();
56
59
  interfaceTypeNamesWithAuthorizationDirectives = new Set();
57
60
  internalGraph;
58
61
  invalidConfigureDescriptionNodeDatas = [];
@@ -62,8 +65,7 @@ class NormalizationFactory {
62
65
  isParentObjectShareable = false;
63
66
  isSubgraphEventDrivenGraph = false;
64
67
  isSubgraphVersionTwo = false;
65
- fieldSetDataByTypeName = new Map();
66
- heirFieldAuthorizationDataByTypeName = new Map();
68
+ keyFieldSetDatasByTypeName = new Map();
67
69
  lastParentNodeKind = graphql_1.Kind.NULL;
68
70
  lastChildNodeKind = graphql_1.Kind.NULL;
69
71
  leafTypeNamesWithAuthorizationDirectives = new Set();
@@ -81,6 +83,7 @@ class NormalizationFactory {
81
83
  referencedTypeNames = new Set();
82
84
  renamedParentTypeName = '';
83
85
  subgraphName;
86
+ unvalidatedExternalFieldCoords = new Set();
84
87
  usesEdfsNatsStreamConfiguration = false;
85
88
  warnings = [];
86
89
  constructor(internalGraph, subgraphName) {
@@ -488,13 +491,13 @@ class NormalizationFactory {
488
491
  /*
489
492
  * @extends is not interpreted as an extension under the following circumstances:
490
493
  * 1. It's a root type
491
- * 2. It's a V2 subgraph
494
+ * 2. It's a V2 subgraph (but extends is temporarily propagated to handle @external key fields)
492
495
  * 3. And (of course) if @extends isn't defined at all
493
496
  */
494
- if (isRootType || this.isSubgraphVersionTwo || !directivesByDirectiveName.has(string_constants_2.EXTENDS)) {
497
+ if (isRootType || !directivesByDirectiveName.has(string_constants_2.EXTENDS)) {
495
498
  return types_1.ExtensionType.NONE;
496
499
  }
497
- // If it's a V1 subgraph and defines @extends, it is considered an extension across subgraphs
500
+ // If a V1 non-root Object defines @extends, it is considered an extension across subgraphs.
498
501
  return types_1.ExtensionType.EXTENDS;
499
502
  }
500
503
  setParentDataExtensionType(parentData, incomingExtensionType) {
@@ -725,27 +728,25 @@ class NormalizationFactory {
725
728
  }
726
729
  addFieldDataByNode(fieldDataByFieldName, node, argumentDataByArgumentName, directivesByDirectiveName) {
727
730
  const name = node.name.value;
728
- const fieldPath = `${this.originalParentTypeName}.${name}`;
729
- const isNodeExternalOrShareableResult = (0, utils_4.isNodeExternalOrShareable)(node, !this.isSubgraphVersionTwo, directivesByDirectiveName);
731
+ const fieldCoords = `${this.originalParentTypeName}.${name}`;
732
+ const { isExternal, isShareable } = (0, utils_4.isNodeExternalOrShareable)(node, !this.isSubgraphVersionTwo, directivesByDirectiveName);
730
733
  const fieldData = {
731
734
  argumentDataByArgumentName: argumentDataByArgumentName,
732
735
  configureDescriptionDataBySubgraphName: new Map(),
733
- isExternalBySubgraphName: new Map([
734
- [this.subgraphName, isNodeExternalOrShareableResult.isExternal],
736
+ externalFieldDataBySubgraphName: new Map([
737
+ [this.subgraphName, (0, utils_4.newExternalFieldData)(isExternal)],
735
738
  ]),
736
739
  isInaccessible: directivesByDirectiveName.has(string_constants_2.INACCESSIBLE),
737
- isShareableBySubgraphName: new Map([
738
- [this.subgraphName, isNodeExternalOrShareableResult.isShareable],
739
- ]),
740
+ isShareableBySubgraphName: new Map([[this.subgraphName, isShareable]]),
740
741
  kind: graphql_1.Kind.FIELD_DEFINITION,
741
742
  name,
742
743
  namedTypeName: (0, ast_1.getTypeNodeNamedTypeName)(node.type),
743
- node: (0, ast_1.getMutableFieldNode)(node, fieldPath, this.errors),
744
+ node: (0, ast_1.getMutableFieldNode)(node, fieldCoords, this.errors),
744
745
  originalParentTypeName: this.originalParentTypeName,
745
746
  persistedDirectivesData: (0, utils_4.newPersistedDirectivesData)(),
746
747
  renamedParentTypeName: this.renamedParentTypeName || this.originalParentTypeName,
747
748
  subgraphNames: new Set([this.subgraphName]),
748
- type: (0, ast_1.getMutableTypeNode)(node.type, fieldPath, this.errors),
749
+ type: (0, ast_1.getMutableTypeNode)(node.type, fieldCoords, this.errors),
749
750
  directivesByDirectiveName,
750
751
  description: (0, utils_1.formatDescription)(node.description),
751
752
  };
@@ -1031,23 +1032,24 @@ class NormalizationFactory {
1031
1032
  this.extractConfigureDescriptionsData(newParentData);
1032
1033
  this.parentDefinitionDataByTypeName.set(typeName, newParentData);
1033
1034
  }
1034
- extractKeyFieldSets(node, keyFieldSetData) {
1035
- const isUnresolvableByRawKeyFieldSet = keyFieldSetData.isUnresolvableByKeyFieldSet;
1035
+ extractKeyFieldSets(node, keyFieldSetDataByFieldSet) {
1036
1036
  const parentTypeName = node.name.value;
1037
1037
  if (!node.directives?.length) {
1038
1038
  // This should never happen
1039
1039
  this.errors.push((0, errors_1.expectedEntityError)(parentTypeName));
1040
1040
  return;
1041
1041
  }
1042
- // validation happens elsewhere
1042
+ // full validation happens elsewhere
1043
+ let keyNumber = 0;
1043
1044
  for (const directive of node.directives) {
1044
1045
  if (directive.name.value !== string_constants_2.KEY) {
1045
1046
  continue;
1046
1047
  }
1048
+ keyNumber += 1;
1047
1049
  if (!directive.arguments || directive.arguments.length < 1) {
1048
1050
  continue;
1049
1051
  }
1050
- let keyFieldSet;
1052
+ let rawFieldSet;
1051
1053
  let isUnresolvable = false;
1052
1054
  for (const arg of directive.arguments) {
1053
1055
  if (arg.name.value === string_constants_2.RESOLVABLE) {
@@ -1057,18 +1059,339 @@ class NormalizationFactory {
1057
1059
  continue;
1058
1060
  }
1059
1061
  if (arg.name.value !== string_constants_2.FIELDS) {
1060
- keyFieldSet = undefined;
1062
+ rawFieldSet = undefined;
1061
1063
  break;
1062
1064
  }
1063
1065
  if (arg.value.kind !== graphql_1.Kind.STRING) {
1064
- keyFieldSet = undefined;
1066
+ rawFieldSet = undefined;
1065
1067
  break;
1066
1068
  }
1067
- keyFieldSet = arg.value.value;
1069
+ rawFieldSet = arg.value.value;
1068
1070
  }
1069
- if (keyFieldSet !== undefined) {
1070
- isUnresolvableByRawKeyFieldSet.set(keyFieldSet, isUnresolvable);
1071
+ if (rawFieldSet === undefined) {
1072
+ continue;
1071
1073
  }
1074
+ const { error, documentNode } = (0, utils_1.safeParse)('{' + rawFieldSet + '}');
1075
+ if (error || !documentNode) {
1076
+ this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_2.KEY, parentTypeName, (0, utils_5.numberToOrdinal)(keyNumber), [
1077
+ (0, errors_1.unparsableFieldSetErrorMessage)(rawFieldSet, error),
1078
+ ]));
1079
+ continue;
1080
+ }
1081
+ const normalizedFieldSet = (0, utils_2.getNormalizedFieldSet)(documentNode);
1082
+ const keyFieldSetData = keyFieldSetDataByFieldSet.get(normalizedFieldSet);
1083
+ if (keyFieldSetData) {
1084
+ // Duplicate keys should potentially be a warning. For now, simply propagate if it's resolvable.
1085
+ keyFieldSetData.isUnresolvable ||= isUnresolvable;
1086
+ }
1087
+ else {
1088
+ keyFieldSetDataByFieldSet.set(normalizedFieldSet, {
1089
+ documentNode,
1090
+ isUnresolvable,
1091
+ normalizedFieldSet,
1092
+ rawFieldSet,
1093
+ });
1094
+ }
1095
+ }
1096
+ }
1097
+ getFieldSetParent(isProvides, parentData, fieldName, parentTypeName) {
1098
+ if (!isProvides) {
1099
+ return { fieldSetParentData: parentData };
1100
+ }
1101
+ const fieldData = (0, utils_5.getOrThrowError)(parentData.fieldDataByFieldName, fieldName, `${parentTypeName}.fieldDataByFieldName`);
1102
+ const fieldNamedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
1103
+ const namedTypeData = this.parentDefinitionDataByTypeName.get(fieldNamedTypeName);
1104
+ // This error should never happen
1105
+ if (!namedTypeData) {
1106
+ return {
1107
+ errorString: (0, errors_1.unknownNamedTypeErrorMessage)(`${parentTypeName}.${fieldName}`, fieldNamedTypeName),
1108
+ };
1109
+ }
1110
+ if (namedTypeData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION && namedTypeData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
1111
+ return {
1112
+ errorString: (0, errors_1.incompatibleTypeWithProvidesErrorMessage)(`${parentTypeName}.${fieldName}`, fieldNamedTypeName),
1113
+ };
1114
+ }
1115
+ return { fieldSetParentData: namedTypeData };
1116
+ }
1117
+ validateConditionalFieldSet(selectionSetParentData, fieldSet, directiveFieldName, isProvides, directiveParentTypeName) {
1118
+ // Create a new selection set so that the value can be parsed as a new DocumentNode
1119
+ const { error, documentNode } = (0, utils_1.safeParse)('{' + fieldSet + '}');
1120
+ if (error || !documentNode) {
1121
+ return { errorMessages: [(0, errors_1.unparsableFieldSetErrorMessage)(fieldSet, error)] };
1122
+ }
1123
+ const nf = this;
1124
+ const parentDatas = [selectionSetParentData];
1125
+ const directiveName = (0, utils_2.getConditionalFieldSetDirectiveName)(isProvides);
1126
+ const definedFields = [];
1127
+ const directiveCoords = `${directiveParentTypeName}.${directiveFieldName}`;
1128
+ const fieldCoordsPath = (0, utils_2.getInitialFieldCoordsPath)(isProvides, directiveCoords);
1129
+ const fieldPath = [directiveFieldName];
1130
+ const externalAncestors = new Set();
1131
+ const errorMessages = [];
1132
+ let currentDepth = -1;
1133
+ let shouldDefineSelectionSet = true;
1134
+ let lastFieldName = directiveFieldName;
1135
+ let hasConditionalField = false;
1136
+ (0, index_1.visit)(documentNode, {
1137
+ Argument: {
1138
+ enter() {
1139
+ return false;
1140
+ },
1141
+ },
1142
+ Field: {
1143
+ enter(node) {
1144
+ const parentData = parentDatas[currentDepth];
1145
+ const parentTypeName = parentData.name;
1146
+ if (parentData.kind === graphql_1.Kind.UNION_TYPE_DEFINITION) {
1147
+ errorMessages.push((0, errors_1.invalidSelectionOnUnionErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName));
1148
+ return index_1.BREAK;
1149
+ }
1150
+ const fieldName = node.name.value;
1151
+ const currentFieldCoords = `${parentTypeName}.${fieldName}`;
1152
+ nf.unvalidatedExternalFieldCoords.delete(currentFieldCoords);
1153
+ // If an object-like was just visited, a selection set should have been entered
1154
+ if (shouldDefineSelectionSet) {
1155
+ errorMessages.push((0, errors_1.invalidSelectionSetErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, (0, utils_5.kindToTypeString)(parentData.kind)));
1156
+ return index_1.BREAK;
1157
+ }
1158
+ fieldCoordsPath.push(currentFieldCoords);
1159
+ fieldPath.push(fieldName);
1160
+ lastFieldName = fieldName;
1161
+ const fieldData = parentData.fieldDataByFieldName.get(fieldName);
1162
+ // undefined if the field does not exist on the parent
1163
+ if (!fieldData) {
1164
+ errorMessages.push((0, errors_1.undefinedFieldInFieldSetErrorMessage)(fieldSet, parentTypeName, fieldName));
1165
+ return index_1.BREAK;
1166
+ }
1167
+ if (definedFields[currentDepth].has(fieldName)) {
1168
+ errorMessages.push((0, errors_1.duplicateFieldInFieldSetErrorMessage)(fieldSet, currentFieldCoords));
1169
+ return index_1.BREAK;
1170
+ }
1171
+ definedFields[currentDepth].add(fieldName);
1172
+ const { isDefinedExternal, isUnconditionallyProvided } = (0, utils_5.getOrThrowError)(fieldData.externalFieldDataBySubgraphName, nf.subgraphName, `${currentFieldCoords}.externalFieldDataBySubgraphName`);
1173
+ const isFieldConditional = isDefinedExternal && !isUnconditionallyProvided;
1174
+ if (!isUnconditionallyProvided) {
1175
+ hasConditionalField = true;
1176
+ }
1177
+ const namedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
1178
+ // The child could itself be a parent
1179
+ const namedTypeData = nf.parentDefinitionDataByTypeName.get(namedTypeName);
1180
+ // The base scalars are not in the parents map
1181
+ if (constants_1.BASE_SCALARS.has(namedTypeName) ||
1182
+ namedTypeData?.kind === graphql_1.Kind.SCALAR_TYPE_DEFINITION ||
1183
+ namedTypeData?.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION) {
1184
+ if (externalAncestors.size < 1 && !isDefinedExternal) {
1185
+ if (nf.isSubgraphVersionTwo) {
1186
+ nf.errors.push((0, errors_1.nonExternalConditionalFieldError)(directiveCoords, nf.subgraphName, currentFieldCoords, fieldSet, directiveName));
1187
+ return;
1188
+ }
1189
+ /* In V1, @requires and @provides do not need to declare any part of the field set @external.
1190
+ * It would appear that any such non-external fields are treated as if they are non-conditionally provided.
1191
+ * */
1192
+ nf.warnings.push((0, warnings_1.nonExternalConditionalFieldWarning)(directiveCoords, nf.subgraphName, currentFieldCoords, fieldSet, directiveName));
1193
+ return;
1194
+ }
1195
+ if (externalAncestors.size < 1 && isUnconditionallyProvided) {
1196
+ // V2 subgraphs return an error when an external key field on an entity extension is provided.
1197
+ if (nf.isSubgraphVersionTwo) {
1198
+ errorMessages.push((0, errors_1.fieldAlreadyProvidedErrorMessage)(currentFieldCoords, nf.subgraphName, directiveName));
1199
+ }
1200
+ else {
1201
+ nf.warnings.push((0, warnings_1.fieldAlreadyProvidedWarning)(currentFieldCoords, directiveName, directiveCoords, nf.subgraphName));
1202
+ }
1203
+ return;
1204
+ }
1205
+ // @TODO re-assess in v2 because this would be breaking for @provides in v1
1206
+ if (!isFieldConditional && !isProvides) {
1207
+ // Do not add unnecessary @requires configurations
1208
+ return;
1209
+ }
1210
+ const conditionalFieldData = (0, utils_5.getValueOrDefault)(nf.conditionalFieldDataByCoords, currentFieldCoords, utils_4.newConditionalFieldData);
1211
+ const fieldSetCondition = (0, utils_6.newFieldSetConditionData)({
1212
+ fieldCoordinatesPath: [...fieldCoordsPath],
1213
+ fieldPath: [...fieldPath],
1214
+ });
1215
+ isProvides
1216
+ ? conditionalFieldData.providedBy.push(fieldSetCondition)
1217
+ : conditionalFieldData.requiredBy.push(fieldSetCondition);
1218
+ return;
1219
+ }
1220
+ if (!namedTypeData) {
1221
+ // Should not be possible to receive this error
1222
+ errorMessages.push((0, errors_1.unknownTypeInFieldSetErrorMessage)(fieldSet, currentFieldCoords, namedTypeName));
1223
+ return index_1.BREAK;
1224
+ }
1225
+ // TODO isFieldConditional
1226
+ if (isDefinedExternal) {
1227
+ if (isProvides) {
1228
+ (0, utils_5.getValueOrDefault)(nf.conditionalFieldDataByCoords, currentFieldCoords, utils_4.newConditionalFieldData).providedBy.push((0, utils_6.newFieldSetConditionData)({
1229
+ fieldCoordinatesPath: [...fieldCoordsPath],
1230
+ fieldPath: [...fieldPath],
1231
+ }));
1232
+ }
1233
+ externalAncestors.add(currentFieldCoords);
1234
+ }
1235
+ if (namedTypeData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
1236
+ namedTypeData.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION ||
1237
+ namedTypeData.kind === graphql_1.Kind.UNION_TYPE_DEFINITION) {
1238
+ shouldDefineSelectionSet = true;
1239
+ parentDatas.push(namedTypeData);
1240
+ return;
1241
+ }
1242
+ },
1243
+ leave() {
1244
+ externalAncestors.delete(fieldCoordsPath.pop() || '');
1245
+ fieldPath.pop();
1246
+ },
1247
+ },
1248
+ InlineFragment: {
1249
+ enter(node) {
1250
+ const parentData = parentDatas[currentDepth];
1251
+ const parentTypeName = parentData.name;
1252
+ const fieldCoordinates = fieldCoordsPath.length < 1 ? selectionSetParentData.name : fieldCoordsPath[fieldCoordsPath.length - 1];
1253
+ if (!node.typeCondition) {
1254
+ errorMessages.push((0, errors_1.inlineFragmentWithoutTypeConditionErrorMessage)(fieldSet, fieldCoordinates));
1255
+ return index_1.BREAK;
1256
+ }
1257
+ const typeConditionName = node.typeCondition.name.value;
1258
+ // It's possible to infinitely define fragments
1259
+ if (typeConditionName === parentTypeName) {
1260
+ parentDatas.push(parentData);
1261
+ shouldDefineSelectionSet = true;
1262
+ return;
1263
+ }
1264
+ if (!(0, utils_1.isKindAbstract)(parentData.kind)) {
1265
+ errorMessages.push((0, errors_1.invalidInlineFragmentTypeErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, parentTypeName));
1266
+ return index_1.BREAK;
1267
+ }
1268
+ const fragmentNamedTypeData = nf.parentDefinitionDataByTypeName.get(typeConditionName);
1269
+ if (!fragmentNamedTypeData) {
1270
+ errorMessages.push((0, errors_1.unknownInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, typeConditionName));
1271
+ return index_1.BREAK;
1272
+ }
1273
+ shouldDefineSelectionSet = true;
1274
+ switch (fragmentNamedTypeData.kind) {
1275
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: {
1276
+ if (!fragmentNamedTypeData.implementedInterfaceTypeNames.has(parentTypeName)) {
1277
+ break;
1278
+ }
1279
+ parentDatas.push(fragmentNamedTypeData);
1280
+ return;
1281
+ }
1282
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION: {
1283
+ const concreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(parentTypeName);
1284
+ if (!concreteTypeNames || !concreteTypeNames.has(typeConditionName)) {
1285
+ break;
1286
+ }
1287
+ parentDatas.push(fragmentNamedTypeData);
1288
+ return;
1289
+ }
1290
+ case graphql_1.Kind.UNION_TYPE_DEFINITION: {
1291
+ parentDatas.push(fragmentNamedTypeData);
1292
+ return;
1293
+ }
1294
+ default: {
1295
+ errorMessages.push((0, errors_1.invalidInlineFragmentTypeConditionTypeErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, typeConditionName, (0, utils_5.kindToTypeString)(fragmentNamedTypeData.kind)));
1296
+ return index_1.BREAK;
1297
+ }
1298
+ }
1299
+ errorMessages.push((0, errors_1.invalidInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, (0, utils_5.kindToTypeString)(parentData.kind), parentTypeName));
1300
+ return index_1.BREAK;
1301
+ },
1302
+ },
1303
+ SelectionSet: {
1304
+ enter() {
1305
+ if (!shouldDefineSelectionSet) {
1306
+ const parentData = parentDatas[currentDepth];
1307
+ if (parentData.kind === graphql_1.Kind.UNION_TYPE_DEFINITION) {
1308
+ // Should never happen
1309
+ errorMessages.push((0, errors_1.unparsableFieldSetSelectionErrorMessage)(fieldSet, lastFieldName));
1310
+ return index_1.BREAK;
1311
+ }
1312
+ const fieldData = parentData.fieldDataByFieldName.get(lastFieldName);
1313
+ if (!fieldData) {
1314
+ errorMessages.push((0, errors_1.undefinedFieldInFieldSetErrorMessage)(fieldSet, parentData.name, lastFieldName));
1315
+ return index_1.BREAK;
1316
+ }
1317
+ const fieldNamedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
1318
+ // If the child is not found, it's a base scalar. Undefined types would have already been handled.
1319
+ const namedTypeData = nf.parentDefinitionDataByTypeName.get(fieldNamedTypeName);
1320
+ const childKind = namedTypeData ? namedTypeData.kind : graphql_1.Kind.SCALAR_TYPE_DEFINITION;
1321
+ errorMessages.push((0, errors_1.invalidSelectionSetDefinitionErrorMessage)(fieldSet, fieldCoordsPath, fieldNamedTypeName, (0, utils_5.kindToTypeString)(childKind)));
1322
+ return index_1.BREAK;
1323
+ }
1324
+ currentDepth += 1;
1325
+ shouldDefineSelectionSet = false;
1326
+ if (currentDepth < 0 || currentDepth >= parentDatas.length) {
1327
+ errorMessages.push((0, errors_1.unparsableFieldSetSelectionErrorMessage)(fieldSet, lastFieldName));
1328
+ return index_1.BREAK;
1329
+ }
1330
+ definedFields.push(new Set());
1331
+ },
1332
+ leave() {
1333
+ if (shouldDefineSelectionSet) {
1334
+ const parentData = parentDatas[currentDepth + 1];
1335
+ errorMessages.push((0, errors_1.invalidSelectionSetErrorMessage)(fieldSet, fieldCoordsPath, parentData.name, (0, utils_5.kindToTypeString)(parentData.kind)));
1336
+ shouldDefineSelectionSet = false;
1337
+ }
1338
+ // Empty selection sets would be a parse error, so it is unnecessary to handle them
1339
+ currentDepth -= 1;
1340
+ parentDatas.pop();
1341
+ definedFields.pop();
1342
+ },
1343
+ },
1344
+ });
1345
+ if (errorMessages.length > 0 || !hasConditionalField) {
1346
+ return { errorMessages };
1347
+ }
1348
+ return {
1349
+ configuration: { fieldName: directiveFieldName, selectionSet: (0, utils_2.getNormalizedFieldSet)(documentNode) },
1350
+ errorMessages,
1351
+ };
1352
+ }
1353
+ validateProvidesOrRequires(parentData, fieldSetByFieldName, isProvides) {
1354
+ const allErrorMessages = [];
1355
+ const configurations = [];
1356
+ const parentTypeName = (0, utils_4.getParentTypeName)(parentData);
1357
+ for (const [fieldName, fieldSet] of fieldSetByFieldName) {
1358
+ /* It is possible to encounter a field before encountering the type definition.
1359
+ Consequently, at that time, it is unknown whether the named type is an entity.
1360
+ If it isn't, the @provides directive does not make sense and can be ignored.
1361
+ */
1362
+ const { fieldSetParentData, errorString } = this.getFieldSetParent(isProvides, parentData, fieldName, parentTypeName);
1363
+ const fieldCoords = `${parentTypeName}.${fieldName}`;
1364
+ if (errorString) {
1365
+ allErrorMessages.push(errorString);
1366
+ continue;
1367
+ }
1368
+ if (!fieldSetParentData) {
1369
+ continue;
1370
+ }
1371
+ const { errorMessages, configuration } = this.validateConditionalFieldSet(fieldSetParentData, fieldSet, fieldName, isProvides, parentTypeName);
1372
+ /*
1373
+ * It is possible to return no error messages nor configuration if the @provides or @requires directive is
1374
+ * considered completely redundant, i.e.,:
1375
+ * 1. All fields to which the directive refers are declared @external but are also key fields on an entity extension.
1376
+ * 2. The subgraph is V1 and all fields to which the directive refers are not declared @external.
1377
+ * In these cases, the fields are considered unconditionally provided.
1378
+ * If all the fields to which the directive refers are unconditionally provided, the directive is redundant.
1379
+ * For V2 subgraphs, this will propagate as an error; for V1 subgraphs, this will propagate as a warning.
1380
+ * */
1381
+ if (errorMessages.length > 0) {
1382
+ allErrorMessages.push(` On field "${fieldCoords}":\n -` + errorMessages.join(string_constants_2.HYPHEN_JOIN));
1383
+ continue;
1384
+ }
1385
+ if (configuration) {
1386
+ configurations.push(configuration);
1387
+ }
1388
+ }
1389
+ if (allErrorMessages.length > 0) {
1390
+ this.errors.push((0, errors_1.invalidProvidesOrRequiresDirectivesError)((0, utils_2.getConditionalFieldSetDirectiveName)(isProvides), allErrorMessages));
1391
+ return;
1392
+ }
1393
+ if (configurations.length > 0) {
1394
+ return configurations;
1072
1395
  }
1073
1396
  }
1074
1397
  validateInterfaceImplementations(data) {
@@ -1575,11 +1898,11 @@ class NormalizationFactory {
1575
1898
  }
1576
1899
  }
1577
1900
  validateEventDrivenKeyDefinition(typeName, invalidKeyFieldSetsByEntityTypeName) {
1578
- const keyFieldSetData = this.keyFieldSetDataByTypeName.get(typeName);
1579
- if (!keyFieldSetData) {
1901
+ const keyFieldSetDataByFieldSet = this.keyFieldSetDatasByTypeName.get(typeName);
1902
+ if (!keyFieldSetDataByFieldSet) {
1580
1903
  return;
1581
1904
  }
1582
- for (const [keyFieldSet, isUnresolvable] of keyFieldSetData.isUnresolvableByKeyFieldSet) {
1905
+ for (const [keyFieldSet, { isUnresolvable }] of keyFieldSetDataByFieldSet) {
1583
1906
  if (isUnresolvable) {
1584
1907
  continue;
1585
1908
  }
@@ -1590,7 +1913,7 @@ class NormalizationFactory {
1590
1913
  for (const [fieldName, fieldData] of fieldDataByFieldName) {
1591
1914
  const fieldPath = `${fieldData.originalParentTypeName}.${fieldName}`;
1592
1915
  if (keyFieldNames.has(fieldName)) {
1593
- if (!fieldData.isExternalBySubgraphName.get(this.subgraphName)) {
1916
+ if (!fieldData.externalFieldDataBySubgraphName.get(this.subgraphName)?.isDefinedExternal) {
1594
1917
  nonExternalKeyFieldNameByFieldPath.set(fieldPath, fieldName);
1595
1918
  }
1596
1919
  continue;
@@ -1765,22 +2088,20 @@ class NormalizationFactory {
1765
2088
  this.internalGraph.addEdge(this.internalGraph.addOrUpdateNode(unionTypeName, { isAbstract: true }), this.internalGraph.addOrUpdateNode(memberTypeName), memberTypeName, true);
1766
2089
  }
1767
2090
  }
1768
- validateAndAddKeyToConfiguration(parentData, keyFieldSetData) {
1769
- const configurationData = (0, utils_5.getOrThrowError)(this.configurationDataByParentTypeName, (0, utils_4.getParentTypeName)(parentData), 'configurationDataByParentTypeName');
1770
- const keys = (0, utils_2.validateKeyFieldSets)(this, parentData, keyFieldSetData.isUnresolvableByKeyFieldSet, configurationData.fieldNames);
1771
- if (keys) {
1772
- configurationData.keys = keys;
1773
- }
1774
- }
1775
- validateAndAddKeysToConfiguration() {
1776
- for (const [parentTypeName, keyFieldSetData] of this.keyFieldSetDataByTypeName) {
1777
- const parentData = this.parentDefinitionDataByTypeName.get(parentTypeName);
2091
+ addValidKeyFieldSetConfigurations() {
2092
+ for (const [entityTypeName, keyFieldSetDataByFieldSet] of this.keyFieldSetDatasByTypeName) {
2093
+ const parentData = this.parentDefinitionDataByTypeName.get(entityTypeName);
1778
2094
  if (!parentData ||
1779
2095
  (parentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && parentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
1780
- this.errors.push((0, errors_1.undefinedObjectLikeParentError)(parentTypeName));
2096
+ this.errors.push((0, errors_1.undefinedCompositeOutputTypeError)(entityTypeName));
1781
2097
  continue;
1782
2098
  }
1783
- this.validateAndAddKeyToConfiguration(parentData, keyFieldSetData);
2099
+ const typeName = (0, utils_4.getParentTypeName)(parentData);
2100
+ const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, typeName, () => (0, utils_6.newConfigurationData)(true, typeName));
2101
+ const keys = (0, utils_2.validateKeyFieldSets)(this, parentData, keyFieldSetDataByFieldSet, configurationData.fieldNames);
2102
+ if (keys) {
2103
+ configurationData.keys = keys;
2104
+ }
1784
2105
  }
1785
2106
  }
1786
2107
  getValidFlattenedDirectiveArray(directivesByDirectiveName, directiveCoords) {
@@ -1862,6 +2183,179 @@ class NormalizationFactory {
1862
2183
  unionDefinitionData.node.types = (0, utils_3.mapToArrayOfValues)(unionDefinitionData.memberByMemberTypeName);
1863
2184
  return unionDefinitionData.node;
1864
2185
  }
2186
+ evaluateExternalKeyFields() {
2187
+ const invalidTypeNames = [];
2188
+ for (const [entityTypeName, keyFieldSetDataByFieldSet] of this.keyFieldSetDatasByTypeName) {
2189
+ const entityParentData = this.parentDefinitionDataByTypeName.get(entityTypeName);
2190
+ // The parent data should always exist.
2191
+ if (!entityParentData ||
2192
+ (entityParentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
2193
+ entityParentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
2194
+ // If somehow the parent data does not exist, prevent the same error occurring by removing that type from the map.
2195
+ invalidTypeNames.push(entityTypeName);
2196
+ this.errors.push((0, errors_1.undefinedCompositeOutputTypeError)(entityTypeName));
2197
+ continue;
2198
+ }
2199
+ const nf = this;
2200
+ for (const keyFieldSetData of keyFieldSetDataByFieldSet.values()) {
2201
+ const parentDatas = [entityParentData];
2202
+ // Entity extension fields are effectively never @external, so propagate a warning.
2203
+ const externalExtensionFieldCoordsByRawFieldSet = new Map();
2204
+ let currentDepth = -1;
2205
+ let shouldDefineSelectionSet = true;
2206
+ (0, index_1.visit)(keyFieldSetData.documentNode, {
2207
+ Argument: {
2208
+ enter() {
2209
+ return index_1.BREAK;
2210
+ },
2211
+ },
2212
+ Field: {
2213
+ enter(node) {
2214
+ const parentData = parentDatas[currentDepth];
2215
+ const parentTypeName = parentData.name;
2216
+ // If a composite type was just visited, a selection set should have been entered
2217
+ if (shouldDefineSelectionSet) {
2218
+ return index_1.BREAK;
2219
+ }
2220
+ const fieldName = node.name.value;
2221
+ const fieldCoords = `${parentTypeName}.${fieldName}`;
2222
+ // If a field declared @external is a key field, it is valid use of @external.
2223
+ nf.unvalidatedExternalFieldCoords.delete(fieldCoords);
2224
+ const fieldData = parentData.fieldDataByFieldName.get(fieldName);
2225
+ // undefined if the field does not exist on the parent
2226
+ if (!fieldData || fieldData.argumentDataByArgumentName.size) {
2227
+ return index_1.BREAK;
2228
+ }
2229
+ // Fields that form part of an entity key are intrinsically shareable
2230
+ fieldData.isShareableBySubgraphName.set(nf.subgraphName, true);
2231
+ /* !!! IMPORTANT NOTE REGARDING INCONSISTENT APOLLO BEHAVIOUR !!!
2232
+ * V1 entities with "@extends" may define unique nested key fields as @external without restriction.
2233
+ * However, V1 entity extensions (with the "extend" keyword) cannot do this.
2234
+ * Instead, an error is returned stating that there must be a non-external definition of the field.
2235
+ * This inconsistency in behaviour appears to be a bug.
2236
+ * It doesn't make much sense to enforce "origin fields" only sometimes (or ever, honestly).
2237
+ * Consequently, a decision was made not ever to enforce meaningless origin fields for extensions.
2238
+ *
2239
+ * In the event the nested key field is not unique, the error may propagate as a field resolvability
2240
+ * error, e.g., unable to use the nested @external key field to satisfy a field set in another subgraph.
2241
+ *
2242
+ * In addition, the nested key field of a V2 entity extension (either "@extends" or "extend" keyword)
2243
+ * are considered unconditionally provided regardless of the presence of "@external".
2244
+ *
2245
+ * However, if the subgraph is an EDG, the @external state should be kept regardless of extension.
2246
+ * */
2247
+ const externalFieldData = fieldData.externalFieldDataBySubgraphName.get(nf.subgraphName);
2248
+ if (nf.edfsDirectiveReferences.size < 1 &&
2249
+ externalFieldData &&
2250
+ externalFieldData.isDefinedExternal &&
2251
+ !externalFieldData.isUnconditionallyProvided) {
2252
+ /*
2253
+ * The key field is unconditionally provided if all the following are true:
2254
+ * 1. The root entity is an extension type.
2255
+ * 2. The field is also a key field for the parent entity.
2256
+ */
2257
+ if (entityParentData.extensionType !== types_1.ExtensionType.NONE) {
2258
+ externalFieldData.isUnconditionallyProvided = true;
2259
+ (0, utils_5.getValueOrDefault)(externalExtensionFieldCoordsByRawFieldSet, keyFieldSetData.rawFieldSet, () => new Set()).add(fieldCoords);
2260
+ }
2261
+ }
2262
+ (0, utils_5.getValueOrDefault)(nf.keyFieldNamesByParentTypeName, parentTypeName, () => new Set()).add(fieldName);
2263
+ const namedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
2264
+ // The base scalars are not in the parents map
2265
+ if (constants_1.BASE_SCALARS.has(namedTypeName)) {
2266
+ return;
2267
+ }
2268
+ // The child could itself be a parent
2269
+ const namedTypeData = nf.parentDefinitionDataByTypeName.get(namedTypeName);
2270
+ if (!namedTypeData) {
2271
+ return index_1.BREAK;
2272
+ }
2273
+ if (namedTypeData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
2274
+ shouldDefineSelectionSet = true;
2275
+ parentDatas.push(namedTypeData);
2276
+ return;
2277
+ }
2278
+ // interfaces and unions are invalid in a key directive
2279
+ if ((0, utils_1.isKindAbstract)(namedTypeData.kind)) {
2280
+ return index_1.BREAK;
2281
+ }
2282
+ },
2283
+ },
2284
+ InlineFragment: {
2285
+ enter() {
2286
+ return index_1.BREAK;
2287
+ },
2288
+ },
2289
+ SelectionSet: {
2290
+ enter() {
2291
+ if (!shouldDefineSelectionSet) {
2292
+ return index_1.BREAK;
2293
+ }
2294
+ currentDepth += 1;
2295
+ shouldDefineSelectionSet = false;
2296
+ if (currentDepth < 0 || currentDepth >= parentDatas.length) {
2297
+ return index_1.BREAK;
2298
+ }
2299
+ },
2300
+ leave() {
2301
+ if (shouldDefineSelectionSet) {
2302
+ shouldDefineSelectionSet = false;
2303
+ }
2304
+ // Empty selection sets would be a parse error, so it is unnecessary to handle them
2305
+ currentDepth -= 1;
2306
+ parentDatas.pop();
2307
+ },
2308
+ },
2309
+ });
2310
+ if (externalExtensionFieldCoordsByRawFieldSet.size < 1) {
2311
+ continue;
2312
+ }
2313
+ for (const [rawFieldSet, fieldCoords] of externalExtensionFieldCoordsByRawFieldSet) {
2314
+ this.warnings.push((0, warnings_1.externalEntityExtensionKeyFieldWarning)(entityParentData.name, rawFieldSet, [...fieldCoords], this.subgraphName));
2315
+ }
2316
+ }
2317
+ }
2318
+ for (const invalidTypeName of invalidTypeNames) {
2319
+ this.keyFieldSetDatasByTypeName.delete(invalidTypeName);
2320
+ }
2321
+ }
2322
+ addValidConditionalFieldSetConfigurations() {
2323
+ for (const [typeName, fieldSetData] of this.fieldSetDataByTypeName) {
2324
+ const parentData = this.parentDefinitionDataByTypeName.get(typeName);
2325
+ if (!parentData ||
2326
+ (parentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && parentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
2327
+ this.errors.push((0, errors_1.undefinedCompositeOutputTypeError)(typeName));
2328
+ continue;
2329
+ }
2330
+ const parentTypeName = (0, utils_4.getParentTypeName)(parentData);
2331
+ const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, parentTypeName, () => (0, utils_6.newConfigurationData)(false, parentTypeName));
2332
+ const provides = this.validateProvidesOrRequires(parentData, fieldSetData.provides, true);
2333
+ if (provides) {
2334
+ configurationData.provides = provides;
2335
+ }
2336
+ const requires = this.validateProvidesOrRequires(parentData, fieldSetData.requires, false);
2337
+ if (requires) {
2338
+ configurationData.requires = requires;
2339
+ }
2340
+ }
2341
+ }
2342
+ addFieldNamesToConfigurationData(fieldDataByFieldName, configurationData) {
2343
+ const externalFieldNames = new Set();
2344
+ for (const [fieldName, fieldData] of fieldDataByFieldName) {
2345
+ const externalFieldData = fieldData.externalFieldDataBySubgraphName.get(this.subgraphName);
2346
+ if (!externalFieldData || externalFieldData.isUnconditionallyProvided) {
2347
+ configurationData.fieldNames.add(fieldName);
2348
+ continue;
2349
+ }
2350
+ externalFieldNames.add(fieldName);
2351
+ if (this.edfsDirectiveReferences.size > 0) {
2352
+ configurationData.fieldNames.add(fieldName);
2353
+ }
2354
+ }
2355
+ if (externalFieldNames.size > 0) {
2356
+ configurationData.externalFieldNames = externalFieldNames;
2357
+ }
2358
+ }
1865
2359
  normalize(document) {
1866
2360
  /* factory.allDirectiveDefinitions is initialized with v1 directive definitions, and v2 definitions are only added
1867
2361
  after the visitor has visited the entire schema and the subgraph is known to be a V2 graph. Consequently,
@@ -1953,6 +2447,8 @@ class NormalizationFactory {
1953
2447
  this.errors.push((0, errors_1.configureDescriptionNoDescriptionError)((0, utils_5.kindToTypeString)(data.kind), data.name));
1954
2448
  }
1955
2449
  }
2450
+ // Check all key field sets for @external fields to assess whether they are conditional
2451
+ this.evaluateExternalKeyFields();
1956
2452
  for (const [parentTypeName, parentDefinitionData] of this.parentDefinitionDataByTypeName) {
1957
2453
  switch (parentDefinitionData.kind) {
1958
2454
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
@@ -1976,6 +2472,10 @@ class NormalizationFactory {
1976
2472
  const isEntity = this.entityDataByTypeName.has(parentTypeName);
1977
2473
  const operationTypeNode = this.operationTypeNodeByTypeName.get(parentTypeName);
1978
2474
  const isObject = parentDefinitionData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION;
2475
+ if (this.isSubgraphVersionTwo && parentDefinitionData.extensionType === types_1.ExtensionType.EXTENDS) {
2476
+ // @extends is essentially ignored in V2. It was only propagated to handle @external key fields.
2477
+ parentDefinitionData.extensionType = types_1.ExtensionType.NONE;
2478
+ }
1979
2479
  if (operationTypeNode) {
1980
2480
  parentDefinitionData.fieldDataByFieldName.delete(string_constants_2.SERVICE_FIELD);
1981
2481
  parentDefinitionData.fieldDataByFieldName.delete(string_constants_2.ENTITIES_FIELD);
@@ -1985,7 +2485,7 @@ class NormalizationFactory {
1985
2485
  if (this.parentsWithChildArguments.has(parentTypeName) || !isObject) {
1986
2486
  const externalInterfaceFieldNames = [];
1987
2487
  for (const [fieldName, fieldData] of parentDefinitionData.fieldDataByFieldName) {
1988
- if (!isObject && fieldData.isExternalBySubgraphName.get(this.subgraphName)) {
2488
+ if (!isObject && fieldData.externalFieldDataBySubgraphName.get(this.subgraphName)?.isDefinedExternal) {
1989
2489
  externalInterfaceFieldNames.push(fieldName);
1990
2490
  }
1991
2491
  // Arguments can only be fully validated once all parents types are known
@@ -1998,18 +2498,15 @@ class NormalizationFactory {
1998
2498
  : this.warnings.push((0, warnings_1.externalInterfaceFieldsWarning)(this.subgraphName, parentTypeName, externalInterfaceFieldNames));
1999
2499
  }
2000
2500
  }
2001
- const newParentTypeName = parentDefinitionData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION
2002
- ? parentDefinitionData.renamedTypeName || parentTypeName
2003
- : parentTypeName;
2004
- const configurationData = {
2005
- fieldNames: new Set(),
2006
- isRootNode: isEntity,
2007
- typeName: newParentTypeName,
2008
- };
2501
+ const newParentTypeName = (0, utils_4.getParentTypeName)(parentDefinitionData);
2502
+ const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, newParentTypeName, () => (0, utils_6.newConfigurationData)(isEntity, parentTypeName));
2009
2503
  const entityInterfaceData = this.entityInterfaceDataByTypeName.get(parentTypeName);
2010
2504
  if (entityInterfaceData) {
2011
2505
  entityInterfaceData.fieldDatas = (0, utils_3.fieldDatasToSimpleFieldDatas)(parentDefinitionData.fieldDataByFieldName.values());
2012
- entityInterfaceData.concreteTypeNames = (0, utils_5.getValueOrDefault)(this.concreteTypeNamesByAbstractTypeName, parentTypeName, () => new Set());
2506
+ const concreteTypeNames = this.concreteTypeNamesByAbstractTypeName.get(parentTypeName);
2507
+ if (concreteTypeNames) {
2508
+ (0, utils_5.addIterableValuesToSet)(concreteTypeNames, entityInterfaceData.concreteTypeNames);
2509
+ }
2013
2510
  configurationData.isInterfaceObject = entityInterfaceData.isInterfaceObject;
2014
2511
  configurationData.entityInterfaceConcreteTypeNames = entityInterfaceData.concreteTypeNames;
2015
2512
  }
@@ -2017,8 +2514,7 @@ class NormalizationFactory {
2017
2514
  if (events) {
2018
2515
  configurationData.events = events;
2019
2516
  }
2020
- this.configurationDataByParentTypeName.set(newParentTypeName, configurationData);
2021
- (0, utils_2.addFieldNamesToConfigurationData)(parentDefinitionData.fieldDataByFieldName, configurationData);
2517
+ this.addFieldNamesToConfigurationData(parentDefinitionData.fieldDataByFieldName, configurationData);
2022
2518
  this.validateInterfaceImplementations(parentDefinitionData);
2023
2519
  definitions.push(this.getCompositeOutputNodeByData(parentDefinitionData));
2024
2520
  // interfaces and objects must define at least one field
@@ -2042,6 +2538,10 @@ class NormalizationFactory {
2042
2538
  throw (0, errors_1.unexpectedKindFatalError)(parentTypeName);
2043
2539
  }
2044
2540
  }
2541
+ // this is where @provides and @requires configurations are added to the ConfigurationData
2542
+ this.addValidConditionalFieldSetConfigurations();
2543
+ // this is where @key configurations are added to the ConfigurationData
2544
+ this.addValidKeyFieldSetConfigurations();
2045
2545
  // Check that explicitly defined operations types are valid objects and that their fields are also valid
2046
2546
  for (const operationType of Object.values(graphql_1.OperationTypeNode)) {
2047
2547
  const operationTypeNode = this.schemaData.operationTypes.get(operationType);
@@ -2067,7 +2567,7 @@ class NormalizationFactory {
2067
2567
  if (!objectData) {
2068
2568
  continue;
2069
2569
  }
2070
- const rootNode = this.configurationDataByParentTypeName.get(defaultTypeName);
2570
+ const rootNode = this.configurationDataByTypeName.get(defaultTypeName);
2071
2571
  if (rootNode) {
2072
2572
  rootNode.isRootNode = true;
2073
2573
  rootNode.typeName = defaultTypeName;
@@ -2101,17 +2601,6 @@ class NormalizationFactory {
2101
2601
  this.errors.push((0, errors_1.undefinedTypeError)(referencedTypeName));
2102
2602
  }
2103
2603
  }
2104
- this.validateAndAddKeysToConfiguration();
2105
- for (const [parentTypeName, fieldSetData] of this.fieldSetDataByTypeName) {
2106
- const parentData = this.parentDefinitionDataByTypeName.get(parentTypeName);
2107
- if (!parentData ||
2108
- (parentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && parentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
2109
- this.errors.push((0, errors_1.undefinedObjectLikeParentError)(parentTypeName));
2110
- continue;
2111
- }
2112
- // this is where keys, provides, and requires are added to the ConfigurationData
2113
- (0, utils_2.validateAndAddConditionalFieldSetsToConfiguration)(this, parentData, fieldSetData);
2114
- }
2115
2604
  const persistedDirectiveDefinitionDataByDirectiveName = new Map();
2116
2605
  for (const directiveDefinitionNode of this.directiveDefinitionByDirectiveName.values()) {
2117
2606
  // TODO @composeDirective directives would also be handled here
@@ -2145,8 +2634,8 @@ class NormalizationFactory {
2145
2634
  // configurationDataMap is map of ConfigurationData per type name.
2146
2635
  // It is an Intermediate configuration object that will be converted to an engine configuration in the router
2147
2636
  concreteTypeNamesByAbstractTypeName: this.concreteTypeNamesByAbstractTypeName,
2148
- conditionalFieldDataByCoordinates: this.conditionalFieldDataByCoordinates,
2149
- configurationDataByTypeName: this.configurationDataByParentTypeName,
2637
+ conditionalFieldDataByCoordinates: this.conditionalFieldDataByCoords,
2638
+ configurationDataByTypeName: this.configurationDataByTypeName,
2150
2639
  entityDataByTypeName: this.entityDataByTypeName,
2151
2640
  entityInterfaces: this.entityInterfaceDataByTypeName,
2152
2641
  isEventDrivenGraph: this.isSubgraphEventDrivenGraph,
@@ -2218,8 +2707,17 @@ function batchNormalize(subgraphs) {
2218
2707
  }
2219
2708
  (0, utils_5.addIterableValuesToSet)(incomingConcreteTypeNames, existingConcreteTypeNames);
2220
2709
  }
2221
- for (const entityData of normalizationResult.entityDataByTypeName.values()) {
2222
- (0, utils_3.upsertEntityData)(entityDataByTypeName, entityData);
2710
+ for (const [typeName, entityData] of normalizationResult.entityDataByTypeName) {
2711
+ const keyFieldSetDataByFieldSet = entityData.keyFieldSetDatasBySubgraphName.get(subgraphName);
2712
+ if (!keyFieldSetDataByFieldSet) {
2713
+ continue;
2714
+ }
2715
+ (0, utils_3.upsertEntityData)({
2716
+ entityDataByTypeName,
2717
+ keyFieldSetDataByFieldSet,
2718
+ typeName,
2719
+ subgraphName,
2720
+ });
2223
2721
  }
2224
2722
  if (subgraph.name) {
2225
2723
  internalSubgraphBySubgraphName.set(subgraphName, {