@wundergraph/composition 0.5.2 → 0.6.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 (41) hide show
  1. package/dist/ast/ast.d.ts +21 -3
  2. package/dist/ast/ast.js +23 -11
  3. package/dist/ast/ast.js.map +1 -1
  4. package/dist/ast/utils.d.ts +16 -103
  5. package/dist/ast/utils.js +132 -16
  6. package/dist/ast/utils.js.map +1 -1
  7. package/dist/errors/errors.d.ts +16 -4
  8. package/dist/errors/errors.js +97 -21
  9. package/dist/errors/errors.js.map +1 -1
  10. package/dist/federation/federation-factory.d.ts +28 -22
  11. package/dist/federation/federation-factory.js +611 -429
  12. package/dist/federation/federation-factory.js.map +1 -1
  13. package/dist/federation/utils.d.ts +130 -0
  14. package/dist/federation/utils.js +15 -0
  15. package/dist/federation/utils.js.map +1 -0
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +2 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/normalization/normalization-factory.d.ts +9 -3
  20. package/dist/normalization/normalization-factory.js +221 -75
  21. package/dist/normalization/normalization-factory.js.map +1 -1
  22. package/dist/normalization/utils.d.ts +4 -0
  23. package/dist/normalization/utils.js +12 -5
  24. package/dist/normalization/utils.js.map +1 -1
  25. package/dist/subgraph/field-configuration.d.ts +12 -1
  26. package/dist/subgraph/subgraph.d.ts +2 -2
  27. package/dist/subgraph/subgraph.js +45 -82
  28. package/dist/subgraph/subgraph.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/dist/utils/constants.js +26 -8
  31. package/dist/utils/constants.js.map +1 -1
  32. package/dist/utils/string-constants.d.ts +13 -1
  33. package/dist/utils/string-constants.js +15 -2
  34. package/dist/utils/string-constants.js.map +1 -1
  35. package/dist/utils/utils.d.ts +15 -3
  36. package/dist/utils/utils.js +58 -23
  37. package/dist/utils/utils.js.map +1 -1
  38. package/package.json +4 -3
  39. package/dist/federation/federation-result.d.ts +0 -6
  40. package/dist/federation/federation-result.js +0 -3
  41. package/dist/federation/federation-result.js.map +0 -1
@@ -11,6 +11,7 @@ const errors_1 = require("../errors/errors");
11
11
  const string_constants_1 = require("../utils/string-constants");
12
12
  const buildASTSchema_1 = require("../buildASTSchema/buildASTSchema");
13
13
  const merge_1 = require("@graphql-tools/merge");
