@theguild/federation-composition 0.10.1 → 0.11.0-alpha-20240315131906-9fcd4e6

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 (100) hide show
  1. package/README.md +3 -16
  2. package/cjs/subgraph/helpers.js +1 -1
  3. package/cjs/subgraph/state.js +183 -4
  4. package/cjs/subgraph/validation/rules/elements/interface-object.js +19 -3
  5. package/cjs/subgraph/validation/rules/elements/key.js +3 -2
  6. package/cjs/subgraph/validation/rules/elements/policy.js +3 -2
  7. package/cjs/subgraph/validation/rules/elements/shareable.js +3 -2
  8. package/cjs/subgraph/validation/rules/elements/tag.js +2 -1
  9. package/cjs/subgraph/validation/rules/known-federation-directive-rule.js +4 -0
  10. package/cjs/subgraph/validation/rules/only-interface-implementation-rule.js +67 -0
  11. package/cjs/subgraph/validation/validate-subgraph.js +2 -0
  12. package/cjs/subgraph/validation/validation-context.js +18 -11
  13. package/cjs/supergraph/composition/interface-type.js +45 -9
  14. package/cjs/supergraph/composition/object-type.js +54 -1
  15. package/cjs/supergraph/composition/visitor.js +7 -0
  16. package/cjs/supergraph/validation/rules/interface-key-missing-implementation-type.js +9 -1
  17. package/cjs/supergraph/validation/rules/interface-object-usage-error.js +24 -0
  18. package/cjs/supergraph/validation/rules/invalid-field-sharing-rule.js +93 -2
  19. package/cjs/supergraph/validation/rules/only-inaccessible-children-rule.js +9 -0
  20. package/cjs/supergraph/validation/rules/satisfiablity/errors.js +3 -0
  21. package/cjs/supergraph/validation/rules/satisfiablity/finder.js +139 -120
  22. package/cjs/supergraph/validation/rules/satisfiablity/graph.js +111 -10
  23. package/cjs/supergraph/validation/rules/satisfiablity/move-validator.js +118 -156
  24. package/cjs/supergraph/validation/rules/satisfiablity/moves.js +6 -1
  25. package/cjs/supergraph/validation/rules/satisfiablity/{fields.js → selection.js} +64 -47
  26. package/cjs/supergraph/validation/rules/satisfiablity/supergraph.js +7 -7
  27. package/cjs/supergraph/validation/rules/satisfiablity/walker.js +5 -4
  28. package/cjs/supergraph/validation/rules/satisfiablity-rule.js +82 -47
  29. package/cjs/supergraph/validation/rules/types-of-the-same-kind-rule.js +21 -4
  30. package/cjs/supergraph/validation/validate-supergraph.js +2 -0
  31. package/cjs/utils/version.js +16 -0
  32. package/esm/subgraph/helpers.js +1 -1
  33. package/esm/subgraph/state.js +183 -4
  34. package/esm/subgraph/validation/rules/elements/interface-object.js +20 -4
  35. package/esm/subgraph/validation/rules/elements/key.js +3 -2
  36. package/esm/subgraph/validation/rules/elements/policy.js +3 -2
  37. package/esm/subgraph/validation/rules/elements/shareable.js +3 -2
  38. package/esm/subgraph/validation/rules/elements/tag.js +2 -1
  39. package/esm/subgraph/validation/rules/known-federation-directive-rule.js +4 -0
  40. package/esm/subgraph/validation/rules/only-interface-implementation-rule.js +63 -0
  41. package/esm/subgraph/validation/validate-subgraph.js +2 -0
  42. package/esm/subgraph/validation/validation-context.js +18 -11
  43. package/esm/supergraph/composition/interface-type.js +45 -9
  44. package/esm/supergraph/composition/object-type.js +54 -1
  45. package/esm/supergraph/composition/visitor.js +7 -0
  46. package/esm/supergraph/validation/rules/interface-key-missing-implementation-type.js +9 -1
  47. package/esm/supergraph/validation/rules/interface-object-usage-error.js +20 -0
  48. package/esm/supergraph/validation/rules/invalid-field-sharing-rule.js +93 -2
  49. package/esm/supergraph/validation/rules/only-inaccessible-children-rule.js +9 -0
  50. package/esm/supergraph/validation/rules/satisfiablity/errors.js +3 -0
  51. package/esm/supergraph/validation/rules/satisfiablity/finder.js +139 -120
  52. package/esm/supergraph/validation/rules/satisfiablity/graph.js +111 -10
  53. package/esm/supergraph/validation/rules/satisfiablity/move-validator.js +119 -157
  54. package/esm/supergraph/validation/rules/satisfiablity/moves.js +6 -1
  55. package/esm/supergraph/validation/rules/satisfiablity/{fields.js → selection.js} +61 -44
  56. package/esm/supergraph/validation/rules/satisfiablity/supergraph.js +7 -7
  57. package/esm/supergraph/validation/rules/satisfiablity/walker.js +5 -4
  58. package/esm/supergraph/validation/rules/satisfiablity-rule.js +83 -48
  59. package/esm/supergraph/validation/rules/types-of-the-same-kind-rule.js +21 -4
  60. package/esm/supergraph/validation/validate-supergraph.js +2 -0
  61. package/esm/utils/version.js +12 -0
  62. package/package.json +1 -1
  63. package/typings/specifications/federation.d.cts +1 -0
  64. package/typings/specifications/federation.d.ts +1 -0
  65. package/typings/subgraph/state.d.cts +8 -0
  66. package/typings/subgraph/state.d.ts +8 -0
  67. package/typings/subgraph/validation/rules/only-interface-implementation-rule.d.cts +4 -0
  68. package/typings/subgraph/validation/rules/only-interface-implementation-rule.d.ts +4 -0
  69. package/typings/subgraph/validation/validation-context.d.cts +4 -0
  70. package/typings/subgraph/validation/validation-context.d.ts +4 -0
  71. package/typings/supergraph/composition/interface-type.d.cts +9 -1
  72. package/typings/supergraph/composition/interface-type.d.ts +9 -1
  73. package/typings/supergraph/composition/object-type.d.cts +1 -0
  74. package/typings/supergraph/composition/object-type.d.ts +1 -0
  75. package/typings/supergraph/composition/visitor.d.cts +2 -1
  76. package/typings/supergraph/composition/visitor.d.ts +2 -1
  77. package/typings/supergraph/validation/rules/interface-object-usage-error.d.cts +4 -0
  78. package/typings/supergraph/validation/rules/interface-object-usage-error.d.ts +4 -0
  79. package/typings/supergraph/validation/rules/invalid-field-sharing-rule.d.cts +2 -1
  80. package/typings/supergraph/validation/rules/invalid-field-sharing-rule.d.ts +2 -1
  81. package/typings/supergraph/validation/rules/satisfiablity/errors.d.cts +2 -1
  82. package/typings/supergraph/validation/rules/satisfiablity/errors.d.ts +2 -1
  83. package/typings/supergraph/validation/rules/satisfiablity/finder.d.cts +5 -3
  84. package/typings/supergraph/validation/rules/satisfiablity/finder.d.ts +5 -3
  85. package/typings/supergraph/validation/rules/satisfiablity/graph.d.cts +9 -5
  86. package/typings/supergraph/validation/rules/satisfiablity/graph.d.ts +9 -5
  87. package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.cts +5 -4
  88. package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.ts +5 -4
  89. package/typings/supergraph/validation/rules/satisfiablity/moves.d.cts +9 -6
  90. package/typings/supergraph/validation/rules/satisfiablity/moves.d.ts +9 -6
  91. package/typings/supergraph/validation/rules/satisfiablity/selection.d.cts +36 -0
  92. package/typings/supergraph/validation/rules/satisfiablity/selection.d.ts +36 -0
  93. package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.cts +2 -2
  94. package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.ts +2 -2
  95. package/typings/supergraph/validation/rules/types-of-the-same-kind-rule.d.cts +4 -0
  96. package/typings/supergraph/validation/rules/types-of-the-same-kind-rule.d.ts +4 -0
  97. package/typings/utils/version.d.cts +3 -0
  98. package/typings/utils/version.d.ts +3 -0
  99. package/typings/supergraph/validation/rules/satisfiablity/fields.d.cts +0 -33
  100. package/typings/supergraph/validation/rules/satisfiablity/fields.d.ts +0 -33
