@wundergraph/composition 0.27.1 → 0.28.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.
Files changed (45) hide show
  1. package/dist/ast/utils.d.ts +0 -2
  2. package/dist/ast/utils.js +0 -34
  3. package/dist/ast/utils.js.map +1 -1
  4. package/dist/errors/errors.d.ts +3 -0
  5. package/dist/errors/errors.js +13 -0
  6. package/dist/errors/errors.js.map +1 -1
  7. package/dist/federation/federation-factory.d.ts +9 -12
  8. package/dist/federation/federation-factory.js +134 -347
  9. package/dist/federation/federation-factory.js.map +1 -1
  10. package/dist/federation/utils.d.ts +27 -6
  11. package/dist/federation/utils.js +123 -0
  12. package/dist/federation/utils.js.map +1 -1
  13. package/dist/federation/walkers.d.ts +1 -1
  14. package/dist/federation/walkers.js +7 -28
  15. package/dist/federation/walkers.js.map +1 -1
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +3 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/normalization/normalization-factory.d.ts +12 -10
  20. package/dist/normalization/normalization-factory.js +52 -29
  21. package/dist/normalization/normalization-factory.js.map +1 -1
  22. package/dist/normalization/utils.d.ts +1 -1
  23. package/dist/normalization/utils.js +54 -25
  24. package/dist/normalization/utils.js.map +1 -1
  25. package/dist/normalization/walkers.js +78 -73
  26. package/dist/normalization/walkers.js.map +1 -1
  27. package/dist/resolvability-graph/graph-nodes.d.ts +48 -0
  28. package/dist/resolvability-graph/graph-nodes.js +104 -0
  29. package/dist/resolvability-graph/graph-nodes.js.map +1 -0
  30. package/dist/resolvability-graph/graph.d.ts +33 -0
  31. package/dist/resolvability-graph/graph.js +406 -0
  32. package/dist/resolvability-graph/graph.js.map +1 -0
  33. package/dist/resolvability-graph/utils.d.ts +65 -0
  34. package/dist/resolvability-graph/utils.js +143 -0
  35. package/dist/resolvability-graph/utils.js.map +1 -0
  36. package/dist/schema-building/utils.js +1 -1
  37. package/dist/schema-building/utils.js.map +1 -1
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/dist/utils/string-constants.d.ts +5 -2
  40. package/dist/utils/string-constants.js +6 -4
  41. package/dist/utils/string-constants.js.map +1 -1
  42. package/dist/utils/utils.d.ts +16 -2
  43. package/dist/utils/utils.js +35 -33
  44. package/dist/utils/utils.js.map +1 -1
  45. package/package.json +3 -5