14
+ const ast_1 = require("../ast/ast");
14
15
  function normalizeSubgraphFromString(subgraph) {
15
16
  let document;
16
17
  try {
@@ -29,6 +30,7 @@ function normalizeSubgraph(document) {
29
30
  }
30
31
  exports.normalizeSubgraph = normalizeSubgraph;
31
32
  class NormalizationFactory {
33
+ abstractToConcreteTypeNames = new Map();
32
34
  allDirectiveDefinitions = new Map();
33
35
  customDirectiveDefinitions = new Map();
34
36
  errors = [];
@@ -36,6 +38,7 @@ class NormalizationFactory {
36
38
  operationTypeNames = new Map();
37
39
  parents = new Map();
38
40
  parentTypeName = '';
41
+ parentsWithChildArguments = new Set();
39
42
  extensions = new Map();
40
43
  isChild = false;
41
44
  isCurrentParentExtension = false;
@@ -54,20 +57,55 @@ class NormalizationFactory {
54
57
  operationTypes: new Map(),
55
58
  };
56
59
  }
57
- extractArguments(node, map) {
60
+ validateInputNamedType(namedType) {
61
+ if (constants_1.BASE_SCALARS.has(namedType)) {
62
+ return { hasUnhandledError: false, typeString: '' };
63
+ }
64
+ const parentContainer = this.parents.get(namedType);
65
+ if (!parentContainer) {
66
+ this.errors.push((0, errors_1.undefinedTypeError)(namedType));
67
+ return { hasUnhandledError: false, typeString: '' };
68
+ }
69
+ switch (parentContainer.kind) {
70
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
71
+ case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
72
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
73
+ return { hasUnhandledError: false, typeString: '' };
74
+ default:
75
+ return { hasUnhandledError: true, typeString: (0, utils_3.kindToTypeString)(parentContainer.kind) };
76
+ }
77
+ }
78
+ extractArguments(node, argumentByName, fieldPath) {
58
79
  if (!node.arguments) {
59
- return map;
80
+ return argumentByName;
60
81
  }
82
+ this.parentsWithChildArguments.add(this.parentTypeName);
83
+ const duplicatedArguments = new Set();
61
84
  for (const argumentNode of node.arguments) {
62
85
  const argumentName = argumentNode.name.value;
63
- if (map.has(argumentName)) {
64
- // TODO
65
- this.errors.push(new Error('duplicate argument'));
86
+ if (argumentByName.has(argumentName)) {
87
+ duplicatedArguments.add(argumentName);
66
88
  continue;
67
89
  }
68
- map.set(argumentName, argumentNode);
90
+ argumentByName.set(argumentName, (0, ast_1.inputValueDefinitionNodeToMutable)(argumentNode, this.parentTypeName));
91
+ }
92
+ if (duplicatedArguments.size > 0) {
93
+ this.errors.push((0, errors_1.duplicateArgumentsError)(fieldPath, [...duplicatedArguments]));
94
+ }
95
+ return argumentByName;
96
+ }
97
+ validateArguments(fieldContainer, fieldPath) {
98
+ const invalidArguments = [];
99
+ for (const [argumentName, argumentNode] of fieldContainer.arguments) {
100
+ const namedType = (0, type_merging_1.getNamedTypeForChild)(fieldPath + `(${argumentName}...)`, argumentNode.type);
101
+ const { hasUnhandledError, typeString } = this.validateInputNamedType(namedType);
102
+ if (hasUnhandledError) {
103
+ invalidArguments.push({ argumentName, namedType, typeString, typeName: (0, merge_1.printTypeNode)(argumentNode.type) });
104
+ }
105
+ }
106
+ if (invalidArguments.length > 0) {
107
+ this.errors.push((0, errors_1.invalidArgumentsError)(fieldPath, invalidArguments));
69
108
  }
70
- return map;
71
109
  }
72
110
  extractDirectives(node, map) {
73
111
  if (!node.directives) {
@@ -292,13 +330,45 @@ class NormalizationFactory {
292
330
  }
293
331
  }
294
332
  }
333
+ isTypeValidImplementation(originalType, implementationType) {
334
+ if (originalType.kind === graphql_1.Kind.NON_NULL_TYPE) {
335
+ if (implementationType.kind !== graphql_1.Kind.NON_NULL_TYPE) {
336
+ return false;
337
+ }
338
+ return this.isTypeValidImplementation(originalType.type, implementationType.type);
339
+ }
340
+ if (implementationType.kind === graphql_1.Kind.NON_NULL_TYPE) {
341
+ return this.isTypeValidImplementation(originalType, implementationType.type);
342
+ }
343
+ switch (originalType.kind) {
344
+ case graphql_1.Kind.NAMED_TYPE:
345
+ if (implementationType.kind === graphql_1.Kind.NAMED_TYPE) {
346
+ const originalTypeName = originalType.name.value;
347
+ const implementationTypeName = implementationType.name.value;
348
+ if (originalTypeName === implementationTypeName) {
349
+ return true;
350
+ }
351
+ const concreteTypes = this.abstractToConcreteTypeNames.get(originalTypeName);
352
+ if (!concreteTypes) {
353
+ return false;
354
+ }
355
+ return concreteTypes.has(implementationTypeName);
356
+ }
357
+ return false;
358
+ default:
359
+ if (implementationType.kind === graphql_1.Kind.LIST_TYPE) {
360
+ return this.isTypeValidImplementation(originalType.type, implementationType.type);
361
+ }
362
+ return false;
363
+ }
364
+ }
295
365
  validateInterfaceImplementations(container) {
296
366
  if (container.interfaces.size < 1) {
297
367
  return;
298
368
  }
299
369
  const implementationErrorsMap = new Map();
300
370
  for (const interfaceName of container.interfaces) {
301
- const interfaceContainer = (0, utils_3.getOrThrowError)(this.parents, interfaceName);
371
+ const interfaceContainer = (0, utils_3.getOrThrowError)(this.parents, interfaceName, string_constants_1.PARENTS);
302
372
  if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
303
373
  throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
304
374
  }
@@ -322,7 +392,7 @@ class NormalizationFactory {
322
392
  unimplementedArguments: new Set(),
323
393
  };
324
394
  // The implemented field type must be equally or more restrictive than the original interface field type
325
- if (!(0, utils_3.isTypeValidImplementation)(interfaceField.node.type, containerField.node.type)) {
395
+ if (!this.isTypeValidImplementation(interfaceField.node.type, containerField.node.type)) {
326
396
  hasErrors = true;
327
397
  hasNestedErrors = true;
328
398
  invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
@@ -373,24 +443,31 @@ class NormalizationFactory {
373
443
  }
374
444
  normalize(document) {
375
445
  const factory = this;
446
+ /* factory.allDirectiveDefinitions is initialized with v1 directive definitions, and v2 definitions are only added
447
+ after the visitor has visited the entire schema and the subgraph is known to be a V2 graph. Consequently,
448
+ allDirectiveDefinitions cannot be used to check for duplicate definitions, and another set (below) is required */
449
+ const definedDirectives = new Set();
450
+ let isCurrentParentRootType = false;
376
451
  (0, graphql_1.visit)(document, {
377
452
  DirectiveDefinition: {
378
453
  enter(node) {
379
454
  const name = node.name.value;
380
- // TODO These sets would potentially allow the user to define these directives more than once
381
- // Add our definitions rather than the existing ones
455
+ if (definedDirectives.has(name)) {
456
+ factory.errors.push((0, errors_1.duplicateDirectiveDefinitionError)(name));
457
+ return false;
458
+ }
459
+ else {
460
+ definedDirectives.add(name);
461
+ }
462
+ // Normalize federation directives by replacing them with predefined definitions
382
463
  if (constants_1.VERSION_TWO_DIRECTIVES.has(name)) {
383
464
  factory.isSubgraphVersionTwo = true;
384
465
  return false;
385
466
  }
467
+ // The V1 directives are always injected
386
468
  if (constants_1.VERSION_ONE_DIRECTIVES.has(name)) {
387
469
  return false;
388
470
  }
389
- const directiveDefinition = factory.allDirectiveDefinitions.get(name);
390
- if (directiveDefinition) {
391
- factory.errors.push((0, errors_1.duplicateDirectiveDefinitionError)(name));
392
- return false;
393
- }
394
471
  factory.allDirectiveDefinitions.set(name, node);
395
472
  factory.customDirectiveDefinitions.set(name, node);
396
473
  return false;
@@ -418,7 +495,7 @@ class NormalizationFactory {
418
495
  }
419
496
  factory.parentTypeName = name;
420
497
  factory.parents.set(name, {
421
- description: node.description,
498
+ description: (0, utils_1.formatDescription)(node.description),
422
499
  directives: factory.extractDirectives(node, new Map()),
423
500
  kind: node.kind,
424
501
  name: node.name,
@@ -459,8 +536,8 @@ class NormalizationFactory {
459
536
  enter(node) {
460
537
  const name = node.name.value;
461
538
  const parent = factory.isCurrentParentExtension
462
- ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
463
- : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
539
+ ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName, string_constants_1.EXTENSIONS)
540
+ : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName, string_constants_1.PARENTS);
464
541
  if (parent.kind !== graphql_1.Kind.ENUM_TYPE_DEFINITION && parent.kind !== graphql_1.Kind.ENUM_TYPE_EXTENSION) {
465
542
  throw (0, errors_1.unexpectedKindFatalError)(name);
466
543
  }
@@ -474,13 +551,16 @@ class NormalizationFactory {
474
551
  parent.values.set(name, {
475
552
  directives: factory.extractDirectives(node, new Map()),
476
553
  name,
477
- node,
554
+ node: { ...node, description: (0, utils_1.formatDescription)(node.description) },
478
555
  });
479
556
  },
480
557
  },
481
558
  FieldDefinition: {
482
559
  enter(node) {
483
560
  const name = node.name.value;
561
+ if (isCurrentParentRootType && (name === string_constants_1.SERVICE_FIELD || name === string_constants_1.ENTITIES_FIELD)) {
562
+ return false;
563
+ }
484
564
  const fieldPath = `${factory.parentTypeName}.${name}`;
485
565
  factory.isChild = true;
486
566
  const fieldRootType = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
@@ -488,8 +568,8 @@ class NormalizationFactory {
488
568
  factory.referencedTypeNames.add(fieldRootType);
489
569
  }
490
570
  const parent = factory.isCurrentParentExtension
491
- ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
492
- : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
571
+ ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName, string_constants_1.EXTENSIONS)
572
+ : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName, string_constants_1.PARENTS);
493
573
  if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
494
574
  parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION &&
495
575
  parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION &&
@@ -503,11 +583,18 @@ class NormalizationFactory {
503
583
  factory.errors.push(error);
504
584
  return;
505
585
  }
586
+ // recreate the node so the argument descriptions are updated
506
587
  parent.fields.set(name, {
507
- arguments: factory.extractArguments(node, new Map),
588
+ arguments: factory.extractArguments(node, new Map(), fieldPath),
508
589
  directives: factory.extractDirectives(node, new Map()),
509
590
  name,
510
- node,
591
+ node: {
592
+ ...node,
593
+ arguments: node.arguments?.map((arg) => ({
594
+ ...arg,
595
+ description: (0, utils_1.formatDescription)(arg.description),
596
+ })),
597
+ }
511
598
  });
512
599
  },
513
600
  leave() {
@@ -523,7 +610,7 @@ class NormalizationFactory {
523
610
  }
524
611
  factory.parentTypeName = name;
525
612
  factory.parents.set(name, {
526
- description: node.description,
613
+ description: (0, utils_1.formatDescription)(node.description),
527
614
  directives: factory.extractDirectives(node, new Map()),
528
615
  fields: new Map(),
529
616
  kind: node.kind,
@@ -566,9 +653,13 @@ class NormalizationFactory {
566
653
  return;
567
654
  }
568
655
  const name = node.name.value;
656
+ const valueRootTypeName = (0, type_merging_1.getNamedTypeForChild)(`${factory.parentTypeName}.${name}`, node.type);
657
+ if (!constants_1.BASE_SCALARS.has(valueRootTypeName)) {
658
+ factory.referencedTypeNames.add(valueRootTypeName);
659
+ }
569
660
  const parent = factory.isCurrentParentExtension
570
- ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
571
- : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
661
+ ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName, string_constants_1.EXTENSIONS)
662
+ : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName, string_constants_1.PARENTS);
572
663
  if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION && parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION) {
573
664
  throw (0, errors_1.unexpectedKindFatalError)(factory.parentTypeName);
574
665
  }
@@ -579,7 +670,7 @@ class NormalizationFactory {
579
670
  parent.fields.set(name, {
580
671
  directives: factory.extractDirectives(node, new Map()),
581
672
  name,
582
- node,
673
+ node: { ...node, description: (0, utils_1.formatDescription)(node.description) },
583
674
  });
584
675
  },
585
676
  },
@@ -595,7 +686,7 @@ class NormalizationFactory {
595
686
  return false;
596
687
  }
597
688
  factory.parents.set(name, {
598
- description: node.description,
689
+ description: (0, utils_1.formatDescription)(node.description),
599
690
  directives: factory.extractDirectives(node, new Map()),
600
691
  fields: new Map(),
601
692
  interfaces: (0, utils_1.extractInterfaces)(node, new Set(), factory.errors),
@@ -621,7 +712,12 @@ class NormalizationFactory {
621
712
  ObjectTypeDefinition: {
622
713
  enter(node) {
623
714
  const name = node.name.value;
715
+ if (name === string_constants_1.SERVICE) {
716
+ return false;
717
+ }
718
+ isCurrentParentRootType = string_constants_1.ROOT_TYPES.has(name);
624
719
  factory.parentTypeName = name;
720
+ (0, utils_1.addConcreteTypesForImplementedInterfaces)(node, factory.abstractToConcreteTypeNames);
625
721
  // handling for @extends directive
626
722
  if ((0, utils_1.isNodeExtension)(node)) {
627
723
  return factory.handleObjectLikeExtension(node);
@@ -632,7 +728,7 @@ class NormalizationFactory {
632
728
  }
633
729
  const isEntity = (0, utils_1.isObjectLikeNodeEntity)(node);
634
730
  factory.parents.set(name, {
635
- description: node.description,
731
+ description: (0, utils_1.formatDescription)(node.description),
636
732
  directives: factory.extractDirectives(node, new Map()),
637
733
  fields: new Map(),
638
734
  interfaces: (0, utils_1.extractInterfaces)(node, new Set(), factory.errors),
@@ -654,16 +750,24 @@ class NormalizationFactory {
654
750
  },
655
751
  leave() {
656
752
  factory.isCurrentParentExtension = false;
753
+ isCurrentParentRootType = false;
657
754
  factory.parentTypeName = '';
658
755
  },
659
756
  },
660
757
  ObjectTypeExtension: {
661
758
  enter(node) {
662
- factory.parentTypeName = node.name.value;
759
+ const name = node.name.value;
760
+ if (name === string_constants_1.SERVICE) {
761
+ return false;
762
+ }
763
+ isCurrentParentRootType = string_constants_1.ROOT_TYPES.has(name);
764
+ factory.parentTypeName = name;
765
+ (0, utils_1.addConcreteTypesForImplementedInterfaces)(node, factory.abstractToConcreteTypeNames);
663
766
  return factory.handleObjectLikeExtension(node);
664
767
  },
665
768
  leave() {
666
769
  factory.isCurrentParentExtension = false;
770
+ isCurrentParentRootType = false;
667
771
  factory.parentTypeName = '';
668
772
  },
669
773
  },
@@ -697,7 +801,7 @@ class NormalizationFactory {
697
801
  return false;
698
802
  }
699
803
  factory.parents.set(name, {
700
- description: node.description,
804
+ description: (0, utils_1.formatDescription)(node.description),
701
805
  directives: factory.extractDirectives(node, new Map()),
702
806
  kind: graphql_1.Kind.SCALAR_TYPE_DEFINITION,
703
807
  name: node.name,
@@ -728,7 +832,7 @@ class NormalizationFactory {
728
832
  SchemaDefinition: {
729
833
  enter(node) {
730
834
  factory.extractDirectives(node, factory.schemaDefinition.directives);
731
- factory.schemaDefinition.description = factory.schemaDefinition.description || node.description;
835
+ factory.schemaDefinition.description = node.description;
732
836
  },
733
837
  },
734
838
  SchemaExtension: {
@@ -749,8 +853,9 @@ class NormalizationFactory {
749
853
  factory.errors.push((0, errors_1.noDefinedUnionMembersError)(name));
750
854
  return false;
751
855
  }
856
+ (0, utils_1.addConcreteTypesForUnion)(node, factory.abstractToConcreteTypeNames);
752
857
  factory.parents.set(name, {
753
- description: node.description,
858
+ description: (0, utils_1.formatDescription)(node.description),
754
859
  directives: factory.extractDirectives(node, new Map()),
755
860
  kind: node.kind,
756
861
  name: node.name,
@@ -769,6 +874,7 @@ class NormalizationFactory {
769
874
  factory.errors.push();
770
875
  return false;
771
876
  }
877
+ (0, utils_1.addConcreteTypesForUnion)(node, factory.abstractToConcreteTypeNames);
772
878
  if (extension) {
773
879
  if (extension.kind !== graphql_1.Kind.UNION_TYPE_EXTENSION) {
774
880
  factory.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
@@ -809,91 +915,107 @@ class NormalizationFactory {
809
915
  const configurationDataMap = new Map();
810
916
  const validExtensionOrphans = new Set();
811
917
  const parentsToIgnore = new Set();
812
- for (const [typeName, extension] of this.extensions) {
813
- const entity = this.entityMap.get(typeName);
918
+ for (const [extensionTypeName, extensionContainer] of this.extensions) {
919
+ const entity = this.entityMap.get(extensionTypeName);
814
920
  const configurationData = {
815
921
  fieldNames: new Set(),
816
922
  isRootNode: !!entity,
817
- selectionSets: entity ? [...entity.keys()] : [],
818
- typeName,
923
+ typeName: extensionTypeName,
819
924
  };
820
- if (extension.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
821
- (0, utils_3.addIterableValuesToSet)(extension.fields.keys(), configurationData.fieldNames);
822
- configurationDataMap.set(typeName, configurationData);
925
+ if (entity) {
926
+ configurationData.keys = [...entity.keys()].map((selectionSet) => ({
927
+ fieldName: '', selectionSet,
928
+ }));
929
+ }
930
+ if (extensionContainer.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
931
+ if (this.operationTypeNames.has(extensionTypeName)) {
932
+ extensionContainer.fields.delete(string_constants_1.SERVICE_FIELD);
933
+ extensionContainer.fields.delete(string_constants_1.ENTITIES_FIELD);
934
+ }
935
+ (0, utils_3.addIterableValuesToSet)(extensionContainer.fields.keys(), configurationData.fieldNames);
936
+ configurationDataMap.set(extensionTypeName, configurationData);
823
937
  }
824
- const baseType = this.parents.get(typeName);
938
+ const baseType = this.parents.get(extensionTypeName);
825
939
  if (!baseType) {
826
- if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
827
- this.errors.push((0, errors_1.noBaseTypeExtensionError)(typeName));
940
+ if (extensionContainer.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
941
+ this.errors.push((0, errors_1.noBaseTypeExtensionError)(extensionTypeName));
828
942
  }
829
943
  else {
830
- (0, utils_2.validateEntityKeys)(this, typeName, true);
831
- this.validateInterfaceImplementations(extension);
832
- validExtensionOrphans.add(typeName);
833
- definitions.push((0, utils_2.objectLikeContainerToNode)(this, extension));
944
+ (0, utils_2.validateEntityKeys)(this, extensionTypeName, true);
945
+ this.validateInterfaceImplementations(extensionContainer);
946
+ validExtensionOrphans.add(extensionTypeName);
947
+ definitions.push((0, utils_2.objectLikeContainerToNode)(this, extensionContainer));
834
948
  }
835
949
  continue;
836
950
  }
837
- if (!(0, utils_1.areBaseAndExtensionKindsCompatible)(baseType.kind, extension.kind, typeName)) {
838
- this.errors.push((0, errors_1.incompatibleExtensionError)(typeName, baseType.kind, extension.kind));
951
+ if (!(0, utils_1.areBaseAndExtensionKindsCompatible)(baseType.kind, extensionContainer.kind, extensionTypeName)) {
952
+ this.errors.push((0, errors_1.incompatibleExtensionError)(extensionTypeName, baseType.kind, extensionContainer.kind));
839
953
  continue;
840
954
  }
841
955
  switch (baseType.kind) {
842
956
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
843
- const enumExtension = extension;
957
+ const enumExtension = extensionContainer;
844
958
  for (const [valueName, enumValueDefinitionNode] of enumExtension.values) {
845
959
  if (!baseType.values.has(valueName)) {
846
960
  baseType.values.set(valueName, enumValueDefinitionNode);
847
961
  continue;
848
962
  }
849
- this.errors.push((0, errors_1.duplicateEnumValueDefinitionError)(valueName, typeName));
963
+ this.errors.push((0, errors_1.duplicateEnumValueDefinitionError)(valueName, extensionTypeName));
850
964
  }
851
965
  definitions.push((0, utils_2.enumContainerToNode)(this, baseType, enumExtension));
852
966
  break;
853
967
  case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
854
- const inputExtension = extension;
968
+ const inputExtension = extensionContainer;
855
969
  for (const [fieldName, inputValueDefinitionNode] of inputExtension.fields) {
856
970
  if (!baseType.fields.has(fieldName)) {
857
971
  baseType.fields.set(fieldName, inputValueDefinitionNode);
858
972
  continue;
859
973
  }
860
- this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, typeName));
974
+ this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, extensionTypeName));
861
975
  }
862
976
  definitions.push((0, utils_2.inputObjectContainerToNode)(this, baseType, inputExtension));
863
977
  break;
864
978
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
865
979
  // intentional fallthrough
866
980
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
867
- const objectLikeExtension = extension;
981
+ const objectLikeExtension = extensionContainer;
982
+ if (this.operationTypeNames.has(extensionTypeName)) {
983
+ objectLikeExtension.fields.delete(string_constants_1.SERVICE_FIELD);
984
+ objectLikeExtension.fields.delete(string_constants_1.ENTITIES_FIELD);
985
+ }
868
986
  for (const [fieldName, fieldContainer] of objectLikeExtension.fields) {
987
+ if (fieldContainer.arguments.size > 0) {
988
+ // Arguments can only be fully validated once all parents types are known
989
+ this.validateArguments(fieldContainer, `${extensionTypeName}.${fieldName}`);
990
+ }
869
991
  if (baseType.fields.has(fieldName)) {
870
- this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, typeName));
992
+ this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, extensionTypeName));
871
993
  continue;
872
994
  }
873
995
  baseType.fields.set(fieldName, fieldContainer);
874
996
  configurationData.fieldNames.add(fieldName);
875
997
  }
876
- (0, utils_2.validateEntityKeys)(this, typeName);
877
- this.mergeUniqueInterfaces(objectLikeExtension.interfaces, baseType.interfaces, typeName);
998
+ (0, utils_2.validateEntityKeys)(this, extensionTypeName);
999
+ this.mergeUniqueInterfaces(objectLikeExtension.interfaces, baseType.interfaces, extensionTypeName);
878
1000
  this.validateInterfaceImplementations(baseType);
879
- configurationDataMap.set(typeName, configurationData);
1001
+ configurationDataMap.set(extensionTypeName, configurationData);
880
1002
  definitions.push((0, utils_2.objectLikeContainerToNode)(this, baseType, objectLikeExtension));
881
1003
  break;
882
1004
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
883
- definitions.push((0, utils_2.scalarContainerToNode)(this, baseType, extension));
1005
+ definitions.push((0, utils_2.scalarContainerToNode)(this, baseType, extensionContainer));
884
1006
  break;
885
1007
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
886
- const unionExtension = extension;
1008
+ const unionExtension = extensionContainer;
887
1009
  definitions.push((0, utils_2.unionContainerToNode)(this, baseType, unionExtension));
888
1010
  break;
889
1011
  default:
890
- throw (0, errors_1.unexpectedKindFatalError)(typeName);
1012
+ throw (0, errors_1.unexpectedKindFatalError)(extensionTypeName);
891
1013
  }
892
1014
  // At this point, the base type has been dealt with, so it doesn't need to be dealt with again
893
- parentsToIgnore.add(typeName);
1015
+ parentsToIgnore.add(extensionTypeName);
894
1016
  }
895
- for (const [typeName, parentContainer] of this.parents) {
896
- if (parentsToIgnore.has(typeName)) {
1017
+ for (const [parentTypeName, parentContainer] of this.parents) {
1018
+ if (parentsToIgnore.has(parentTypeName)) {
897
1019
  continue;
898
1020
  }
899
1021
  switch (parentContainer.kind) {
@@ -906,17 +1028,36 @@ class NormalizationFactory {
906
1028
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
907
1029
  // intentional fallthrough
908
1030
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
909
- const entity = this.entityMap.get(typeName);
1031
+ const entity = this.entityMap.get(parentTypeName);
1032
+ if (this.operationTypeNames.has(parentTypeName)) {
1033
+ parentContainer.fields.delete(string_constants_1.SERVICE_FIELD);
1034
+ parentContainer.fields.delete(string_constants_1.ENTITIES_FIELD);
1035
+ }
1036
+ if (this.parentsWithChildArguments.has(parentTypeName)) {
1037
+ const parentContainer = (0, utils_3.getOrThrowError)(this.parents, parentTypeName, string_constants_1.PARENTS);
1038
+ if (parentContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION
1039
+ && parentContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
1040
+ continue;
1041
+ }
1042
+ for (const [fieldName, fieldContainer] of parentContainer.fields) {
1043
+ // Arguments can only be fully validated once all parents types are known
1044
+ this.validateArguments(fieldContainer, `${parentTypeName}.${fieldName}`);
1045
+ }
1046
+ }
910
1047
  const configurationData = {
911
1048
  fieldNames: new Set(),
912
1049
  isRootNode: !!entity,
913
- selectionSets: entity ? [...entity.keys()] : [],
914
- typeName,
1050
+ typeName: parentTypeName,
915
1051
  };
1052
+ if (entity) {
1053
+ configurationData.keys = [...entity.keys()].map((selectionSet) => ({
1054
+ fieldName: '', selectionSet,
1055
+ }));
1056
+ }
916
1057
  (0, utils_3.addIterableValuesToSet)(parentContainer.fields.keys(), configurationData.fieldNames);
917
- (0, utils_2.validateEntityKeys)(this, typeName);
1058
+ (0, utils_2.validateEntityKeys)(this, parentTypeName);
918
1059
  this.validateInterfaceImplementations(parentContainer);
919
- configurationDataMap.set(typeName, configurationData);
1060
+ configurationDataMap.set(parentTypeName, configurationData);
920
1061
  definitions.push((0, utils_2.objectLikeContainerToNode)(this, parentContainer));
921
1062
  break;
922
1063
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
@@ -926,15 +1067,16 @@ class NormalizationFactory {
926
1067
  definitions.push((0, utils_2.unionContainerToNode)(this, parentContainer));
927
1068
  break;
928
1069
  default:
929
- throw (0, errors_1.unexpectedKindFatalError)(typeName);
1070
+ throw (0, errors_1.unexpectedKindFatalError)(parentTypeName);
930
1071
  }
931
1072
  }
932
1073
  // Check that explicitly defined operations types are valid objects and that their fields are also valid
933
1074
  for (const operationType of Object.values(graphql_1.OperationTypeNode)) {
934
1075
  const node = this.schemaDefinition.operationTypes.get(operationType);
935
- const defaultTypeName = (0, utils_3.getOrThrowError)(utils_1.operationTypeNodeToDefaultType, operationType);
1076
+ const defaultTypeName = (0, utils_3.getOrThrowError)(utils_1.operationTypeNodeToDefaultType, operationType, string_constants_1.OPERATION_TO_DEFAULT);
936
1077
  // If an operation type name was not declared, use the default
937
- const operationTypeName = node ? (0, type_merging_1.getNamedTypeForChild)(`schema.${operationType}`, node.type) : defaultTypeName;
1078
+ const operationTypeName = node ? (0, type_merging_1.getNamedTypeForChild)(`schema.${operationType}`, node.type)
1079
+ : defaultTypeName;
938
1080
  // If a custom type is used, the default type should not be defined
939
1081
  if (operationTypeName !== defaultTypeName &&
940
1082
  (this.parents.has(defaultTypeName) || this.extensions.has(defaultTypeName))) {
@@ -970,7 +1112,7 @@ class NormalizationFactory {
970
1112
  this.errors.push((0, errors_1.operationDefinitionError)(operationTypeName, operationType, container.kind));
971
1113
  continue;
972
1114
  }
973
- // Operations whose response type is an extension orphan could be valid through a federated graph
1115
+ // Root types fields whose response type is an extension orphan could be valid through a federated graph
974
1116
  // However, the field would have to be shareable to ever be valid TODO
975
1117
  for (const fieldContainer of container.fields.values()) {
976
1118
  const fieldName = fieldContainer.name;
@@ -985,7 +1127,11 @@ class NormalizationFactory {
985
1127
  }
986
1128
  }
987
1129
  for (const referencedTypeName of this.referencedTypeNames) {
988
- if (!this.parents.has(referencedTypeName) && !this.entityMap.has(referencedTypeName)) {
1130
+ if (this.parents.has(referencedTypeName) || this.entityMap.has(referencedTypeName)) {
1131
+ continue;
1132
+ }
1133
+ const extension = this.extensions.get(referencedTypeName);
1134
+ if (!extension || extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
989
1135
  this.errors.push((0, errors_1.undefinedTypeError)(referencedTypeName));
990
1136
  }
991
1137
  }