package/README.md CHANGED
@@ -179,31 +179,18 @@ Your feedback and bug reports are welcome and appreciated.
179
179
  - ✅ `REQUIRES_DIRECTIVE_IN_FIELDS_ARG`
180
180
  - ✅ `TYPE_DEFINITION_INVALID`
181
181
  - ✅ `OVERRIDE_COLLISION_WITH_ANOTHER_DIRECTIVE`
182
+ - ✅ `INTERFACE_OBJECT_USAGE_ERROR`
183
+ - ✅ `REQUIRED_INACCESSIBLE`
184
+ - ✅ `SATISFIABILITY_ERROR`
182
185
 
183
186
  ### TODOs
184
187
 
185
- - [ ] `INTERFACE_OBJECT_USAGE_ERROR`
186
188
  - [ ] `INTERFACE_FIELD_NO_IMPLEM`
187
- - [ ] `SATISFIABILITY_ERROR`
188
189
  - [ ] `DISALLOWED_INACCESSIBLE`
189
- - [ ] `DOWNSTREAM_SERVICE_ERROR`
190
190
  - [ ] `EXTERNAL_ARGUMENT_DEFAULT_MISMATCH`
191
191
  - [ ] `EXTERNAL_ARGUMENT_TYPE_MISMATCH`
192
192
  - [ ] `EXTERNAL_COLLISION_WITH_ANOTHER_DIRECTIVE`
193
193
  - [ ] `IMPLEMENTED_BY_INACCESSIBLE`
194
- - [ ] `INVALID_FEDERATION_SUPERGRAPH`
195
194
  - [ ] `LINK_IMPORT_NAME_MISMATCH`
196
- - [ ] `REQUIRED_INACCESSIBLE`
197
- - [ ] `SHAREABLE_HAS_MISMATCHED_RUNTIME_TYPES`
198
- - [ ] `UNSUPPORTED_FEATURE`
199
195
  - [ ] `UNSUPPORTED_LINKED_FEATURE`
200
196
  - [ ] `TYPE_WITH_ONLY_UNUSED_EXTERNAL`