@@ -28,12 +28,11 @@ class FederationFactory {
28
28
  entityDataByTypeName;
29
29
  entityInterfaceFederationDataByTypeName;
30
30
  errors = [];
31
- evaluatedObjectLikesBySubgraph = new Map();
32
31
  fieldConfigurationByFieldPath = new Map();
33
- graph;
34
32
  graphEdges = new Set();
35
33
  graphPaths = new Map();
36
34
  inaccessiblePaths = new Set();
35
+ internalGraph;
37
36
  internalSubgraphBySubgraphName;
38
37
  invalidOrScopesHostPaths = new Set();
39
38
  isVersionTwo = false;
@@ -63,8 +62,8 @@ class FederationFactory {
63
62
  this.concreteTypeNamesByAbstractTypeName = options.concreteTypeNamesByAbstractTypeName;
64
63
  this.entityDataByTypeName = options.entityDataByTypeName;
65
64
  this.entityInterfaceFederationDataByTypeName = options.entityInterfaceFederationDataByTypeName;
66
- this.graph = options.graph;
67
65
  this.internalSubgraphBySubgraphName = options.internalSubgraphBySubgraphName;
66
+ this.internalGraph = options.internalGraph;
68
67
  this.warnings = options.warnings || [];
69
68
  }
70
69
  getValidImplementedInterfaces(data) {
@@ -163,295 +162,85 @@ class FederationFactory {
163
162
  }
164
163
  return interfaces;
165
164
  }
166
- isFieldResolvableByEntityAncestor(entityAncestors, fieldSubgraphs, parentTypeName) {
167
- if (!this.graph.hasNode(parentTypeName)) {
168
- return false;
169
- }
170
- for (const entityAncestorName of entityAncestors) {
171
- const path = `${entityAncestorName}.${parentTypeName}`;
172
- if (entityAncestorName !== parentTypeName && this.graphPaths.get(path)) {
173
- return true;
174
- }
175
- if (entityAncestorName === parentTypeName) {
176
- const hasOverlap = (0, utils_3.doSetsIntersect)(fieldSubgraphs, (0, utils_3.getOrThrowError)(this.entityDataByTypeName, entityAncestorName, string_constants_1.ENTITIES).subgraphNames);
177
- this.graphPaths.set(path, hasOverlap);
178
- return hasOverlap;
179
- }
180
- if ((0, utils_3.hasSimplePath)(this.graph, entityAncestorName, parentTypeName)) {
181
- this.graphPaths.set(path, true);
182
- return true;
183
- }
184
- this.graphPaths.set(path, false);
185
- }
186
- return false;
187
- }
188
- shouldEvaluateObjectLike(rootTypeFieldSubgraphs, parentTypeName) {
189
- for (const subgraph of rootTypeFieldSubgraphs) {
190
- const evaluatedObjectLikes = this.evaluatedObjectLikesBySubgraph.get(subgraph);
191
- if (evaluatedObjectLikes && evaluatedObjectLikes.has(parentTypeName)) {
192
- continue;
193
- }
194
- return true;
195
- }
196
- return false;
197
- }
198
- updateEvaluatedSubgraphOccurrences(rootTypeFieldSubgraphs, objectSubgraphs, entityAncestors, parentTypeName) {
199
- const mutualSubgraphs = (0, utils_3.getAllMutualEntries)(rootTypeFieldSubgraphs, objectSubgraphs);
200
- if (mutualSubgraphs.size > 0) {
201
- for (const mutualSubgraph of mutualSubgraphs) {
202
- const evaluatedObjects = this.evaluatedObjectLikesBySubgraph.get(mutualSubgraph);
203
- if (evaluatedObjects) {
204
- evaluatedObjects.add(parentTypeName);
205
- }
206
- else {
207
- this.evaluatedObjectLikesBySubgraph.set(mutualSubgraph, new Set([parentTypeName]));
208
- }
209
- }
210
- }
211
- for (const entityAncestorTypeName of entityAncestors) {
212
- const entityObjectData = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, entityAncestorTypeName, 'parentDefinitionDataByTypeName');
213
- const mutualEntityAncestorRootTypeFieldSubgraphs = (0, utils_3.getAllMutualEntries)(rootTypeFieldSubgraphs, entityObjectData.subgraphNames);
214
- const mutualEntityAncestorSubgraphsNames = (0, utils_3.getAllMutualEntries)(mutualEntityAncestorRootTypeFieldSubgraphs, objectSubgraphs);
215
- for (const mutualSubgraphName of mutualEntityAncestorSubgraphsNames) {
216
- const objects = this.evaluatedObjectLikesBySubgraph.get(mutualSubgraphName);
217
- if (objects) {
218
- objects.add(parentTypeName);
219
- }
220
- else {
221
- this.evaluatedObjectLikesBySubgraph.set(mutualSubgraphName, new Set([parentTypeName]));
222
- }
223
- }
224
- }
225
- }
226
- evaluateResolvabilityOfObject(objectData, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors, isParentAbstract = false) {
227
- const parentTypeName = objectData.name;
228
- if (evaluatedObjectLikes.has(parentTypeName)) {
165
+ addValidPrimaryKeyTargetsToEntityData(entityData) {
166
+ if (!entityData) {
229
167
  return;
230
168
  }
231
- if (!this.shouldEvaluateObjectLike(rootTypeFieldData.subgraphs, parentTypeName)) {
232
- evaluatedObjectLikes.add(parentTypeName);
233
- return;
169
+ const internalSubgraph = (0, utils_3.getOrThrowError)(this.internalSubgraphBySubgraphName, this.currentSubgraphName, 'internalSubgraphBySubgraphName');
170
+ const parentDefinitionDataByTypeName = internalSubgraph.parentDefinitionDataByTypeName;
171
+ const parentExtensionDataByTypeName = internalSubgraph.parentExtensionDataByTypeName;
172
+ const objectData = parentDefinitionDataByTypeName.get(entityData.typeName) || parentExtensionDataByTypeName.get(entityData.typeName);
173
+ if (!objectData ||
174
+ (objectData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && objectData.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION)) {
175
+ throw (0, errors_1.incompatibleParentKindFatalError)(entityData.typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, objectData?.kind || graphql_1.Kind.NULL);
234
176
  }
235
- for (const [fieldName, fieldData] of objectData.fieldDataByFieldName) {
236
- const fieldPath = `${fieldData.renamedParentTypeName}.${fieldName}`;
237
- if (this.inaccessiblePaths.has(fieldPath)) {
238
- continue;
239
- }
240
- const namedFieldTypeName = fieldData.namedTypeName;
241
- if (string_constants_1.ROOT_TYPES.has(namedFieldTypeName)) {
242
- continue;
243
- }
244
- // Avoid an infinite loop with self-referential objects
245
- if (evaluatedObjectLikes.has(namedFieldTypeName)) {
246
- continue;
247
- }
248
- if ((0, utils_5.isFieldExternalInAllMutualSubgraphs)(rootTypeFieldData.subgraphs, fieldData)) {
177
+ const configurationData = (0, utils_3.getOrThrowError)(internalSubgraph.configurationDataByParentTypeName, entityData.typeName, 'internalSubgraph.configurationDataByParentTypeName');
178
+ const implicitKeys = [];
179
+ const graphNode = this.internalGraph.nodeByNodeName.get(`${this.currentSubgraphName}.${entityData.typeName}`);
180
+ // Any errors in the field sets would be caught when evaluating the explicit entities, so they are ignored here
181
+ (0, utils_2.validateImplicitFieldSets)({
182
+ configurationData,
183
+ fieldSets: entityData.keyFieldSets,
184
+ implicitKeys,
185
+ objectData,
186
+ parentDefinitionDataByTypeName,
187
+ parentExtensionDataByTypeName,
188
+ graphNode,
189
+ });
190
+ for (const [typeName, entityInterfaceFederationData] of this.entityInterfaceFederationDataByTypeName) {
191
+ if (!entityInterfaceFederationData.concreteTypeNames?.has(entityData.typeName)) {
249
192
  continue;
250
193
  }
251
- this.updateEvaluatedSubgraphOccurrences(rootTypeFieldData.subgraphs, objectData.subgraphNames, entityAncestors, parentTypeName);
252
- evaluatedObjectLikes.add(parentTypeName);
253
- const isFieldResolvable = (0, utils_3.doSetsIntersect)(rootTypeFieldData.subgraphs, fieldData.subgraphNames) ||
254
- this.isFieldResolvableByEntityAncestor(entityAncestors, fieldData.subgraphNames, parentTypeName);
255
- const newCurrentFieldPath = currentFieldPath + (isParentAbstract ? ' ' : '.') + fieldName;
256
- const entity = this.entityDataByTypeName.get(namedFieldTypeName);
257
- if (isFieldResolvable) {
258
- // The base scalars are not in this.parentMap
259
- if (constants_1.BASE_SCALARS.has(namedFieldTypeName)) {
260
- continue;
261
- }
262
- const namedTypeData = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, namedFieldTypeName, string_constants_1.PARENT_DEFINITION_DATA);
263
- switch (namedTypeData.kind) {
264
- case graphql_1.Kind.ENUM_TYPE_DEFINITION:
265
- // intentional fallthrough
266
- case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
267
- continue;
268
- case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
269
- this.evaluateResolvabilityOfObject(namedTypeData, rootTypeFieldData, newCurrentFieldPath, evaluatedObjectLikes, entity ? [...entityAncestors, namedFieldTypeName] : [...entityAncestors]);
270
- continue;
271
- case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
272
- // intentional fallthrough
273
- case graphql_1.Kind.UNION_TYPE_DEFINITION:
274
- this.evaluateResolvabilityOfAbstractType(namedFieldTypeName, namedTypeData.kind, rootTypeFieldData, newCurrentFieldPath, evaluatedObjectLikes, entity ? [...entityAncestors, namedFieldTypeName] : [...entityAncestors]);
275
- continue;
276
- default:
277
- this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(namedTypeData.kind)));
278
- continue;
279
- }
280
- }
281
- if (constants_1.BASE_SCALARS.has(namedFieldTypeName)) {
282
- this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldData.subgraphNames], newCurrentFieldPath, parentTypeName));
194
+ const interfaceObjectEntityData = this.entityDataByTypeName.get(typeName);
195
+ if (!interfaceObjectEntityData) {
283
196
  continue;
284
197
  }
285
- const namedTypeData = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, namedFieldTypeName, 'parentDefinitionDataByTypeName');
286
- switch (namedTypeData.kind) {
287
- case graphql_1.Kind.ENUM_TYPE_DEFINITION:
288
- // intentional fallthrough
289
- case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
290
- this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldData.subgraphNames], newCurrentFieldPath, parentTypeName));
291
- continue;
292
- case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
293
- // intentional fallthrough
294
- case graphql_1.Kind.UNION_TYPE_DEFINITION:
295
- // intentional fallthrough
296
- case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
297
- this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldData.subgraphNames], newCurrentFieldPath + string_constants_1.SELECTION_REPRESENTATION, parentTypeName));
298
- continue;
299
- default:
300
- this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(namedTypeData.kind)));
301
- }
198
+ (0, utils_2.validateImplicitFieldSets)({
199
+ configurationData,
200
+ fieldSets: interfaceObjectEntityData.keyFieldSets,
201
+ implicitKeys,
202
+ objectData,
203
+ parentDefinitionDataByTypeName,
204
+ parentExtensionDataByTypeName,
205
+ graphNode,
206
+ });
302
207
  }
303
- }
304
- evaluateResolvabilityOfAbstractType(abstractTypeName, abstractKind, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors) {
305
- if (evaluatedObjectLikes.has(abstractTypeName)) {
208
+ if (implicitKeys.length < 1) {
306
209
  return;
307
210
  }
308
- evaluatedObjectLikes.add(abstractTypeName);
309
- const concreteTypeNames = this.concreteTypeNamesByAbstractTypeName.get(abstractTypeName);
310
- if (!concreteTypeNames) {
311
- (0, errors_1.noConcreteTypesForAbstractTypeError)((0, utils_3.kindToTypeString)(abstractKind), abstractTypeName);
211
+ if (!configurationData.keys || configurationData.keys.length < 1) {
212
+ configurationData.isRootNode = true;
213
+ configurationData.keys = implicitKeys;
312
214
  return;
313
215
  }
314
- for (const concreteTypeName of concreteTypeNames) {
315
- if (evaluatedObjectLikes.has(concreteTypeName)) {
316
- continue;
317
- }
318
- const concreteTypeData = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, concreteTypeName, 'parentDefinitionDataByTypeName');
319
- if (concreteTypeData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
320
- throw (0, errors_1.unexpectedParentKindErrorMessage)(concreteTypeName, 'Object', (0, utils_3.kindToTypeString)(concreteTypeData.kind));
321
- }
322
- // If the concrete type is unreachable through an inline fragment, it is not an error
323
- if (!(0, utils_3.doSetsIntersect)(concreteTypeData.subgraphNames, rootTypeFieldData.subgraphs)) {
216
+ const existingKeys = new Set(configurationData.keys.map((key) => key.selectionSet));
217
+ for (const implicitKey of implicitKeys) {
218
+ if (existingKeys.has(implicitKey.selectionSet)) {
324
219
  continue;
325
220
  }
326
- const entity = this.entityDataByTypeName.get(concreteTypeName);
327
- this.evaluateResolvabilityOfObject(concreteTypeData, rootTypeFieldData, currentFieldPath + ` ... on ` + concreteTypeName, evaluatedObjectLikes, entity ? [...entityAncestors, concreteTypeName] : [...entityAncestors], true);
221
+ configurationData.keys.push(implicitKey);
222
+ existingKeys.add(implicitKey.selectionSet);
328
223
  }
329
224
  }
330
- addValidPrimaryKeyTargetsToEntityData(entityData) {
331
- if (!entityData) {
332
- return;
333
- }
334
- const internalSubgraph = (0, utils_3.getOrThrowError)(this.internalSubgraphBySubgraphName, this.currentSubgraphName, 'internalSubgraphBySubgraphName');
225
+ addValidPrimaryKeyTargetsFromInterfaceObject(internalSubgraph, interfaceObjectTypeName, entityData, graphNode) {
335
226
  const parentDefinitionDataByTypeName = internalSubgraph.parentDefinitionDataByTypeName;
336
227
  const parentExtensionDataByTypeName = internalSubgraph.parentExtensionDataByTypeName;
337
- const objectData = parentDefinitionDataByTypeName.get(entityData.typeName) || parentExtensionDataByTypeName.get(entityData.typeName);
338
- if (!objectData ||
339
- (objectData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && objectData.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION)) {
340
- throw (0, errors_1.incompatibleParentKindFatalError)(entityData.typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, objectData?.kind || graphql_1.Kind.NULL);
228
+ const interfaceObjectData = parentDefinitionDataByTypeName.get(interfaceObjectTypeName);
229
+ if (!interfaceObjectData || interfaceObjectData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
230
+ throw (0, errors_1.incompatibleParentKindFatalError)(interfaceObjectTypeName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceObjectData?.kind || graphql_1.Kind.NULL);
341
231
  }
342
- const configurationData = (0, utils_3.getOrThrowError)(internalSubgraph.configurationDataByParentTypeName, entityData.typeName, 'internalSubgraph.configurationDataMap');
343
- const keyFieldNames = new Set();
232
+ const configurationData = (0, utils_3.getOrThrowError)(internalSubgraph.configurationDataByParentTypeName, entityData.typeName, 'internalSubgraph.configurationDataByParentTypeName');
344
233
  const implicitKeys = [];
345
234
  // Any errors in the field sets would be caught when evaluating the explicit entities, so they are ignored here
346
- for (const fieldSet of entityData.keyFieldSets) {
347
- // Create a new selection set so that the value can be parsed as a new DocumentNode
348
- const { error, documentNode } = (0, utils_1.safeParse)('{' + fieldSet + '}');
349
- if (error || !documentNode) {
350
- // This would be caught as an error elsewhere
351
- continue;
352
- }
353
- const parentDatas = [objectData];
354
- const definedFields = [];
355
- let currentDepth = -1;
356
- let shouldDefineSelectionSet = true;
357
- let shouldAddKeyFieldSet = true;
358
- (0, graphql_1.visit)(documentNode, {
359
- Argument: {
360
- enter() {
361
- // Fields that define arguments are never allowed in a key FieldSet
362
- // However, at this stage, it actually means the argument is undefined on the field
363
- shouldAddKeyFieldSet = false;
364
- return graphql_1.BREAK;
365
- },
366
- },
367
- Field: {
368
- enter(node) {
369
- const parentData = parentDatas[currentDepth];
370
- // If an object-like was just visited, a selection set should have been entered
371
- if (shouldDefineSelectionSet) {
372
- shouldAddKeyFieldSet = false;
373
- return graphql_1.BREAK;
374
- }
375
- const fieldName = node.name.value;
376
- const fieldData = parentData.fieldDataByFieldName.get(fieldName);
377
- // undefined if the field does not exist on the parent
378
- if (!fieldData || fieldData.argumentDataByArgumentName.size || definedFields[currentDepth].has(fieldName)) {
379
- shouldAddKeyFieldSet = false;
380
- return graphql_1.BREAK;
381
- }
382
- definedFields[currentDepth].add(fieldName);
383
- // Depth 0 is the original parent type
384
- // If a field is external, but it's part of a key FieldSet, it will be included in the root configuration
385
- if (currentDepth === 0) {
386
- keyFieldNames.add(fieldName);
387
- }
388
- const namedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
389
- // The base scalars are not in the parents map
390
- if (constants_1.BASE_SCALARS.has(namedTypeName)) {
391
- return;
392
- }
393
- // The child could itself be a parent and could exist as an object extension
394
- const fieldNamedTypeData = parentDefinitionDataByTypeName.get(namedTypeName) || parentExtensionDataByTypeName.get(namedTypeName);
395
- if (!fieldNamedTypeData) {
396
- shouldAddKeyFieldSet = false;
397
- return graphql_1.BREAK;
398
- }
399
- if (fieldNamedTypeData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
400
- fieldNamedTypeData.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
401
- shouldDefineSelectionSet = true;
402
- parentDatas.push(fieldNamedTypeData);
403
- return;
404
- }
405
- // interfaces and unions are invalid in a key directive
406
- if ((0, utils_1.isKindAbstract)(fieldNamedTypeData.kind)) {
407
- shouldAddKeyFieldSet = false;
408
- return graphql_1.BREAK;
409
- }
410
- },
411
- },
412
- InlineFragment: {
413
- enter() {
414
- shouldAddKeyFieldSet = false;
415
- return graphql_1.BREAK;
416
- },
417
- },
418
- SelectionSet: {
419
- enter() {
420
- if (!shouldDefineSelectionSet) {
421
- shouldAddKeyFieldSet = false;
422
- return graphql_1.BREAK;
423
- }
424
- currentDepth += 1;
425
- shouldDefineSelectionSet = false;
426
- if (currentDepth < 0 || currentDepth >= parentDatas.length) {
427
- shouldAddKeyFieldSet = false;
428
- return graphql_1.BREAK;
429
- }
430
- definedFields.push(new Set());
431
- },
432
- leave() {
433
- if (shouldDefineSelectionSet) {
434
- shouldAddKeyFieldSet = false;
435
- return graphql_1.BREAK;
436
- }
437
- // Empty selection sets would be a parse error, so it is unnecessary to handle them
438
- currentDepth -= 1;
439
- parentDatas.pop();
440
- definedFields.pop();
441
- },
442
- },
443
- });
444
- if (!shouldAddKeyFieldSet) {
445
- continue;
446
- }
447
- // Add any top-level fields that compose the key in case they are external
448
- (0, utils_3.addIterableValuesToSet)(keyFieldNames, configurationData.fieldNames);
449
- implicitKeys.push({
450
- fieldName: '',
451
- selectionSet: (0, utils_4.getNormalizedFieldSet)(documentNode),
452
- disableEntityResolver: true,
453
- });
454
- }
235
+ (0, utils_2.validateImplicitFieldSets)({
236
+ configurationData,
237
+ fieldSets: entityData.keyFieldSets,
238
+ implicitKeys,
239
+ objectData: interfaceObjectData,
240
+ parentDefinitionDataByTypeName,
241
+ parentExtensionDataByTypeName,
242
+ graphNode,
243
+ });
455
244
  if (implicitKeys.length < 1) {
456
245
  return;
457
246
  }
@@ -512,60 +301,6 @@ class FederationFactory {
512
301
  }
513
302
  }
514
303
  }
515
- evaluateRootNodeFieldsResolvability() {
516
- for (const rootTypeName of string_constants_1.ROOT_TYPES) {
517
- const rootTypeData = this.parentDefinitionDataByTypeName.get(rootTypeName);
518
- if (!rootTypeData || rootTypeData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
519
- continue;
520
- }
521
- // After evaluating all of a root type's fields, break and return if there are errors
522
- if (this.errors.length > 0) {
523
- break;
524
- }
525
- // If a root type field returns a Scalar or Enum, track it so that it is not evaluated it again
526
- const evaluatedRootScalarsAndEnums = new Set(constants_1.BASE_SCALARS);
527
- for (const [rootTypeFieldName, fieldData] of rootTypeData.fieldDataByFieldName) {
528
- const namedRootFieldTypeName = fieldData.namedTypeName;
529
- if (evaluatedRootScalarsAndEnums.has(namedRootFieldTypeName)) {
530
- continue;
531
- }
532
- if (!this.shouldEvaluateObjectLike(fieldData.subgraphNames, namedRootFieldTypeName)) {
533
- continue;
534
- }
535
- const namedTypeData = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, namedRootFieldTypeName, 'parentDefinitionDataByTypeName');
536
- const fieldPath = `${rootTypeName}.${rootTypeFieldName}`;
537
- if (this.inaccessiblePaths.has(fieldPath)) {
538
- continue;
539
- }
540
- const rootTypeFieldData = {
541
- fieldName: rootTypeFieldName,
542
- fieldTypeNodeString: (0, merge_1.printTypeNode)(fieldData.node.type),
543
- path: fieldPath,
544
- typeName: rootTypeName,
545
- subgraphs: fieldData.subgraphNames,
546
- };
547
- switch (namedTypeData.kind) {
548
- case graphql_1.Kind.ENUM_TYPE_DEFINITION:
549
- // intentional fallthrough
550
- case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
551
- // Root type fields whose response type is an Enums and Scalars will always be resolvable
552
- // Consequently, subsequent checks can be skipped
553
- evaluatedRootScalarsAndEnums.add(namedRootFieldTypeName);
554
- continue;
555
- case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
556
- this.evaluateResolvabilityOfObject(namedTypeData, rootTypeFieldData, fieldPath, new Set(), this.entityDataByTypeName.has(namedRootFieldTypeName) ? [namedRootFieldTypeName] : []);
557
- continue;
558
- case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
559
- // intentional fallthrough
560
- case graphql_1.Kind.UNION_TYPE_DEFINITION:
561
- this.evaluateResolvabilityOfAbstractType(namedRootFieldTypeName, namedTypeData.kind, rootTypeFieldData, fieldPath, new Set(), this.entityDataByTypeName.has(namedRootFieldTypeName) ? [namedRootFieldTypeName] : []);
562
- continue;
563
- default:
564
- this.errors.push((0, errors_1.unexpectedObjectResponseType)(fieldPath, (0, utils_3.kindToTypeString)(namedTypeData.kind)));
565
- }
566
- }
567
- }
568
- }
569
304
  upsertEnumValueData(enumValueDataByValueName, incomingData, isParentInaccessible) {
570
305
  const existingData = enumValueDataByValueName.get(incomingData.name);
571
306
  const baseData = existingData || incomingData;
@@ -752,10 +487,10 @@ class FederationFactory {
752
487
  if (isParentInaccessible) {
753
488
  this.inaccessiblePaths.add(incomingData.name);
754
489
  }
490
+ if (entityInterfaceData && entityInterfaceData.interfaceObjectSubgraphs.has(subgraphName)) {
491
+ incomingData.kind = graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
492
+ }
755
493
  if (!existingData) {
756
- if (entityInterfaceData && entityInterfaceData.interfaceObjectSubgraphs.has(subgraphName)) {
757
- incomingData.kind = graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
758
- }
759
494
  incomingData.node = {
760
495
  kind: incomingData.kind,
761
496
  name: (0, utils_1.stringToNameNode)(incomingData.name),
@@ -1072,7 +807,7 @@ class FederationFactory {
1072
807
  subgraphNumber += 1;
1073
808
  this.currentSubgraphName = internalSubgraph.name;
1074
809
  this.isVersionTwo ||= internalSubgraph.isVersionTwo;
1075
- (0, walkers_1.createMultiGraphAndRenameRootTypes)(this, internalSubgraph);
810
+ (0, walkers_1.renameRootTypes)(this, internalSubgraph);
1076
811
  for (const parentDefinitionData of internalSubgraph.parentDefinitionDataByTypeName.values()) {
1077
812
  this.upsertParentDefinitionData(parentDefinitionData, internalSubgraph.name);
1078
813
  }
@@ -1098,21 +833,38 @@ class FederationFactory {
1098
833
  }
1099
834
  }
1100
835
  }
836
+ handleInterfaceObjectForInternalGraph({ entityData, internalSubgraph, interfaceObjectData, interfaceObjectNode, resolvableKeyFieldSets, subgraphName, }) {
837
+ const entityGraphNode = this.internalGraph.addOrUpdateNode(entityData.typeName);
838
+ const entityDataNode = this.internalGraph.addEntityDataNode(entityData.typeName);
839
+ for (const satisfiedFieldSet of interfaceObjectNode.satisfiedFieldSets) {
840
+ entityGraphNode.satisfiedFieldSets.add(satisfiedFieldSet);
841
+ if (resolvableKeyFieldSets.has(satisfiedFieldSet)) {
842
+ entityDataNode.addTargetSubgraphByFieldSet(satisfiedFieldSet, subgraphName);
843
+ }
844
+ }
845
+ const fieldDatas = interfaceObjectData.fieldDatasBySubgraphName.get(subgraphName);
846
+ for (const { name, namedTypeName } of fieldDatas || []) {
847
+ this.internalGraph.addEdge(entityGraphNode, this.internalGraph.addOrUpdateNode(namedTypeName), name);
848
+ }
849
+ this.internalGraph.addEdge(interfaceObjectNode, entityGraphNode, entityData.typeName, true);
850
+ this.addValidPrimaryKeyTargetsFromInterfaceObject(internalSubgraph, interfaceObjectNode.typeName, entityData, entityGraphNode);
851
+ }
1101
852
  handleEntityInterfaces() {
1102
- for (const [typeName, entityInterfaceData] of this.entityInterfaceFederationDataByTypeName) {
853
+ for (const [entityInterfaceTypeName, entityInterfaceData] of this.entityInterfaceFederationDataByTypeName) {
1103
854
  (0, utils_3.subtractSourceSetFromTargetSet)(entityInterfaceData.interfaceFieldNames, entityInterfaceData.interfaceObjectFieldNames);
1104
- const entityInterface = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, typeName, string_constants_1.PARENT_DEFINITION_DATA);
855
+ const entityInterface = (0, utils_3.getOrThrowError)(this.parentDefinitionDataByTypeName, entityInterfaceTypeName, string_constants_1.PARENT_DEFINITION_DATA);
1105
856
  if (entityInterface.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
1106
857
  // TODO error
1107
858
  continue;
1108
859
  }
1109
860
  for (const subgraphName of entityInterfaceData.interfaceObjectSubgraphs) {
1110
- const configurationDataMap = (0, utils_3.getOrThrowError)(this.internalSubgraphBySubgraphName, subgraphName, 'internalSubgraphBySubgraphName').configurationDataByParentTypeName;
1111
- const concreteTypeNames = this.concreteTypeNamesByAbstractTypeName.get(typeName);
861
+ const internalSubgraph = (0, utils_3.getOrThrowError)(this.internalSubgraphBySubgraphName, subgraphName, 'internalSubgraphBySubgraphName');
862
+ const configurationDataMap = internalSubgraph.configurationDataByParentTypeName;
863
+ const concreteTypeNames = this.concreteTypeNamesByAbstractTypeName.get(entityInterfaceTypeName);
1112
864
  if (!concreteTypeNames) {
1113
865
  continue;
1114
866
  }
1115
- const interfaceObjectConfiguration = (0, utils_3.getOrThrowError)(configurationDataMap, typeName, 'configurationDataMap');
867
+ const interfaceObjectConfiguration = (0, utils_3.getOrThrowError)(configurationDataMap, entityInterfaceTypeName, 'configurationDataMap');
1116
868
  const keys = interfaceObjectConfiguration.keys;
1117
869
  if (!keys) {
1118
870
  // TODO no keys error
@@ -1121,6 +873,8 @@ class FederationFactory {
1121
873
  interfaceObjectConfiguration.entityInterfaceConcreteTypeNames = entityInterfaceData.concreteTypeNames;
1122
874
  const fieldNames = interfaceObjectConfiguration.fieldNames;
1123
875
  const authorizationData = this.authorizationDataByParentTypeName.get(entityInterfaceData.typeName);
876
+ this.internalGraph.setSubgraphName(subgraphName);
877
+ const interfaceObjectNode = this.internalGraph.addOrUpdateNode(entityInterfaceTypeName, { isAbstract: true });
1124
878
  for (const concreteTypeName of concreteTypeNames) {
1125
879
  if (configurationDataMap.has(concreteTypeName)) {
1126
880
  // error TODO
@@ -1139,31 +893,48 @@ class FederationFactory {
1139
893
  continue;
1140
894
  }
1141
895
  // The subgraph locations of the interface object must be added to the concrete types that implement it
1142
- const entity = this.entityDataByTypeName.get(concreteTypeName);
1143
- if (entity) {
1144
- // TODO error if not an entity
1145
- entity.subgraphNames.add(subgraphName);
1146
- }
896
+ const entityData = (0, utils_3.getOrThrowError)(this.entityDataByTypeName, concreteTypeName, 'entityDataByTypeName');
897
+ entityData.subgraphNames.add(subgraphName);
1147
898
  const configurationData = {
1148
899
  fieldNames,
1149
900
  isRootNode: true,
1150
901
  keys,
1151
902
  typeName: concreteTypeName,
1152
903
  };
904
+ const resolvableKeyFieldSets = new Set();
905
+ for (const key of keys.filter((k) => !k.disableEntityResolver)) {
906
+ resolvableKeyFieldSets.add(key.selectionSet);
907
+ }
1153
908
  for (const fieldName of entityInterfaceData.interfaceObjectFieldNames) {
1154
909
  const existingFieldData = concreteTypeData.fieldDataByFieldName.get(fieldName);
1155
910
  if (existingFieldData) {
1156
911
  // TODO handle shareability
1157
912
  continue;
1158
913
  }
1159
- const interfaceFieldData = (0, utils_3.getOrThrowError)(entityInterface.fieldDataByFieldName, fieldName, `${typeName}.fieldDataByFieldName`);
914
+ const interfaceFieldData = (0, utils_3.getOrThrowError)(entityInterface.fieldDataByFieldName, fieldName, `${entityInterfaceTypeName}.fieldDataByFieldName`);
1160
915
  concreteTypeData.fieldDataByFieldName.set(fieldName, { ...interfaceFieldData });
1161
916
  }
1162
917
  configurationDataMap.set(concreteTypeName, configurationData);
918
+ this.handleInterfaceObjectForInternalGraph({
919
+ internalSubgraph,
920
+ subgraphName,
921
+ interfaceObjectData: entityInterfaceData,
922
+ interfaceObjectNode,
923
+ resolvableKeyFieldSets,
924
+ entityData,
925
+ });
1163
926
  }
1164
927
  }
1165
928
  }
1166
929
  }
930
+ fieldDataToGraphFieldData(fieldData) {
931
+ return {
932
+ name: fieldData.name,
933
+ namedTypeName: fieldData.namedTypeName,
934
+ isLeaf: (0, utils_3.isNodeLeaf)(this.parentDefinitionDataByTypeName.get(fieldData.namedTypeName)?.kind),
935
+ subgraphNames: fieldData.subgraphNames,
936
+ };
937
+ }
1167
938
  pushParentDefinitionDataToDocumentDefinitions(interfaceImplementations) {
1168
939
  for (const [parentTypeName, parentDefinitionData] of this.parentDefinitionDataByTypeName) {
1169
940
  switch (parentDefinitionData.kind) {
@@ -1208,6 +979,7 @@ class FederationFactory {
1208
979
  this.routerDefinitions.push((0, utils_5.getNodeForRouterSchemaByData)(parentDefinitionData, this.persistedDirectiveDefinitionByDirectiveName, this.errors));
1209
980
  if ((0, utils_5.isNodeDataInaccessible)(parentDefinitionData)) {
1210
981
  this.validateReferencesOfInaccessibleType(parentDefinitionData);
982
+ this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
1211
983
  break;
1212
984
  }
1213
985
  if (clientEnumValueNodes.length < 1) {
@@ -1268,6 +1040,7 @@ class FederationFactory {
1268
1040
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
1269
1041
  const fieldNodes = [];
1270
1042
  const clientSchemaFieldNodes = [];
1043
+ const graphFieldDataByFieldName = new Map();
1271
1044
  const invalidFieldNames = new Set();
1272
1045
  const isObject = parentDefinitionData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION;
1273
1046
  for (const [fieldName, fieldData] of parentDefinitionData.fieldDataByFieldName) {
@@ -1281,11 +1054,13 @@ class FederationFactory {
1281
1054
  continue;
1282
1055
  }
1283
1056
  clientSchemaFieldNodes.push((0, utils_5.getClientSchemaFieldNodeByFieldData)(fieldData));
1057
+ graphFieldDataByFieldName.set(fieldName, this.fieldDataToGraphFieldData(fieldData));
1284
1058
  }
1285
1059
  if (isObject && invalidFieldNames.size > 0) {
1286
1060
  this.errors.push((0, errors_1.invalidFieldShareabilityError)(parentDefinitionData, invalidFieldNames));
1287
1061
  }
1288
1062
  parentDefinitionData.node.fields = fieldNodes;
1063
+ this.internalGraph.initializeNode(parentTypeName, graphFieldDataByFieldName);
1289
1064
  // Implemented interfaces can only be validated after all fields are merged
1290
1065
  if (parentDefinitionData.implementedInterfaceTypeNames.size > 0) {
1291
1066
  interfaceImplementations.push({ data: parentDefinitionData, clientSchemaFieldNodes });
@@ -1299,6 +1074,7 @@ class FederationFactory {
1299
1074
  break;
1300
1075
  }
1301
1076
  this.validateReferencesOfInaccessibleType(parentDefinitionData);
1077
+ this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
1302
1078
  break;
1303
1079
  }
1304
1080
  if (clientSchemaFieldNodes.length < 1) {
@@ -1321,6 +1097,7 @@ class FederationFactory {
1321
1097
  this.routerDefinitions.push((0, utils_5.getNodeForRouterSchemaByData)(parentDefinitionData, this.persistedDirectiveDefinitionByDirectiveName, this.errors));
1322
1098
  if ((0, utils_5.isNodeDataInaccessible)(parentDefinitionData)) {
1323
1099
  this.validateReferencesOfInaccessibleType(parentDefinitionData);
1100
+ this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
1324
1101
  break;
1325
1102
  }
1326
1103
  this.clientDefinitions.push({
@@ -1333,6 +1110,7 @@ class FederationFactory {
1333
1110
  this.routerDefinitions.push((0, utils_5.getNodeForRouterSchemaByData)(parentDefinitionData, this.persistedDirectiveDefinitionByDirectiveName, this.errors));
1334
1111
  if ((0, utils_5.isNodeDataInaccessible)(parentDefinitionData)) {
1335
1112
  this.validateReferencesOfInaccessibleType(parentDefinitionData);
1113
+ this.internalGraph.setNodeInaccessible(parentDefinitionData.name);
1336
1114
  break;
1337
1115
  }
1338
1116
  const clientMembers = this.getClientSchemaUnionMembers(parentDefinitionData);
@@ -1365,6 +1143,7 @@ class FederationFactory {
1365
1143
  this.routerDefinitions.push((0, utils_5.getNodeForRouterSchemaByData)(data, this.persistedDirectiveDefinitionByDirectiveName, this.errors));
1366
1144
  if ((0, utils_5.isNodeDataInaccessible)(data)) {
1367
1145
  this.validateReferencesOfInaccessibleType(data);
1146
+ this.internalGraph.setNodeInaccessible(data.name);
1368
1147
  continue;
1369
1148
  }
1370
1149
  const clientInterfaces = [];
@@ -1373,6 +1152,9 @@ class FederationFactory {
1373
1152
  clientInterfaces.push((0, utils_1.stringToNamedTypeNode)(interfaceTypeName));
1374
1153
  }
1375
1154
  }
1155
+ /* It is not possible for clientSchemaFieldNodes to be empty.
1156
+ * If all interface fields were declared @inaccessible, the error would be caught above.
1157
+ * */
1376
1158
  this.clientDefinitions.push({
1377
1159
  ...data.node,
1378
1160
  directives: (0, utils_5.getClientPersistedDirectiveNodes)(data),
@@ -1706,14 +1488,20 @@ class FederationFactory {
1706
1488
  this.pushParentDefinitionDataToDocumentDefinitions(definitionsWithInterfaces);
1707
1489
  this.validateInterfaceImplementationsAndPushToDocumentDefinitions(definitionsWithInterfaces);
1708
1490
  this.validateQueryRootType();
1709
- // return any composition errors before checking whether all fields are resolvable
1491
+ // Return any composition errors before checking whether all fields are resolvable
1710
1492
  if (this.errors.length > 0) {
1711
1493
  return { errors: this.errors };
1712
1494
  }
1713
1495
  /* Resolvability evaluations are not necessary for contracts because the source graph resolvability checks must
1714
- ** have already completed without error. */
1496
+ * have already completed without error. */
1715
1497
  const warnings = this.warnings.length > 0 ? { warnings: this.warnings } : {};
1716
- this.evaluateRootNodeFieldsResolvability();
1498
+ // Resolvability checks are unnecessary for a single subgraph
1499
+ if (this.internalSubgraphBySubgraphName.size > 1) {
1500
+ const resolvabilityErrors = this.internalGraph.validate();
1501
+ if (resolvabilityErrors.length > 0) {
1502
+ return { errors: resolvabilityErrors, ...warnings };
1503
+ }
1504
+ }
1717
1505
  if (this.errors.length > 0) {
1718
1506
  return { errors: this.errors, ...warnings };
1719
1507
  }
@@ -1902,7 +1690,7 @@ function initializeFederationFactory(subgraphs) {
1902
1690
  if (subgraphs.length < 1) {
1903
1691
  return { errors: [errors_1.minimumSubgraphRequirementError] };
1904
1692
  }
1905
- const { authorizationDataByParentTypeName, concreteTypeNamesByAbstractTypeName, entityDataByTypeName, errors, graph, internalSubgraphBySubgraphName, warnings, } = (0, normalization_factory_1.batchNormalize)(subgraphs);
1693
+ const { authorizationDataByParentTypeName, concreteTypeNamesByAbstractTypeName, entityDataByTypeName, errors, internalSubgraphBySubgraphName, internalGraph, warnings, } = (0, normalization_factory_1.batchNormalize)(subgraphs);
1906
1694
  if (errors) {
1907
1695
  return { errors };
1908
1696
  }
@@ -1913,8 +1701,7 @@ function initializeFederationFactory(subgraphs) {
1913
1701
  for (const [typeName, entityInterfaceData] of internalSubgraph.entityInterfaces) {
1914
1702
  // Always add each entity interface to the invalid entity interfaces map
1915
1703
  // If not, earlier checks would not account for implementations not yet seen
1916
- const invalidEntityInterfaces = (0, utils_3.getValueOrDefault)(invalidEntityInterfacesByTypeName, typeName, () => []);
1917
- invalidEntityInterfaces.push({
1704
+ (0, utils_3.getValueOrDefault)(invalidEntityInterfacesByTypeName, typeName, () => []).push({
1918
1705
  subgraphName,
1919
1706
  concreteTypeNames: entityInterfaceData.concreteTypeNames || new Set(),
1920
1707
  });
@@ -1947,8 +1734,8 @@ function initializeFederationFactory(subgraphs) {
1947
1734
  concreteTypeNamesByAbstractTypeName,
1948
1735
  entityDataByTypeName,
1949
1736
  entityInterfaceFederationDataByTypeName,
1950
- graph,
1951
1737
  internalSubgraphBySubgraphName,
1738
+ internalGraph,
1952
1739
  warnings,
1953
1740
  }),
1954
1741
  };