201
- - [ ] `SATISFIABILITY_ERROR` - deeply nested key fields
202
- - [ ] `SATISFIABILITY_ERROR` - fragments in keys
203
- - [ ] `SATISFIABILITY_ERROR` - support interfaces... (kill me)
204
- - [ ] `SATISFIABILITY_ERROR` - @require - check if fields defined by @require can be resolved by
205
- current subgraph or by moving to other subgraphs.
206
- - [ ] `SATISFIABILITY_ERROR` - @provides?
207
- - [ ] more accurate key fields comparison (I did string ≠ string but we need to make it better)
208
- - [ ] support `@interfaceObject`
209
- - [ ] support `[String!]!` and `[String!]` comparison, not only `String!` vs `String`
@@ -111,7 +111,7 @@ function visitFields({ context, selectionSet, typeDefinition, interceptField, in
111
111
  interceptUnknownField,
112
112
  interceptInterfaceType,
113
113
  });
114
- break;
114
+ continue;
115
115
  }
116
116
  const selectionFieldDef = selection.name.value === '__typename'
117
117
  ? {
@@ -42,14 +42,23 @@ function createSubgraphStateBuilder(graph, typeDefs, version, links) {
42
42
  version,
43
43
  };
44
44
  const schemaDef = typeDefs.definitions.find(isSchemaDefinition);
45
+ const leafTypeNames = new Set(specifiedScalars);
46
+ for (const typeDef of typeDefs.definitions) {
47
+ if (typeDef.kind === graphql_1.Kind.SCALAR_TYPE_DEFINITION ||
48
+ typeDef.kind === graphql_1.Kind.SCALAR_TYPE_EXTENSION ||
49
+ typeDef.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION ||
50
+ typeDef.kind === graphql_1.Kind.ENUM_TYPE_EXTENSION) {
51
+ leafTypeNames.add(typeDef.name.value);
52
+ }
53
+ }
45
54
  const expectedQueryTypeName = decideOnRootTypeName(schemaDef, graphql_1.OperationTypeNode.QUERY, 'Query');
46
55
  const expectedMutationTypeName = decideOnRootTypeName(schemaDef, graphql_1.OperationTypeNode.MUTATION, 'Mutation');
47
56
  const expectedSubscriptionTypeName = decideOnRootTypeName(schemaDef, graphql_1.OperationTypeNode.SUBSCRIPTION, 'Subscription');
48
57
  const composedDirectives = new Set();
49
58
  const directiveBuilder = directiveFactory(state);
50
59
  const scalarTypeBuilder = scalarTypeFactory(state);
51
- const objectTypeBuilder = objectTypeFactory(state, renameObjectType);
52
60
  const interfaceTypeBuilder = interfaceTypeFactory(state);
61
+ const objectTypeBuilder = objectTypeFactory(state, renameObjectType, interfaceTypeBuilder, isInterfaceObject);
53
62
  const inputObjectTypeBuilder = inputObjectTypeFactory(state);
54
63
  const unionTypeBuilder = unionTypeFactory(state);
55
64
  const enumTypeBuilder = enumTypeFactory(state);
@@ -65,7 +74,18 @@ function createSubgraphStateBuilder(graph, typeDefs, version, links) {
65
74
  }
66
75
  return typeName;
67
76
  }
77
+ function isInterfaceObject(typeName) {
78
+ const found = state.types.get(typeName);
79
+ if (!found) {
80
+ return false;
81
+ }
82
+ if (found.kind !== TypeKind.INTERFACE) {
83
+ return false;
84
+ }
85
+ return found.isInterfaceObject;
86
+ }
68
87
  return {
88
+ isInterfaceObject,
69
89
  directive: directiveBuilder,
70
90
  scalarType: scalarTypeBuilder,
71
91
  objectType: objectTypeBuilder,
@@ -92,18 +112,26 @@ function createSubgraphStateBuilder(graph, typeDefs, version, links) {
92
112
  const isObjectType = typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
93
113
  typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION;
94
114
  const outputTypeName = resolveTypeName(node.type);
115
+ const isLeaf = leafTypeNames.has(outputTypeName);
95
116
  const referencesEnumType = enumTypesByName.has(outputTypeName);
96
117
  if (referencesEnumType) {
97
118
  enumTypeBuilder.setReferencedByOutputType(outputTypeName, `${typeDef.name.value}.${node.name.value}`);
98
119
  }
99
- if (isInterfaceType) {
120
+ if (isInterfaceType || isInterfaceObject(typeDef.name.value)) {
100
121
  interfaceTypeBuilder.field.setType(typeDef.name.value, node.name.value, (0, helpers_js_1.printOutputType)(node.type));
122
+ interfaceTypeBuilder.field.setUsed(typeDef.name.value, node.name.value);
123
+ if (isLeaf) {
124
+ interfaceTypeBuilder.field.setLeaf(typeDef.name.value, node.name.value);
125
+ }
101
126
  return;
102
127
  }
103
128
  if (!isObjectType) {
104
129
  throw new Error(`Expected to find an object type`);
105
130
  }
106
131
  objectTypeBuilder.field.setType(typeDef.name.value, node.name.value, (0, helpers_js_1.printOutputType)(node.type));
132
+ if (isLeaf) {
133
+ objectTypeBuilder.field.setLeaf(typeDef.name.value, node.name.value);
134
+ }
107
135
  if (typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
108
136
  objectTypeBuilder.field.setExtension(typeDef.name.value, node.name.value);
109
137
  }
@@ -155,6 +183,16 @@ function createSubgraphStateBuilder(graph, typeDefs, version, links) {
155
183
  }
156
184
  },
157
185
  ObjectTypeDefinition(node) {
186
+ if (hasInterfaceObjectDirective(node)) {
187
+ interfaceTypeBuilder.setDefinition(node.name.value);
188
+ interfaceTypeBuilder.setInterfaceObject(node.name.value);
189
+ if (node.interfaces) {
190
+ for (const interfaceNode of node.interfaces) {
191
+ interfaceTypeBuilder.setInterface(node.name.value, interfaceNode.name.value);
192
+ }
193
+ }
194
+ return;
195
+ }
158
196
  objectTypeBuilder.setDefinition(node.name.value);
159
197
  if (node.name.value === expectedQueryTypeName) {
160
198
  state.schema.queryType = renameObjectType(node.name.value);
@@ -172,6 +210,8 @@ function createSubgraphStateBuilder(graph, typeDefs, version, links) {
172
210
  }
173
211
  },
174
212
  ObjectTypeExtension(node) {
213
+ if (hasInterfaceObjectDirective(node)) {
214
+ }
175
215
  if (node.name.value === expectedQueryTypeName) {
176
216
  state.schema.queryType = renameObjectType(node.name.value);
177
217
  }
@@ -539,12 +579,18 @@ function scalarTypeFactory(state) {
539
579
  },
540
580
  };
541
581
  }
542
- function objectTypeFactory(state, renameObject) {
582
+ function objectTypeFactory(state, renameObject, interfaceTypeBuilder, isInterfaceObject) {
543
583
  return {
544
584
  setDefinition(typeName) {
585
+ if (isInterfaceObject(typeName)) {
586
+ return interfaceTypeBuilder.setDefinition(typeName);
587
+ }
545
588
  getOrCreateObjectType(state, renameObject, typeName).isDefinition = true;
546
589
  },
547
590
  setExtension(typeName, extensionType) {
591
+ if (isInterfaceObject(typeName)) {
592
+ return interfaceTypeBuilder.setExtension(typeName);
593
+ }
548
594
  const objectType = getOrCreateObjectType(state, renameObject, typeName);
549
595
  objectType.extension = true;
550
596
  if (objectType.extensionType !== '@extends') {
@@ -552,9 +598,15 @@ function objectTypeFactory(state, renameObject) {
552
598
  }
553
599
  },
554
600
  setDescription(typeName, description) {
601
+ if (isInterfaceObject(typeName)) {
602
+ return interfaceTypeBuilder.setDescription(typeName, description);
603
+ }
555
604
  getOrCreateObjectType(state, renameObject, typeName).description = description;
556
605
  },
557
606
  setExternal(typeName) {
607
+ if (isInterfaceObject(typeName)) {
608
+ return;
609
+ }
558
610
  const objectType = getOrCreateObjectType(state, renameObject, typeName);
559
611
  objectType.external = true;
560
612
  for (const field of objectType.fields.values()) {
@@ -562,10 +614,16 @@ function objectTypeFactory(state, renameObject) {
562
614
  }
563
615
  },
564
616
  setInterface(typeName, interfaceName) {
617
+ if (isInterfaceObject(typeName)) {
618
+ return interfaceTypeBuilder.setInterface(typeName, interfaceName);
619
+ }
565
620
  getOrCreateObjectType(state, renameObject, typeName).interfaces.add(interfaceName);
566
621
  getOrCreateInterfaceType(state, interfaceName).implementedBy.add(typeName);
567
622
  },
568
623
  setKey(typeName, fields, fieldsUsedInKey, resolvable) {
624
+ if (isInterfaceObject(typeName)) {
625
+ return interfaceTypeBuilder.setKey(typeName, fields, fieldsUsedInKey, resolvable);
626
+ }
569
627
  const objectType = getOrCreateObjectType(state, renameObject, typeName);
570
628
  objectType.keys.push({ fields, resolvable });
571
629
  for (const field of fieldsUsedInKey) {
@@ -573,110 +631,212 @@ function objectTypeFactory(state, renameObject) {
573
631
  }
574
632
  },
575
633
  setInaccessible(typeName) {
634
+ if (isInterfaceObject(typeName)) {
635
+ return interfaceTypeBuilder.setInaccessible(typeName);
636
+ }
576
637
  const objectType = getOrCreateObjectType(state, renameObject, typeName);
577
638
  objectType.inaccessible = true;
578
639
  },
579
640
  setAuthenticated(typeName) {
641
+ if (isInterfaceObject(typeName)) {
642
+ return interfaceTypeBuilder.setAuthenticated(typeName);
643
+ }
580
644
  const objectType = getOrCreateObjectType(state, renameObject, typeName);
581
645
  objectType.authenticated = true;
582
646
  },
583
647
  setPolicies(typeName, policies) {
648
+ if (isInterfaceObject(typeName)) {
649
+ return interfaceTypeBuilder.setPolicies(typeName, policies);
650
+ }
584
651
  getOrCreateObjectType(state, renameObject, typeName).policies.push(...policies);
585
652
  },
586
653
  setScopes(typeName, scopes) {
654
+ if (isInterfaceObject(typeName)) {
655
+ return interfaceTypeBuilder.setScopes(typeName, scopes);
656
+ }
587
657
  getOrCreateObjectType(state, renameObject, typeName).scopes.push(...scopes);
588
658
  },
589
659
  setShareable(typeName) {
660
+ if (isInterfaceObject(typeName)) {
661
+ return;
662
+ }
590
663
  getOrCreateObjectType(state, renameObject, typeName).shareable = true;
591
664
  },
592
665
  setTag(typeName, tag) {
666
+ if (isInterfaceObject(typeName)) {
667
+ return interfaceTypeBuilder.setTag(typeName, tag);
668
+ }
593
669
  getOrCreateObjectType(state, renameObject, typeName).tags.add(tag);
594
670
  },
595
671
  setDirective(typeName, directive) {
672
+ if (isInterfaceObject(typeName)) {
673
+ return interfaceTypeBuilder.setDirective(typeName, directive);
674
+ }
596
675
  getOrCreateObjectType(state, renameObject, typeName).ast.directives.push(directive);
597
676
  },
598
677
  field: {
599
678
  setType(typeName, fieldName, fieldType) {
679
+ if (isInterfaceObject(typeName)) {
680
+ return interfaceTypeBuilder.field.setType(typeName, fieldName, fieldType);
681
+ }
600
682
  getOrCreateObjectField(state, renameObject, typeName, fieldName).type = fieldType;
601
683
  },
684
+ setLeaf(typeName, fieldName) {
685
+ if (isInterfaceObject(typeName)) {
686
+ return interfaceTypeBuilder.field.setLeaf(typeName, fieldName);
687
+ }
688
+ getOrCreateObjectField(state, renameObject, typeName, fieldName).isLeaf = true;
689
+ },
602
690
  setExtension(typeName, fieldName) {
691
+ if (isInterfaceObject(typeName)) {
692
+ return;
693
+ }
603
694
  getOrCreateObjectField(state, renameObject, typeName, fieldName).extension = true;
604
695
  },
605
696
  setDirective(typeName, fieldName, directive) {
697
+ if (isInterfaceObject(typeName)) {
698
+ return interfaceTypeBuilder.field.setDirective(typeName, fieldName, directive);
699
+ }
606
700
  getOrCreateObjectField(state, renameObject, typeName, fieldName).ast.directives.push(directive);
607
701
  },
608
702
  setDescription(typeName, fieldName, description) {
703
+ if (isInterfaceObject(typeName)) {
704
+ return interfaceTypeBuilder.field.setDescription(typeName, fieldName, description);
705
+ }
609
706
  getOrCreateObjectField(state, renameObject, typeName, fieldName).description = description;
610
707
  },
611
708
  setDeprecated(typeName, fieldName, reason) {
709
+ if (isInterfaceObject(typeName)) {
710
+ return interfaceTypeBuilder.field.setDeprecated(typeName, fieldName, reason);
711
+ }
612
712
  getOrCreateObjectField(state, renameObject, typeName, fieldName).deprecated = {
613
713
  reason,
614
714
  deprecated: true,
615
715
  };
616
716
  },
617
717
  setAuthenticated(typeName, fieldName) {
718
+ if (isInterfaceObject(typeName)) {
719
+ return interfaceTypeBuilder.field.setAuthenticated(typeName, fieldName);
720
+ }
618
721
  getOrCreateObjectField(state, renameObject, typeName, fieldName).authenticated = true;
619
722
  },
620
723
  setPolicies(typeName, fieldName, policies) {
724
+ if (isInterfaceObject(typeName)) {
725
+ return interfaceTypeBuilder.field.setPolicies(typeName, fieldName, policies);
726
+ }
621
727
  getOrCreateObjectField(state, renameObject, typeName, fieldName).policies.push(...policies);
622
728
  },
623
729
  setScopes(typeName, fieldName, scopes) {
730
+ if (isInterfaceObject(typeName)) {
731
+ return interfaceTypeBuilder.field.setScopes(typeName, fieldName, scopes);
732
+ }
624
733
  getOrCreateObjectField(state, renameObject, typeName, fieldName).scopes.push(...scopes);
625
734
  },
626
735
  setExternal(typeName, fieldName) {
736
+ if (isInterfaceObject(typeName)) {
737
+ return interfaceTypeBuilder.field.setExternal(typeName, fieldName);
738
+ }
627
739
  getOrCreateObjectField(state, renameObject, typeName, fieldName).external = true;
628
740
  },
629
741
  setInaccessible(typeName, fieldName) {
742
+ if (isInterfaceObject(typeName)) {
743
+ return interfaceTypeBuilder.field.setInaccessible(typeName, fieldName);
744
+ }
630
745
  getOrCreateObjectField(state, renameObject, typeName, fieldName).inaccessible = true;
631
746
  },
632
747
  setOverride(typeName, fieldName, override) {
748
+ if (isInterfaceObject(typeName)) {
749
+ return interfaceTypeBuilder.field.setOverride(typeName, fieldName, override);
750
+ }
633
751
  getOrCreateObjectField(state, renameObject, typeName, fieldName).override = override;
634
752
  },
635
753
  setProvides(typeName, fieldName, provides) {
754
+ if (isInterfaceObject(typeName)) {
755
+ return;
756
+ }
636
757
  getOrCreateObjectField(state, renameObject, typeName, fieldName).provides = provides;
637
758
  },
638
759
  setRequires(typeName, fieldName, requires) {
760
+ if (isInterfaceObject(typeName)) {
761
+ return interfaceTypeBuilder.field.setRequires(typeName, fieldName, requires);
762
+ }
639
763
  getOrCreateObjectField(state, renameObject, typeName, fieldName).requires = requires;
640
764
  },
641
765
  markAsProvided(typeName, fieldName) {
766
+ if (isInterfaceObject(typeName)) {
767
+ return;
768
+ }
642
769
  getOrCreateObjectField(state, renameObject, typeName, fieldName).provided = true;
643
770
  },
644
771
  markedAsRequired(typeName, fieldName) {
772
+ if (isInterfaceObject(typeName)) {
773
+ return;
774
+ }
645
775
  getOrCreateObjectField(state, renameObject, typeName, fieldName).required = true;
646
776
  },
647
777
  setShareable(typeName, fieldName) {
778
+ if (isInterfaceObject(typeName)) {
779
+ return interfaceTypeBuilder.field.setShareable(typeName, fieldName);
780
+ }
648
781
  getOrCreateObjectField(state, renameObject, typeName, fieldName).shareable = true;
649
782
  },
650
783
  setTag(typeName, fieldName, tag) {
784
+ if (isInterfaceObject(typeName)) {
785
+ return interfaceTypeBuilder.field.setTag(typeName, fieldName, tag);
786
+ }
651
787
  getOrCreateObjectField(state, renameObject, typeName, fieldName).tags.add(tag);
652
788
  },
653
789
  setUsed(typeName, fieldName) {
790
+ if (isInterfaceObject(typeName)) {
791
+ return interfaceTypeBuilder.field.setUsed(typeName, fieldName);
792
+ }
654
793
  getOrCreateObjectField(state, renameObject, typeName, fieldName).used = true;
655
794
  },
656
795
  arg: {
657
796
  setType(typeName, fieldName, argName, argType) {
797
+ if (isInterfaceObject(typeName)) {
798
+ return interfaceTypeBuilder.field.arg.setType(typeName, fieldName, argName, argType);
799
+ }
658
800
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).type =
659
801
  argType;
660
802
  },
661
803
  setDescription(typeName, fieldName, argName, description) {
804
+ if (isInterfaceObject(typeName)) {
805
+ return interfaceTypeBuilder.field.arg.setDescription(typeName, fieldName, argName, description);
806
+ }
662
807
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).description = description;
663
808
  },
664
809
  setDeprecated(typeName, fieldName, argName, reason) {
810
+ if (isInterfaceObject(typeName)) {
811
+ return interfaceTypeBuilder.field.arg.setDeprecated(typeName, fieldName, argName, reason);
812
+ }
665
813
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).deprecated = {
666
814
  reason,
667
815
  deprecated: true,
668
816
  };
669
817
  },
670
818
  setDirective(typeName, fieldName, argName, directive) {
819
+ if (isInterfaceObject(typeName)) {
820
+ return interfaceTypeBuilder.field.arg.setDirective(typeName, fieldName, argName, directive);
821
+ }
671
822
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).ast.directives.push(directive);
672
823
  },
673
824
  setDefaultValue(typeName, fieldName, argName, defaultValue) {
825
+ if (isInterfaceObject(typeName)) {
826
+ return interfaceTypeBuilder.field.arg.setDefaultValue(typeName, fieldName, argName, defaultValue);
827
+ }
674
828
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).defaultValue = defaultValue;
675
829
  },
676
830
  setInaccessible(typeName, fieldName, argName) {
831
+ if (isInterfaceObject(typeName)) {
832
+ return interfaceTypeBuilder.field.arg.setInaccessible(typeName, fieldName, argName);
833
+ }
677
834
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).inaccessible = true;
678
835
  },
679
836
  setTag(typeName, fieldName, argName, tag) {
837
+ if (isInterfaceObject(typeName)) {
838
+ return interfaceTypeBuilder.field.arg.setTag(typeName, fieldName, argName, tag);
839
+ }
680
840
  getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).tags.add(tag);
681
841
  },
682
842
  },
@@ -694,8 +854,15 @@ function interfaceTypeFactory(state) {
694
854
  setInterface(typeName, interfaceName) {
695
855
  getOrCreateInterfaceType(state, typeName).interfaces.add(interfaceName);
696
856
  },
857
+ setInterfaceObject(typeName) {
858
+ getOrCreateInterfaceType(state, typeName).isInterfaceObject = true;
859
+ },
697
860
  setKey(typeName, fields, fieldsUsedInKey, resolvable) {
698
- getOrCreateInterfaceType(state, typeName).keys.push({ fields, resolvable });
861
+ const interfaceType = getOrCreateInterfaceType(state, typeName);
862
+ interfaceType.keys.push({ fields, resolvable });
863
+ for (const field of fieldsUsedInKey) {
864
+ interfaceType.fieldsUsedAsKeys.add(field);
865
+ }
699
866
  },
700
867
  setInaccessible(typeName) {
701
868
  const objectType = getOrCreateInterfaceType(state, typeName);
@@ -724,6 +891,9 @@ function interfaceTypeFactory(state) {
724
891
  setType(typeName, fieldName, fieldType) {
725
892
  getOrCreateInterfaceField(state, typeName, fieldName).type = fieldType;
726
893
  },
894
+ setLeaf(typeName, fieldName) {
895
+ getOrCreateInterfaceField(state, typeName, fieldName).isLeaf = true;
896
+ },
727
897
  setExternal(typeName, fieldName) {
728
898
  getOrCreateInterfaceField(state, typeName, fieldName).external = true;
729
899
  },
@@ -1020,6 +1190,7 @@ function getOrCreateInterfaceType(state, typeName) {
1020
1190
  kind: TypeKind.INTERFACE,
1021
1191
  name: typeName,
1022
1192
  fields: new Map(),
1193
+ fieldsUsedAsKeys: new Set(),
1023
1194
  extension: false,
1024
1195
  keys: [],
1025
1196
  inaccessible: false,
@@ -1030,6 +1201,7 @@ function getOrCreateInterfaceType(state, typeName) {
1030
1201
  interfaces: new Set(),
1031
1202
  implementedBy: new Set(),
1032
1203
  isDefinition: false,
1204
+ isInterfaceObject: false,
1033
1205
  ast: {
1034
1206
  directives: [],
1035
1207
  },
@@ -1111,6 +1283,8 @@ function getOrCreateObjectField(state, renameObject, typeName, fieldName) {
1111
1283
  const field = {
1112
1284
  name: fieldName,
1113
1285
  type: MISSING,
1286
+ usedAsKey: false,
1287
+ isLeaf: false,
1114
1288
  external: false,
1115
1289
  inaccessible: false,
1116
1290
  authenticated: false,
@@ -1141,6 +1315,8 @@ function getOrCreateInterfaceField(state, typeName, fieldName) {
1141
1315
  }
1142
1316
  const field = {
1143
1317
  name: fieldName,
1318
+ usedAsKey: false,
1319
+ isLeaf: false,
1144
1320
  type: MISSING,
1145
1321
  external: false,
1146
1322
  inaccessible: false,
@@ -1251,3 +1427,6 @@ function decideOnRootTypeName(schemaDef, kind, defaultName) {
1251
1427
  return (schemaDef?.operationTypes?.find(operationType => operationType.operation === kind)?.type.name
1252
1428
  .value ?? defaultName);
1253
1429
  }
1430
+ function hasInterfaceObjectDirective(node) {
1431
+ return node.directives?.some(d => d.name.value === 'interfaceObject');
1432
+ }
@@ -12,9 +12,25 @@ function InterfaceObjectRules(context) {
12
12
  if (!context.isAvailableFederationDirective('interfaceObject', node)) {
13
13
  return;
14
14
  }
15
- context.reportError(new graphql_1.GraphQLError(`@interfaceObject is not yet supported. See https://github.com/the-guild-org/federation/issues/7`, {
16
- extensions: { code: 'UNSUPPORTED_FEATURE' },
17
- }));
15
+ if (context.satisfiesVersionRange('< v2.3')) {
16
+ context.reportError(new graphql_1.GraphQLError(`@interfaceObject is not yet supported. See https://github.com/the-guild-org/federation/issues/7`, {
17
+ extensions: { code: 'UNSUPPORTED_FEATURE' },
18
+ }));
19
+ return;
20
+ }
21
+ const typeDef = context.typeNodeInfo.getTypeDef();
22
+ if (!typeDef) {
23
+ return;
24
+ }
25
+ if (typeDef.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
26
+ typeDef.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
27
+ return;
28
+ }
29
+ if (!typeDef.directives?.some(d => d.name.value === 'key')) {
30
+ context.reportError(new graphql_1.GraphQLError(`The @interfaceObject directive can only be applied to entity types but type "${typeDef.name.value}" has no @key in this subgraph.`, {
31
+ extensions: { code: 'INTERFACE_OBJECT_USAGE_ERROR' },
32
+ }));
33
+ }
18
34
  },
19
35
  };
20
36
  }
@@ -18,9 +18,10 @@ function KeyRules(context) {
18
18
  return;
19
19
  }
20
20
  const typeCoordinate = typeDef.name.value;
21
- const usedOnInterface = typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION ||
22
- typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION;
23
21
  const usedOnObject = typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION;
22
+ const usedOnInterface = typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION ||
23
+ typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION ||
24
+ (usedOnObject && context.stateBuilder.isInterfaceObject(typeDef.name.value));
24
25
  if (!usedOnObject && !usedOnInterface) {
25
26
  return;
26
27
  }
@@ -55,8 +55,9 @@ function PolicyRule(context) {
55
55
  if (!typeDef) {
56
56
  throw new Error('Could not find the parent type of the field annotated with @policy');
57
57
  }
58
- if (typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
59
- typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
58
+ if ((typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
59
+ typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) &&
60
+ !context.stateBuilder.isInterfaceObject(typeDef.name.value)) {
60
61
  context.stateBuilder.objectType.field.setPolicies(typeDef.name.value, parent.name.value, policies);
61
62
  }
62
63
  break;
@@ -17,8 +17,9 @@ function ShareableRules(context) {
17
17
  if (!typeDef) {
18
18
  return;
19
19
  }
20
- if (typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
21
- typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
20
+ if ((typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
21
+ typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) &&
22
+ !context.stateBuilder.isInterfaceObject(typeDef.name.value)) {
22
23
  if (fieldDef) {
23
24
  context.stateBuilder.objectType.field.setShareable(typeDef.name.value, fieldDef.name.value);
24
25
  }
@@ -45,7 +45,8 @@ function TagRules(context) {
45
45
  throw new Error('Could not find the parent type of the field annotated with @tag');
46
46
  }
47
47
  if (typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION ||
48
- typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION) {
48
+ typeDef.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION ||
49
+ context.stateBuilder.isInterfaceObject(typeDef.name.value)) {
49
50
  context.stateBuilder.interfaceType.field.setTag(typeDef.name.value, parent.name.value, tag);
50
51
  }
51
52
  else {
@@ -22,6 +22,10 @@ function KnownFederationDirectivesRule(context) {
22
22
  return {
23
23
  Directive(node) {
24
24
  const name = node.name.value;
25
+ if (!availableDirectivesSet.has(name) && name === 'interfaceObject') {
26
+ context.reportError(new graphql_1.GraphQLError(`Unknown directive "@interfaceObject". If you meant the "@interfaceObject" federation 2 directive, note that this schema is a federation 1 schema. To be a federation 2 schema, it needs to @link to the federation specification v2.`, { nodes: node, extensions: { code: 'INVALID_GRAPHQL' } }));
27
+ return;
28
+ }
25
29
  if (!availableDirectivesSet.has(name) &&
26
30
  knownDirectivesSet.has(name) &&
27
31
  !name.startsWith('federation__')) {
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OnlyInterfaceImplementationRule = void 0;
4
+ const graphql_1 = require("graphql");
5
+ function OnlyInterfaceImplementationRule(context) {
6
+ const { definitions } = context.getDocument();
7
+ let filled = false;
8
+ const typeNameToKind = new Map();
9
+ function fillTypeNameToKindMap() {
10
+ for (const node of definitions) {
11
+ switch (node.kind) {
12
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
13
+ case graphql_1.Kind.OBJECT_TYPE_EXTENSION:
14
+ typeNameToKind.set(node.name.value, 'ObjectType');
15
+ break;
16
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
17
+ case graphql_1.Kind.INTERFACE_TYPE_EXTENSION:
18
+ typeNameToKind.set(node.name.value, 'InterfaceType');
19
+ break;
20
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
21
+ case graphql_1.Kind.UNION_TYPE_EXTENSION:
22
+ typeNameToKind.set(node.name.value, 'UnionType');
23
+ break;
24
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
25
+ case graphql_1.Kind.ENUM_TYPE_EXTENSION:
26
+ typeNameToKind.set(node.name.value, 'EnumType');
27
+ break;
28
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
29
+ case graphql_1.Kind.SCALAR_TYPE_EXTENSION:
30
+ typeNameToKind.set(node.name.value, 'ScalarType');
31
+ break;
32
+ case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
33
+ case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION:
34
+ typeNameToKind.set(node.name.value, 'InputObjectType');
35
+ break;
36
+ }
37
+ }
38
+ filled = true;
39
+ }
40
+ function findKindByName(typeName) {
41
+ if (!filled) {
42
+ fillTypeNameToKindMap();
43
+ }
44
+ return typeNameToKind.get(typeName);
45
+ }
46
+ function check(node) {
47
+ if (!node.interfaces) {
48
+ return;
49
+ }
50
+ for (const interfaceNode of node.interfaces) {
51
+ const interfaceName = interfaceNode.name.value;
52
+ const kind = findKindByName(interfaceName);
53
+ if (kind && kind !== 'InterfaceType') {
54
+ context.reportError(new graphql_1.GraphQLError(`Cannot implement non-interface type ${interfaceName} (of type ObjectType)`, {
55
+ extensions: {
56
+ code: 'INVALID_GRAPHQL',
57
+ },
58
+ }));
59
+ }
60
+ }
61
+ }
62
+ return {
63
+ ObjectTypeDefinition: check,
64
+ ObjectTypeExtension: check,
65
+ };
66
+ }
67
+ exports.OnlyInterfaceImplementationRule = OnlyInterfaceImplementationRule;
@@ -26,6 +26,7 @@ const known_federation_directive_rule_js_1 = require("./rules/known-federation-d
26
26
  const known_root_type_rule_js_1 = require("./rules/known-root-type-rule.js");
27
27
  const known_type_names_rule_js_1 = require("./rules/known-type-names-rule.js");
28
28
  const lone_schema_definition_rule_js_1 = require("./rules/lone-schema-definition-rule.js");
29
+ const only_interface_implementation_rule_js_1 = require("./rules/only-interface-implementation-rule.js");
29
30
  const provided_arguments_on_directives_rule_js_1 = require("./rules/provided-arguments-on-directives-rule.js");
30
31
  const provided_required_arguments_on_directives_rule_js_1 = require("./rules/provided-required-arguments-on-directives-rule.js");
31
32
  const query_root_type_inaccessible_rule_js_1 = require("./rules/query-root-type-inaccessible-rule.js");
@@ -116,6 +117,7 @@ function validateSubgraph(subgraph, stateBuilder, federation, __internal) {
116
117
  compose_directive_js_1.ComposeDirectiveRules,
117
118
  ];
118
119
  const graphqlRules = [
120
+ only_interface_implementation_rule_js_1.OnlyInterfaceImplementationRule,
119
121
  lone_schema_definition_rule_js_1.LoneSchemaDefinitionRule,
120
122
  unique_operation_types_rule_js_1.UniqueOperationTypesRule,
121
123
  unique_type_names_rule_js_1.UniqueTypeNamesRule,