@theguild/federation-composition 0.9.0 → 0.10.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 (126) hide show
  1. package/README.md +2 -1
  2. package/cjs/specifications/link.js +40 -5
  3. package/cjs/subgraph/helpers.js +2 -2
  4. package/cjs/subgraph/state.js +8 -0
  5. package/cjs/subgraph/validation/rules/elements/provides.js +8 -6
  6. package/cjs/subgraph/validation/rules/elements/requires.js +9 -7
  7. package/cjs/subgraph/validation/validate-state.js +9 -3
  8. package/cjs/subgraph/validation/validate-subgraph.js +4 -2
  9. package/cjs/subgraph/validation/validation-context.js +10 -0
  10. package/cjs/supergraph/composition/directive.js +3 -0
  11. package/cjs/supergraph/composition/enum-type.js +3 -0
  12. package/cjs/supergraph/composition/input-object-type.js +3 -0
  13. package/cjs/supergraph/composition/interface-type.js +4 -0
  14. package/cjs/supergraph/composition/object-type.js +18 -20
  15. package/cjs/supergraph/composition/scalar-type.js +2 -0
  16. package/cjs/supergraph/composition/union-type.js +2 -0
  17. package/cjs/supergraph/state.js +24 -26
  18. package/cjs/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
  19. package/cjs/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
  20. package/cjs/supergraph/validation/rules/satisfiablity/constants.js +4 -0
  21. package/cjs/supergraph/validation/rules/satisfiablity/edge.js +64 -0
  22. package/cjs/supergraph/validation/rules/satisfiablity/errors.js +44 -0
  23. package/cjs/supergraph/validation/rules/satisfiablity/fields.js +147 -0
  24. package/cjs/supergraph/validation/rules/satisfiablity/finder.js +267 -0
  25. package/cjs/supergraph/validation/rules/satisfiablity/graph.js +675 -0
  26. package/cjs/supergraph/validation/rules/satisfiablity/helpers.js +41 -0
  27. package/cjs/supergraph/validation/rules/satisfiablity/move-validator.js +337 -0
  28. package/cjs/supergraph/validation/rules/satisfiablity/moves.js +52 -0
  29. package/cjs/supergraph/validation/rules/satisfiablity/node.js +89 -0
  30. package/cjs/supergraph/validation/rules/satisfiablity/operation-path.js +70 -0
  31. package/cjs/supergraph/validation/rules/satisfiablity/supergraph.js +37 -0
  32. package/cjs/supergraph/validation/rules/satisfiablity/walker.js +306 -0
  33. package/cjs/supergraph/validation/rules/satisfiablity-rule.js +45 -1081
  34. package/cjs/supergraph/validation/validate-supergraph.js +1 -1
  35. package/cjs/utils/logger.js +127 -0
  36. package/esm/specifications/link.js +40 -5
  37. package/esm/subgraph/helpers.js +2 -2
  38. package/esm/subgraph/state.js +8 -0
  39. package/esm/subgraph/validation/rules/elements/provides.js +8 -6
  40. package/esm/subgraph/validation/rules/elements/requires.js +9 -7
  41. package/esm/subgraph/validation/validate-state.js +9 -3
  42. package/esm/subgraph/validation/validate-subgraph.js +4 -2
  43. package/esm/subgraph/validation/validation-context.js +11 -1
  44. package/esm/supergraph/composition/directive.js +3 -0
  45. package/esm/supergraph/composition/enum-type.js +3 -0
  46. package/esm/supergraph/composition/input-object-type.js +3 -0
  47. package/esm/supergraph/composition/interface-type.js +4 -0
  48. package/esm/supergraph/composition/object-type.js +18 -20
  49. package/esm/supergraph/composition/scalar-type.js +2 -0
  50. package/esm/supergraph/composition/union-type.js +2 -0
  51. package/esm/supergraph/state.js +24 -26
  52. package/esm/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
  53. package/esm/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
  54. package/esm/supergraph/validation/rules/satisfiablity/constants.js +1 -0
  55. package/esm/supergraph/validation/rules/satisfiablity/edge.js +54 -0
  56. package/esm/supergraph/validation/rules/satisfiablity/errors.js +40 -0
  57. package/esm/supergraph/validation/rules/satisfiablity/fields.js +142 -0
  58. package/esm/supergraph/validation/rules/satisfiablity/finder.js +261 -0
  59. package/esm/supergraph/validation/rules/satisfiablity/graph.js +671 -0
  60. package/esm/supergraph/validation/rules/satisfiablity/helpers.js +35 -0
  61. package/esm/supergraph/validation/rules/satisfiablity/move-validator.js +333 -0
  62. package/esm/supergraph/validation/rules/satisfiablity/moves.js +46 -0
  63. package/esm/supergraph/validation/rules/satisfiablity/node.js +85 -0
  64. package/esm/supergraph/validation/rules/satisfiablity/operation-path.js +66 -0
  65. package/esm/supergraph/validation/rules/satisfiablity/supergraph.js +33 -0
  66. package/esm/supergraph/validation/rules/satisfiablity/walker.js +301 -0
  67. package/esm/supergraph/validation/rules/satisfiablity-rule.js +40 -1076
  68. package/esm/supergraph/validation/validate-supergraph.js +1 -1
  69. package/esm/utils/logger.js +119 -0
  70. package/package.json +2 -1
  71. package/typings/subgraph/state.d.cts +2 -0
  72. package/typings/subgraph/state.d.ts +2 -0
  73. package/typings/subgraph/validation/validate-state.d.cts +2 -1
  74. package/typings/subgraph/validation/validate-state.d.ts +2 -1
  75. package/typings/subgraph/validation/validation-context.d.cts +3 -0
  76. package/typings/subgraph/validation/validation-context.d.ts +3 -0
  77. package/typings/supergraph/composition/common.d.cts +2 -1
  78. package/typings/supergraph/composition/common.d.ts +2 -1
  79. package/typings/supergraph/composition/directive.d.cts +4 -0
  80. package/typings/supergraph/composition/directive.d.ts +4 -0
  81. package/typings/supergraph/composition/enum-type.d.cts +4 -0
  82. package/typings/supergraph/composition/enum-type.d.ts +4 -0
  83. package/typings/supergraph/composition/input-object-type.d.cts +4 -0
  84. package/typings/supergraph/composition/input-object-type.d.ts +4 -0
  85. package/typings/supergraph/composition/interface-type.d.cts +5 -0
  86. package/typings/supergraph/composition/interface-type.d.ts +5 -0
  87. package/typings/supergraph/composition/object-type.d.cts +5 -0
  88. package/typings/supergraph/composition/object-type.d.ts +5 -0
  89. package/typings/supergraph/composition/scalar-type.d.cts +3 -0
  90. package/typings/supergraph/composition/scalar-type.d.ts +3 -0
  91. package/typings/supergraph/composition/union-type.d.cts +3 -0
  92. package/typings/supergraph/composition/union-type.d.ts +3 -0
  93. package/typings/supergraph/state.d.cts +4 -4
  94. package/typings/supergraph/state.d.ts +4 -4
  95. package/typings/supergraph/validation/rules/satisfiablity/constants.d.cts +2 -0
  96. package/typings/supergraph/validation/rules/satisfiablity/constants.d.ts +2 -0
  97. package/typings/supergraph/validation/rules/satisfiablity/edge.d.cts +31 -0
  98. package/typings/supergraph/validation/rules/satisfiablity/edge.d.ts +31 -0
  99. package/typings/supergraph/validation/rules/satisfiablity/errors.d.cts +17 -0
  100. package/typings/supergraph/validation/rules/satisfiablity/errors.d.ts +17 -0
  101. package/typings/supergraph/validation/rules/satisfiablity/fields.d.cts +33 -0
  102. package/typings/supergraph/validation/rules/satisfiablity/fields.d.ts +33 -0
  103. package/typings/supergraph/validation/rules/satisfiablity/finder.d.cts +28 -0
  104. package/typings/supergraph/validation/rules/satisfiablity/finder.d.ts +28 -0
  105. package/typings/supergraph/validation/rules/satisfiablity/graph.d.cts +63 -0
  106. package/typings/supergraph/validation/rules/satisfiablity/graph.d.ts +63 -0
  107. package/typings/supergraph/validation/rules/satisfiablity/helpers.d.cts +7 -0
  108. package/typings/supergraph/validation/rules/satisfiablity/helpers.d.ts +7 -0
  109. package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.cts +25 -0
  110. package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.ts +25 -0
  111. package/typings/supergraph/validation/rules/satisfiablity/moves.d.cts +24 -0
  112. package/typings/supergraph/validation/rules/satisfiablity/moves.d.ts +24 -0
  113. package/typings/supergraph/validation/rules/satisfiablity/node.d.cts +31 -0
  114. package/typings/supergraph/validation/rules/satisfiablity/node.d.ts +31 -0
  115. package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.cts +29 -0
  116. package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.ts +29 -0
  117. package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.cts +14 -0
  118. package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.ts +14 -0
  119. package/typings/supergraph/validation/rules/satisfiablity/walker.d.cts +35 -0
  120. package/typings/supergraph/validation/rules/satisfiablity/walker.d.ts +35 -0
  121. package/typings/utils/logger.d.cts +33 -0
  122. package/typings/utils/logger.d.ts +33 -0
  123. package/cjs/utils/dependency-graph.js +0 -227
  124. package/esm/utils/dependency-graph.js +0 -222
  125. package/typings/utils/dependency-graph.d.cts +0 -31
  126. package/typings/utils/dependency-graph.d.ts +0 -31
@@ -1,778 +1,61 @@
1
- import { GraphQLError, Kind, OperationTypeNode, print, specifiedScalarTypes, } from 'graphql';
2
- import { parseFields } from '../../../subgraph/helpers.js';
3
- import { TypeKind } from '../../../subgraph/state.js';
4
- import { DepGraph } from '../../../utils/dependency-graph.js';
5
- import { isDefined } from '../../../utils/helpers.js';
1
+ import { GraphQLError, Kind, print, specifiedScalarTypes } from 'graphql';
6
2
  import { isList, isNonNull, stripNonNull, stripTypeModifiers } from '../../../utils/state.js';
7
- function isSatisfiableQueryPath(supergraphState, queryPath) {
8
- const root = queryPath[0];
9
- const leaf = queryPath[queryPath.length - 1];
10
- if ('fieldName' in root) {
11
- const rootType = supergraphState.objectTypes.get(root.typeName);
12
- if (!rootType) {
13
- throw new Error(`Type "${root.typeName}" not found in Supergraph state`);
14
- }
15
- const rootField = rootType.fields.get(root.fieldName);
16
- if (!rootField) {
17
- throw new Error(`Field "${root.typeName}.${root.fieldName}" not found in Supergraph state`);
18
- }
19
- const graphsWithRootField = Array.from(rootField.byGraph)
20
- .filter(([_, f]) => f.external === false)
21
- .map(([g, _]) => g);
22
- if (!('fieldName' in leaf)) {
23
- throw new Error('Leaf field is missing in the query path');
24
- }
25
- const leafType = supergraphState.objectTypes.get(leaf.typeName);
26
- if (!leafType) {
27
- throw new Error(`Type "${leaf.typeName}" not found in Supergraph state`);
28
- }
29
- const leafField = leafType.fields.get(leaf.fieldName);
30
- if (!leafField) {
31
- throw new Error(`Field "${leaf.typeName}.${leaf.fieldName}" not found in Supergraph state`);
32
- }
33
- if (graphsWithRootField.some(graphWithRootField => canTraverseToField(leafField.name, supergraphState, leafType, graphWithRootField))) {
34
- return true;
35
- }
36
- function canAdvance(stepIndex, fromGraphId) {
37
- const step = queryPath[stepIndex];
38
- if (!('fieldName' in step)) {
39
- return canAdvance(stepIndex + 1, fromGraphId);
40
- }
41
- const typeState = supergraphState.objectTypes.get(step.typeName);
42
- if (!typeState) {
43
- throw new Error(`Type "${step.typeName}" not found in Supergraph state`);
44
- }
45
- const fieldState = typeState.fields.get(step.fieldName);
46
- if (!fieldState) {
47
- throw new Error(`Field "${step.typeName}.${step.fieldName}" not found in Supergraph state`);
48
- }
49
- const accessibleTargetGraphs = new Set();
50
- if (!canTraverseToField(fieldState.name, supergraphState, typeState, fromGraphId, accessibleTargetGraphs)) {
51
- return false;
52
- }
53
- if (step === leaf) {
54
- return true;
55
- }
56
- return Array.from(accessibleTargetGraphs).some(graphId => canAdvance(stepIndex + 1, graphId));
57
- }
58
- const canResolveStepByStep = graphsWithRootField.some(graphId => {
59
- return canAdvance(0, graphId);
60
- });
61
- if (canResolveStepByStep) {
62
- return true;
63
- }
64
- }
65
- else {
66
- throw new Error('Root field is missing in the query path');
67
- }
68
- return false;
69
- }
70
- function canTraverseToField(fieldName, supergraphState, entityState, sourceGraphId, accessibleTargetGraphs = new Set(), visitedGraphs = new Set(), collectedFieldPaths = new Set()) {
71
- const objectTypeStateInSourceGraph = entityState.byGraph.get(sourceGraphId);
72
- const sourceGraphKeys = objectTypeStateInSourceGraph?.keys || [];
73
- const fieldState = entityState.fields.get(fieldName);
74
- if (!fieldState) {
75
- throw new Error(`Field "${entityState.name}.${fieldName}" not found in Supergraph state`);
76
- }
77
- const fieldStateInSourceGraph = fieldState.byGraph.get(sourceGraphId);
78
- if (fieldStateInSourceGraph && fieldStateInSourceGraph.external === false) {
79
- accessibleTargetGraphs.add(sourceGraphId);
80
- return true;
81
- }
82
- for (const targetGraphId of entityState.byGraph.keys()) {
83
- if (targetGraphId === sourceGraphId) {
84
- continue;
85
- }
86
- if (visitedGraphs.has(targetGraphId)) {
87
- continue;
88
- }
89
- const fieldState = entityState.fields.get(fieldName);
90
- if (!fieldState) {
91
- throw new Error(`Field "${entityState.name}.${fieldName}" not found in Supergraph state`);
92
- }
93
- const objectTypeStateInTargetGraph = entityState.byGraph.get(targetGraphId);
94
- const targetGraphKeys = objectTypeStateInTargetGraph?.keys || [];
95
- if (sourceGraphKeys.length === 0 && targetGraphKeys.length === 0) {
96
- continue;
97
- }
98
- for (const targetKey of targetGraphKeys) {
99
- if (!targetKey.resolvable) {
100
- continue;
101
- }
102
- const targetKeyFields = resolveFieldsFromFieldSet(targetKey.fields, entityState.name, targetGraphId, supergraphState);
103
- let resolvableFieldPaths = new Set();
104
- for (const [requiredFieldPath, { typeName, fieldName }] of targetKeyFields.pairs) {
105
- if (collectedFieldPaths.has(requiredFieldPath)) {
106
- resolvableFieldPaths.add(requiredFieldPath);
107
- continue;
108
- }
109
- const objectType = supergraphState.objectTypes.get(typeName);
110
- if (!objectType) {
111
- throw new Error(`Type "${typeName}" not found in Supergraph state`);
112
- }
113
- const field = objectType.fields.get(fieldName);
114
- if (!field) {
115
- throw new Error(`Field "${typeName}.${fieldName}" not found in Supergraph state`);
116
- }
117
- const fieldInGraph = field.byGraph.get(sourceGraphId);
118
- if (!fieldInGraph) {
119
- break;
120
- }
121
- const fieldInGraphIsExternal = fieldInGraph.external === true;
122
- const fieldIsDirectlyAccessed = requiredFieldPath.indexOf('.') === requiredFieldPath.lastIndexOf('.');
123
- if (fieldInGraphIsExternal && fieldIsDirectlyAccessed) {
124
- break;
125
- }
126
- resolvableFieldPaths.add(requiredFieldPath);
127
- }
128
- if (resolvableFieldPaths.size !== targetKeyFields.paths.size) {
129
- continue;
130
- }
131
- if (fieldState.byGraph.has(targetGraphId)) {
132
- accessibleTargetGraphs.add(targetGraphId);
133
- return true;
134
- }
135
- const canResolve = canTraverseToField(fieldName, supergraphState, entityState, targetGraphId, accessibleTargetGraphs, new Set([...visitedGraphs, targetGraphId]), new Set([...collectedFieldPaths, ...resolvableFieldPaths]));
136
- if (canResolve) {
137
- accessibleTargetGraphs.add(targetGraphId);
138
- return true;
139
- }
140
- }
141
- }
142
- return false;
143
- }
144
- function canGraphMoveToGraphByEntity(supergraphState, entityName, sourceGraphId, targetGraphId) {
145
- const objectTypeState = supergraphState.objectTypes.get(entityName);
146
- if (!objectTypeState) {
147
- throw new Error(`Type "${entityName}" not found in supergraph state`);
148
- }
149
- const objectTypeStateInSourceGraph = objectTypeState.byGraph.get(sourceGraphId);
150
- const objectTypeStateInTargetGraph = objectTypeState.byGraph.get(targetGraphId);
151
- const sourceGraphKeys = objectTypeStateInSourceGraph?.keys || [];
152
- const targetGraphKeys = objectTypeStateInTargetGraph?.keys || [];
153
- if (sourceGraphKeys.length === 0 && targetGraphKeys.length === 0) {
154
- return false;
155
- }
156
- if (sourceGraphKeys.length === 0) {
157
- return targetGraphKeys
158
- .filter(k => k.resolvable === true)
159
- .some(k => {
160
- const targetKeyFields = resolveFieldsFromFieldSet(k.fields, objectTypeState.name, targetGraphId, supergraphState);
161
- return Array.from(targetKeyFields.coordinates).every(fieldPath => {
162
- const [typeName, fieldName] = fieldPath.split('.');
163
- if (typeName === objectTypeState.name) {
164
- const fieldState = objectTypeState.fields.get(fieldName);
165
- if (!fieldState) {
166
- throw new Error(`Field "${fieldPath}" not found in object type "${typeName}"`);
167
- }
168
- const fieldStateByGraph = fieldState.byGraph.get(targetGraphId);
169
- if (!fieldStateByGraph) {
170
- throw new Error(`Field "${fieldPath}" not found in object type "${typeName}" in graph "${targetGraphId}"`);
171
- }
172
- return fieldStateByGraph.external === false;
173
- }
174
- const currentTypeState = supergraphState.objectTypes.get(typeName) ??
175
- supergraphState.interfaceTypes.get(typeName);
176
- if (!currentTypeState) {
177
- throw new Error(`Type "${typeName}" not found`);
178
- }
179
- const fieldState = currentTypeState.fields.get(fieldName);
180
- if (!fieldState) {
181
- throw new Error(`Field "${fieldPath}" not found in object type "${typeName}"`);
182
- }
183
- const fieldStateByGraph = fieldState.byGraph.get(targetGraphId);
184
- if (!fieldStateByGraph) {
185
- throw new Error(`Field "${fieldPath}" not found in object type "${typeName}" in graph "${targetGraphId}"`);
186
- }
187
- if ('external' in fieldStateByGraph) {
188
- return fieldStateByGraph.external === false;
189
- }
190
- return true;
191
- });
192
- });
193
- }
194
- return sourceGraphKeys.some(sourceGraphKey => {
195
- const sourceKeyFields = resolveFieldsFromFieldSet(sourceGraphKey.fields, objectTypeState.name, sourceGraphId, supergraphState);
196
- const hasNonKeyFields = Array.from(objectTypeState.fields).some(([_, fState]) => {
197
- const f = fState.byGraph.get(sourceGraphId);
198
- if (f && f.usedAsKey === false) {
199
- return true;
200
- }
201
- return false;
202
- });
203
- if (Array.from(sourceKeyFields.coordinates).every(fieldPath => {
204
- const [typeName, fieldName] = fieldPath.split('.');
205
- const objectTypeState = supergraphState.objectTypes.get(typeName);
206
- if (!objectTypeState) {
207
- throw new Error(`Type "${typeName}" not found`);
208
- }
209
- const fieldState = objectTypeState.fields.get(fieldName);
210
- if (!fieldState) {
211
- throw new Error(`Field "${fieldPath}" not found in object type "${typeName}"`);
212
- }
213
- const fieldStateByGraph = fieldState.byGraph.get(sourceGraphId);
214
- if (!fieldStateByGraph) {
215
- throw new Error(`Field "${fieldPath}" not found in object type "${typeName}" in graph "${sourceGraphId}"`);
216
- }
217
- return (!hasNonKeyFields &&
218
- objectTypeState.byGraph.get(sourceGraphId).extension !== true &&
219
- fieldStateByGraph.external === true &&
220
- fieldStateByGraph.usedAsKey);
221
- })) {
222
- return false;
223
- }
224
- return (targetGraphKeys
225
- .filter(k => k.resolvable === true)
226
- .some(k => {
227
- const targetKeyFields = resolveFieldsFromFieldSet(k.fields, objectTypeState.name, targetGraphId, supergraphState);
228
- for (const fieldPath of targetKeyFields.paths) {
229
- if (!sourceKeyFields.paths.has(fieldPath)) {
230
- return false;
231
- }
232
- }
233
- return true;
234
- }));
235
- });
236
- }
237
- function canGraphResolveFieldDirectly(objectTypeSuperState, fieldSuperState, graphId, supergraphState) {
238
- const objectTypeInGraph = objectTypeSuperState.byGraph.get(graphId);
239
- if (!objectTypeInGraph) {
240
- throw new Error(`Object type "${objectTypeSuperState.name}" not found in graph "${graphId}"`);
241
- }
242
- const fieldInGraph = fieldSuperState.byGraph.get(graphId);
243
- if (!fieldInGraph) {
244
- return false;
245
- }
246
- if ((fieldInGraph.shareable === true ||
247
- objectTypeSuperState.byGraph.get(graphId).shareable === true) &&
248
- supergraphState.graphs.get(graphId).version !== 'v1.0') {
249
- return true;
250
- }
251
- if (fieldInGraph.external === true) {
252
- if (fieldInGraph.usedAsKey === true) {
253
- const graphHasAtLeastOneResolvableField = Array.from(objectTypeSuperState.fields.values()).some(f => {
254
- if (f.name === fieldSuperState.name) {
255
- return false;
256
- }
257
- const fInGraph = f.byGraph.get(graphId);
258
- if (!fInGraph) {
259
- return false;
260
- }
261
- if (fInGraph.external === true) {
262
- return false;
263
- }
264
- if (fInGraph.inaccessible === true) {
265
- return false;
266
- }
267
- if (typeof fInGraph.override === 'string') {
268
- return false;
269
- }
270
- return true;
271
- });
272
- if (graphHasAtLeastOneResolvableField) {
273
- return true;
274
- }
275
- }
276
- return false;
277
- }
278
- return true;
279
- }
280
- function canGraphResolveField(objectTypeSuperState, fieldSuperState, graphId, supergraphState, movabilityGraph) {
281
- const objectTypeInGraph = objectTypeSuperState.byGraph.get(graphId);
282
- if (!objectTypeInGraph) {
283
- throw new Error(`Object type "${objectTypeSuperState.name}" not found in graph "${graphId}"`);
284
- }
285
- const fieldInGraph = fieldSuperState.byGraph.get(graphId);
286
- if (fieldInGraph &&
287
- fieldInGraph.external === false &&
288
- canGraphResolveFieldDirectly(objectTypeSuperState, fieldSuperState, graphId, supergraphState)) {
289
- return true;
290
- }
291
- const graphsWithField = Array.from(fieldSuperState.byGraph).filter(([g, _]) => {
292
- if (g === graphId) {
293
- return false;
294
- }
295
- const fieldInGraph = fieldSuperState.byGraph.get(g);
296
- if (!fieldInGraph || fieldInGraph.external === true) {
297
- return false;
298
- }
299
- return true;
300
- });
301
- const canMoveToGraphWithField = graphsWithField.some(([g, _]) => {
302
- return canGraphMoveToGraphBasedOnMovabilityGraph(movabilityGraph, graphId, g);
303
- });
304
- return canMoveToGraphWithField;
305
- }
306
- function canGraphMoveToGraphBasedOnMovabilityGraph(movabilityGraph, sourceId, destinationId, visited = new Set()) {
307
- const key = `${sourceId} => ${destinationId}`;
308
- if (visited.has(key)) {
309
- return false;
310
- }
311
- else {
312
- visited.add(key);
313
- }
314
- const deps = movabilityGraph.directDependenciesOf(sourceId);
315
- if (deps.includes(destinationId)) {
316
- return true;
317
- }
318
- return deps.some(depId => canGraphMoveToGraphBasedOnMovabilityGraph(movabilityGraph, depId, destinationId, visited));
319
- }
320
- function findLeafs(movabilityGraph, sourceId, destinationId, leafs, visited = new Set()) {
321
- const key = `${sourceId} => ${destinationId}`;
322
- if (leafs === undefined) {
323
- leafs = new Set();
324
- }
325
- const deps = movabilityGraph.directDependenciesOf(sourceId);
326
- if (visited.has(key)) {
327
- return Array.from(leafs);
328
- }
329
- else {
330
- visited.add(key);
331
- }
332
- for (const depId of deps) {
333
- if (!canGraphMoveToGraphBasedOnMovabilityGraph(movabilityGraph, depId, destinationId)) {
334
- leafs.add(depId);
335
- findLeafs(movabilityGraph, depId, destinationId, leafs, visited);
336
- }
337
- }
338
- return Array.from(leafs);
339
- }
340
- function createMemoizedQueryPathFinder() {
341
- const memoizedQueryPaths = new Map();
342
- return function findQueryPathsMemoized(supergraphState, rootTypeName, rootTypeFieldsToStartWith, leafTypeName, leafFieldName, typesInBetweenRootAndLeaf = []) {
343
- const rootType = supergraphState.objectTypes.get(rootTypeName);
344
- const paths = [];
345
- for (const rootFieldName of rootTypeFieldsToStartWith) {
346
- const key = JSON.stringify({
347
- rootFieldName,
348
- rootTypeName,
349
- leafTypeName,
350
- leafFieldName,
351
- typesInBetweenRootAndLeaf,
352
- });
353
- const memoized = memoizedQueryPaths.get(key);
354
- if (memoized) {
355
- return memoized;
356
- }
357
- const rootFieldState = rootType.fields.get(rootFieldName);
358
- if (rootFieldState) {
359
- const fieldOutputTypeName = stripTypeModifiers(rootFieldState.type);
360
- const referencedType = supergraphState.objectTypes.get(fieldOutputTypeName) ??
361
- supergraphState.unionTypes.get(fieldOutputTypeName) ??
362
- supergraphState.interfaceTypes.get(fieldOutputTypeName);
363
- if (!referencedType) {
364
- continue;
365
- }
366
- findQueryPathInType(new Set(), supergraphState, referencedType, leafTypeName, leafFieldName, path => {
367
- const memoized = memoizedQueryPaths.get(key);
368
- if (memoized) {
369
- memoized.push(path);
370
- }
371
- else {
372
- memoizedQueryPaths.set(key, [path]);
373
- }
374
- paths.push(path);
375
- }, typesInBetweenRootAndLeaf, [
376
- {
377
- typeName: rootTypeName,
378
- fieldName: rootFieldName,
379
- },
380
- ]);
381
- }
382
- }
383
- return paths;
384
- };
385
- }
3
+ import { isFieldEdge } from './satisfiablity/edge.js';
4
+ import { Supergraph } from './satisfiablity/supergraph.js';
386
5
  export function SatisfiabilityRule(context, supergraphState) {
387
- const typeDependencies = buildOutputTypesDependencies(supergraphState);
388
- let movabilityGraph = new Map();
389
- function getMovabilityGraphForType(typeName) {
390
- const existingMovabilityGraph = movabilityGraph.get(typeName);
391
- if (existingMovabilityGraph) {
392
- return existingMovabilityGraph;
393
- }
394
- const objectState = supergraphState.objectTypes.get(typeName);
395
- if (!objectState) {
396
- throw new Error(`State of object type "${typeName}" not found in Supergraph state`);
6
+ const supergraph = new Supergraph(supergraphState);
7
+ const unreachables = supergraph.validate();
8
+ const errorByFieldCoordinate = {};
9
+ for (const unreachable of unreachables) {
10
+ const edge = unreachable.superPath.edge();
11
+ if (!edge) {
12
+ throw new Error('Expected edge to be defined');
397
13
  }
398
- const graph = new DepGraph({
399
- circular: true,
400
- });
401
- const graphIds = Array.from(objectState.byGraph.keys());
402
- for (const sourceGraphId of objectState.byGraph.keys()) {
403
- graph.addNode(sourceGraphId);
404
- }
405
- for (const sourceGraphId of objectState.byGraph.keys()) {
406
- const otherGraphIds = graphIds.filter(g => g !== sourceGraphId);
407
- for (const destGraphId of otherGraphIds) {
408
- if (canGraphMoveToGraphByEntity(supergraphState, objectState.name, sourceGraphId, destGraphId)) {
409
- graph.addDependency(sourceGraphId, destGraphId);
410
- }
14
+ if (isFieldEdge(edge)) {
15
+ const fieldCoordinate = `${edge.move.typeName}.${edge.move.fieldName}`;
16
+ if (!errorByFieldCoordinate[fieldCoordinate]) {
17
+ errorByFieldCoordinate[fieldCoordinate] = [];
411
18
  }
19
+ errorByFieldCoordinate[fieldCoordinate].push(unreachable);
412
20
  }
413
- movabilityGraph.set(typeName, graph);
414
- return graph;
415
21
  }
416
22
  return {
417
23
  ObjectTypeField(objectState, fieldState) {
418
- if (objectState.name === 'Query' ||
419
- objectState.name === 'Mutation' ||
420
- objectState.name === 'Subscription') {
421
- return;
422
- }
423
- if (objectState.byGraph.size === 1) {
424
- return;
425
- }
426
- const dependenciesOfObjectType = typeDependencies.dependentsOf(objectState.name);
427
- const isReachableByRootType = {
428
- query: dependenciesOfObjectType.includes('Query'),
429
- mutation: dependenciesOfObjectType.includes('Mutation'),
430
- subscription: dependenciesOfObjectType.includes('Subscription'),
431
- };
432
- const isReachable = isReachableByRootType.query ||
433
- isReachableByRootType.mutation ||
434
- isReachableByRootType.subscription;
435
- if (!isReachable) {
436
- return;
437
- }
438
- const objectStateGraphPairs = Array.from(objectState.byGraph);
439
- const fieldStateGraphPairs = Array.from(fieldState.byGraph);
440
- if (objectStateGraphPairs.some(([_, objectTypeStateInGraph]) => objectTypeStateInGraph.inaccessible === true) ||
441
- fieldStateGraphPairs.some(([_, fieldStateInGraph]) => fieldStateInGraph.inaccessible === true)) {
442
- return;
443
- }
444
- const isFieldShareableInAllSubgraphs = Array.from(fieldState.byGraph).every(([graphId, fieldStateInGraph]) => {
445
- const fieldShareable = fieldStateInGraph.shareable &&
446
- context.subgraphStates.get(graphId).version !== 'v1.0';
447
- const typeShareable = objectState.byGraph.get(graphId).shareable === true;
448
- return fieldShareable || typeShareable;
449
- });
450
- if (isFieldShareableInAllSubgraphs) {
451
- return;
452
- }
453
- if (fieldState.byGraph.size === objectState.byGraph.size &&
454
- fieldStateGraphPairs.every(([_, f]) => f.usedAsKey === true && f.external === false && !f.override)) {
455
- return;
456
- }
457
- const keysInAllGraphs = objectStateGraphPairs.every(([_, o]) => o.keys.length > 0);
458
- const uniqueKeyFieldsSet = new Set(objectStateGraphPairs
459
- .map(([_, o]) => o.keys.map(k => k.fields))
460
- .flat(1));
461
- if (keysInAllGraphs && uniqueKeyFieldsSet.size === 1) {
24
+ const coordinate = `${objectState.name}.${fieldState.name}`;
25
+ const unreachables = errorByFieldCoordinate[coordinate];
26
+ if (!unreachables?.length) {
462
27
  return;
463
28
  }
464
- const aggregatedErrorByRootType = {
465
- Query: {
466
- query: null,
467
- reasons: [],
468
- },
469
- Mutation: {
470
- query: null,
471
- reasons: [],
472
- },
473
- Subscription: {
474
- query: null,
475
- reasons: [],
476
- },
477
- };
478
- const currentObjectTypeMovabilityGraph = getMovabilityGraphForType(objectState.name);
479
- if (!currentObjectTypeMovabilityGraph) {
480
- throw new Error(`Movability graph for object type "${objectState.name}" not found in Supergraph state`);
481
- }
482
- const findQueryPaths = createMemoizedQueryPathFinder();
483
- if (uniqueKeyFieldsSet.size > 0) {
484
- for (const graphId of objectState.byGraph.keys()) {
485
- const fieldStateInGraph = fieldState.byGraph.get(graphId);
486
- if (canGraphResolveField(objectState, fieldState, graphId, supergraphState, currentObjectTypeMovabilityGraph)) {
487
- continue;
488
- }
489
- if (fieldStateInGraph?.external === true) {
490
- const objectStateInGraph = objectState.byGraph.get(graphId);
491
- if (objectStateInGraph.extension === true) {
492
- continue;
493
- }
494
- if (fieldStateInGraph.required) {
495
- continue;
496
- }
497
- if (fieldStateInGraph.provided) {
498
- continue;
499
- }
500
- }
501
- const subgraphState = context.subgraphStates.get(graphId);
502
- const schemaDefinitionOfGraph = subgraphState.schema;
503
- const rootTypes = [
504
- schemaDefinitionOfGraph.queryType
505
- ? [
506
- 'Query',
507
- subgraphState.types.get(schemaDefinitionOfGraph.queryType),
508
- ]
509
- : undefined,
510
- schemaDefinitionOfGraph.mutationType
511
- ? [
512
- 'Mutation',
513
- subgraphState.types.get(schemaDefinitionOfGraph.mutationType),
514
- ]
515
- : undefined,
516
- schemaDefinitionOfGraph.subscriptionType
517
- ? [
518
- 'Subscription',
519
- subgraphState.types.get(schemaDefinitionOfGraph.subscriptionType),
520
- ]
521
- : undefined,
522
- ].filter(isDefined);
523
- if (rootTypes.length === 0) {
524
- continue;
525
- }
526
- const otherGraphIds = objectStateGraphPairs
527
- .filter(([g, _]) => g !== graphId)
528
- .map(([g, _]) => g);
529
- const graphsWithField = fieldStateGraphPairs
530
- .filter(([g, _]) => canGraphResolveFieldDirectly(objectState, fieldState, g, supergraphState))
531
- .map(([g, _]) => g);
532
- const leafs = graphsWithField
533
- .map(g => findLeafs(currentObjectTypeMovabilityGraph, graphId, g))
534
- .flat(1);
535
- if (leafs.length === 0 &&
536
- currentObjectTypeMovabilityGraph.directDependenciesOf(graphId).length > 0) {
537
- continue;
538
- }
539
- for (const [normalizedName, rootType] of rootTypes) {
540
- const paths = findQueryPaths(supergraphState, normalizedName, Array.from(rootType.fields.keys()), objectState.name, fieldState.name, dependenciesOfObjectType);
541
- const nonResolvablePaths = paths.filter(queryPath => !isSatisfiableQueryPath(supergraphState, queryPath));
542
- if (nonResolvablePaths.length === 0) {
543
- continue;
544
- }
545
- let shortestPath = nonResolvablePaths[0];
546
- for (let i = 0; i < nonResolvablePaths.length; i++) {
547
- const curr = nonResolvablePaths[i];
548
- if (shortestPath.length > curr.length) {
549
- shortestPath = curr;
550
- }
551
- if (shortestPath.length === curr.length) {
552
- shortestPath = curr;
553
- }
554
- }
555
- const query = printQueryPath(supergraphState, shortestPath);
556
- const reasons = [];
557
- const canBeIndirectlyResolved = leafs.length > 0;
558
- const cannotMoveToList = (sourceGraphId) => canBeIndirectlyResolved
559
- ? graphsWithField
560
- .map(gid => {
561
- const keys = objectState.byGraph.get(gid).keys.map(k => k.fields);
562
- if (keys.length > 0) {
563
- return objectState.byGraph
564
- .get(gid)
565
- .keys.map(k => k.fields)
566
- .map(fields => `cannot move to subgraph "${context.graphIdToName(gid)}" using @key(fields: "${fields}") of "${objectState.name}", the key field(s) cannot be resolved from subgraph "${context.graphIdToName(sourceGraphId)}".`);
567
- }
568
- return `cannot move to subgraph "${context.graphIdToName(gid)}", which has field "${objectState.name}.${fieldState.name}", because type "${objectState.name}" has no @key defined in subgraph "${context.graphIdToName(gid)}".`;
569
- })
570
- .flat(1)
571
- : otherGraphIds
572
- .filter(g => !currentObjectTypeMovabilityGraph.directDependenciesOf(graphId).includes(g))
573
- .map(gid => {
574
- const keys = objectState.byGraph.get(gid).keys.map(k => k.fields);
575
- if (keys.length > 0) {
576
- return objectState.byGraph
577
- .get(gid)
578
- .keys.map(k => k.fields)
579
- .map(fields => `cannot move to subgraph "${context.graphIdToName(gid)}" using @key(fields: "${fields}") of "${objectState.name}", the key field(s) cannot be resolved from subgraph "${context.graphIdToName(sourceGraphId)}".`);
580
- }
581
- return `cannot move to subgraph "${context.graphIdToName(gid)}", which has field "${objectState.name}.${fieldState.name}", because type "${objectState.name}" has no @key defined in subgraph "${context.graphIdToName(gid)}".`;
582
- })
583
- .flat(1);
584
- const fromSubgraphs = [graphId].concat(leafs);
585
- if (!fieldStateInGraph) {
586
- fromSubgraphs.forEach(gid => {
587
- reasons.push([
588
- gid,
589
- [`cannot find field "${objectState.name}.${fieldState.name}".`].concat(cannotMoveToList(gid)),
590
- ]);
591
- });
592
- }
593
- else if (fieldStateInGraph.external) {
594
- fromSubgraphs.forEach(gid => {
595
- reasons.push([
596
- gid,
597
- [
598
- `field "${objectState.name}.${fieldState.name}" is not resolvable because marked @external.`,
599
- ].concat(cannotMoveToList(gid)),
600
- ]);
601
- });
602
- }
603
- else {
604
- console.log('can NOT resolve field', fieldState.name, 'in graph', graphId, 'reason: unknown');
605
- }
606
- if (!query || reasons.length === 0) {
607
- continue;
608
- }
609
- context.reportError(new GraphQLError([
610
- 'The following supergraph API query:',
611
- query,
612
- 'cannot be satisfied by the subgraphs because:',
613
- ...reasons.map(([gid, reasons]) => {
614
- return (`- from subgraph "${context.graphIdToName(gid)}":\n` +
615
- reasons.map(r => ` - ${r}`).join('\n'));
616
- }),
617
- ].join('\n'), {
618
- extensions: {
619
- code: 'SATISFIABILITY_ERROR',
620
- },
621
- }));
622
- }
29
+ for (const unreachable of unreachables) {
30
+ const queryString = printQueryPath(supergraphState, unreachable.superPath.steps());
31
+ if (!queryString) {
32
+ return;
623
33
  }
624
- }
625
- else {
626
- const graphsWithoutField = objectStateGraphPairs.filter(([graphId]) => !fieldState.byGraph.has(graphId));
627
- const graphsWithField = fieldStateGraphPairs.map(([graphId]) => graphId);
628
- for (const [graphId] of graphsWithoutField) {
629
- const subgraphState = context.subgraphStates.get(graphId);
630
- const isShareableWithOtherGraphs = Array.from(subgraphState.types.values())
631
- .filter(t => dependenciesOfObjectType.includes(t.name))
632
- .every(t => {
633
- if (t.kind === TypeKind.OBJECT) {
634
- if (t.shareable &&
635
- subgraphState.version !== 'v1.0') {
636
- return true;
637
- }
638
- const fields = Array.from(t.fields.values());
639
- if (fields
640
- .filter(f => stripTypeModifiers(f.type) === objectState.name)
641
- .every(f => f.shareable === true &&
642
- subgraphState.version !== 'v1.0')) {
643
- return true;
644
- }
645
- }
646
- return false;
647
- });
648
- if (isShareableWithOtherGraphs) {
649
- continue;
650
- }
651
- const graphHasAtLeastOneResolvableField = Array.from(objectState.fields.values()).some(f => {
652
- const fieldInGraph = f.byGraph.get(graphId);
653
- if (!fieldInGraph) {
654
- return false;
655
- }
656
- if (fieldInGraph.inaccessible === true) {
657
- return false;
658
- }
659
- return true;
660
- });
661
- if (!graphHasAtLeastOneResolvableField) {
662
- continue;
663
- }
664
- const entityTypesReferencingLookingObject = dependenciesOfObjectType
665
- .map(typeName => supergraphState.objectTypes.get(typeName))
666
- .filter((t) => !!t && Array.from(t.byGraph.values()).some(tg => tg.keys.length > 0));
667
- const graphIdsUnableToResolveFieldViaEntityType = [];
668
- for (const [graphId] of graphsWithoutField) {
669
- const localEntityTypes = entityTypesReferencingLookingObject.filter(et => et.byGraph.has(graphId));
670
- const isFieldResolvableThroughEntity = localEntityTypes.some(et => graphsWithField.some(targetGraphId => canGraphMoveToGraphByEntity(supergraphState, et.name, graphId, targetGraphId)));
671
- if (!isFieldResolvableThroughEntity) {
672
- graphIdsUnableToResolveFieldViaEntityType.push(graphId);
673
- }
674
- }
675
- if (graphIdsUnableToResolveFieldViaEntityType.length === 0) {
676
- continue;
677
- }
678
- const schemaDefinitionOfGraph = subgraphState.schema;
679
- const rootTypes = [
680
- schemaDefinitionOfGraph.queryType
681
- ? [
682
- 'Query',
683
- subgraphState.types.get(schemaDefinitionOfGraph.queryType),
684
- ]
685
- : undefined,
686
- schemaDefinitionOfGraph.mutationType
687
- ? [
688
- 'Mutation',
689
- subgraphState.types.get(schemaDefinitionOfGraph.mutationType),
690
- ]
691
- : undefined,
692
- schemaDefinitionOfGraph.subscriptionType
693
- ? [
694
- 'Subscription',
695
- subgraphState.types.get(schemaDefinitionOfGraph.subscriptionType),
696
- ]
697
- : undefined,
698
- ].filter(isDefined);
699
- if (rootTypes.length === 0) {
700
- continue;
701
- }
702
- for (const [normalizedName, rootType] of rootTypes) {
703
- const rootTypeFields = Array.from(rootType.fields.keys()).filter(f => f !== '_entities' && f !== '_service');
704
- if (rootTypeFields.length === 0) {
705
- continue;
706
- }
707
- const supergraphRootType = supergraphState.objectTypes.get(normalizedName);
708
- const rootFieldsReferencingObjectType = Array.from(supergraphRootType.fields.values()).filter(f => {
709
- const fieldOutputTypeName = stripTypeModifiers(f.type);
710
- return (fieldOutputTypeName === objectState.name ||
711
- dependenciesOfObjectType.includes(fieldOutputTypeName));
712
- });
713
- if (rootFieldsReferencingObjectType.length === 0) {
714
- continue;
715
- }
716
- const graphIdsImplementingObjectType = Array.from(objectState.byGraph.keys());
717
- if (rootFieldsReferencingObjectType.every(field => graphIdsImplementingObjectType.every(g => field.byGraph.has(g)))) {
718
- continue;
719
- }
720
- const areRootFieldsShared = graphsWithField.every(g => {
721
- const localVersion = context.subgraphStates.get(g).version;
722
- if (localVersion !== 'v1.0') {
723
- return false;
724
- }
725
- const localSubgraph = context.subgraphStates.get(g);
726
- const localSchemaDefinition = localSubgraph.schema;
727
- const localRootTypeName = normalizedName === 'Query'
728
- ? localSchemaDefinition.queryType
729
- : normalizedName === 'Mutation'
730
- ? localSchemaDefinition.mutationType
731
- : normalizedName === 'Subscription'
732
- ? localSchemaDefinition.subscriptionType
733
- : undefined;
734
- if (!localRootTypeName) {
735
- return false;
736
- }
737
- const localRootType = localSubgraph.types.get(localRootTypeName);
738
- const localRootFields = Array.from(localRootType.fields.keys());
739
- return rootFieldsReferencingObjectType.every(f => localRootFields.includes(f.name));
740
- });
741
- if (areRootFieldsShared) {
742
- continue;
743
- }
744
- const paths = findQueryPaths(supergraphState, normalizedName, Array.from(rootType.fields.keys()), objectState.name, fieldState.name, dependenciesOfObjectType);
745
- const nonResolvablePaths = paths.filter(queryPath => !isSatisfiableQueryPath(supergraphState, queryPath));
746
- if (nonResolvablePaths.length === 0) {
747
- continue;
748
- }
749
- if (!aggregatedErrorByRootType[normalizedName].query) {
750
- aggregatedErrorByRootType[normalizedName].query = printExampleQuery(supergraphState, normalizedName, rootTypeFields, objectState.name, fieldState.name, graphId, dependenciesOfObjectType);
751
- }
752
- const firstFieldImplementingGraph = graphsWithField[0];
753
- const graphNameOwningField = context.graphIdToName(firstFieldImplementingGraph);
754
- aggregatedErrorByRootType[normalizedName].reasons.push([
755
- graphId,
756
- [
757
- `cannot find field "${objectState.name}.${fieldState.name}".`,
758
- `cannot move to subgraph "${graphNameOwningField}", which has field "${objectState.name}.${fieldState.name}", because type "${objectState.name}" has no @key defined in subgraph "${graphNameOwningField}".`,
759
- ],
760
- ]);
34
+ const errorsBySourceGraph = {};
35
+ const reasons = [];
36
+ for (const error of unreachable.listErrors()) {
37
+ const sourceGraphName = error.sourceGraphName;
38
+ if (!errorsBySourceGraph[sourceGraphName]) {
39
+ errorsBySourceGraph[sourceGraphName] = [];
761
40
  }
41
+ errorsBySourceGraph[sourceGraphName].push(error);
762
42
  }
763
- }
764
- for (const rootTypeName in aggregatedErrorByRootType) {
765
- const details = aggregatedErrorByRootType[rootTypeName];
766
- if (!details.query || details.reasons.length === 0) {
43
+ for (const sourceGraphName in errorsBySourceGraph) {
44
+ const errors = errorsBySourceGraph[sourceGraphName];
45
+ reasons.push([sourceGraphName, errors.map(e => e.message)]);
46
+ }
47
+ if (reasons.length === 0) {
767
48
  continue;
768
49
  }
769
50
  context.reportError(new GraphQLError([
770
51
  'The following supergraph API query:',
771
- details.query,
52
+ queryString,
772
53
  'cannot be satisfied by the subgraphs because:',
773
- ...details.reasons.map(([graphId, reasons]) => {
774
- return (`- from subgraph "${context.graphIdToName(graphId)}":\n` +
775
- reasons.map(r => ` - ${r}`).join('\n'));
54
+ ...reasons.map(([graphName, reasons]) => {
55
+ if (reasons.length === 1) {
56
+ return `- from subgraph "${graphName}": ${reasons[0]}`;
57
+ }
58
+ return (`- from subgraph "${graphName}":\n` + reasons.map(r => ` - ${r}`).join('\n'));
776
59
  }),
777
60
  ].join('\n'), {
778
61
  extensions: {
@@ -783,38 +66,6 @@ export function SatisfiabilityRule(context, supergraphState) {
783
66
  },
784
67
  };
785
68
  }
786
- function buildOutputTypesDependencies(supergraphState) {
787
- const graph = new DepGraph({
788
- circular: true,
789
- });
790
- for (const [typeName, typeState] of supergraphState.objectTypes) {
791
- if (typeName === '_Service') {
792
- continue;
793
- }
794
- graph.addNode(typeName);
795
- for (const [_, fieldState] of typeState.fields) {
796
- const referencedTypeName = stripTypeModifiers(fieldState.type);
797
- if (!graph.hasNode(referencedTypeName)) {
798
- graph.addNode(referencedTypeName);
799
- }
800
- graph.addDependency(typeName, referencedTypeName);
801
- }
802
- }
803
- for (const [typeName, typeState] of supergraphState.unionTypes) {
804
- if (typeName === '_Entity') {
805
- continue;
806
- }
807
- graph.addNode(typeName);
808
- for (const memberType of typeState.members) {
809
- const referencedTypeName = memberType;
810
- if (!graph.hasNode(referencedTypeName)) {
811
- graph.addNode(referencedTypeName);
812
- }
813
- graph.addDependency(typeName, referencedTypeName);
814
- }
815
- }
816
- return graph;
817
- }
818
69
  function printLine(msg, indentLevel) {
819
70
  return ' '.repeat(indentLevel + 1) + msg;
820
71
  }
@@ -831,7 +82,6 @@ function printQueryPath(supergraphState, queryPath) {
831
82
  throw new Error(`Field "${point.typeName}.${point.fieldName}" not found in Supergraph state`);
832
83
  }
833
84
  const args = Array.from(fieldState.args)
834
- .filter(([_, argState]) => isNonNull(argState.type))
835
85
  .map(([name, argState]) => `${name}: ${print(createEmptyValueNode(argState.type, supergraphState))}`)
836
86
  .join(', ');
837
87
  const argsPrinted = args.length > 0 ? `(${args})` : '';
@@ -875,231 +125,6 @@ function printQueryPath(supergraphState, queryPath) {
875
125
  lines.push('}');
876
126
  return lines.join('\n');
877
127
  }
878
- function findQueryPathInType(visitedTypes, supergraphState, typeState, leafTypeName, leafFieldName, onQueryPathFound, typesInBetweenRootAndLeaf = [], currentPath = []) {
879
- if (typeState.name !== leafTypeName) {
880
- if (!typesInBetweenRootAndLeaf.includes(typeState.name)) {
881
- return;
882
- }
883
- if (visitedTypes.has(typeState.name)) {
884
- return;
885
- }
886
- visitedTypes.add(typeState.name);
887
- }
888
- if ('fields' in typeState) {
889
- for (const [fieldName, fieldState] of typeState.fields) {
890
- if (fieldName === leafFieldName && typeState.name === leafTypeName) {
891
- onQueryPathFound(currentPath.concat([{ typeName: typeState.name, fieldName }]));
892
- return;
893
- }
894
- const fieldOutputTypeName = stripTypeModifiers(fieldState.type);
895
- const referencedType = supergraphState.objectTypes.get(fieldOutputTypeName) ??
896
- supergraphState.unionTypes.get(fieldOutputTypeName) ??
897
- supergraphState.interfaceTypes.get(fieldOutputTypeName);
898
- if (!referencedType) {
899
- continue;
900
- }
901
- if (referencedType.name === typeState.name) {
902
- return;
903
- }
904
- findQueryPathInType(visitedTypes, supergraphState, referencedType, leafTypeName, leafFieldName, onQueryPathFound, typesInBetweenRootAndLeaf, currentPath.concat([{ typeName: typeState.name, fieldName }]));
905
- }
906
- }
907
- else {
908
- for (const member of typeState.members) {
909
- const referencedType = supergraphState.objectTypes.get(member) ??
910
- supergraphState.unionTypes.get(member) ??
911
- supergraphState.interfaceTypes.get(member);
912
- if (!referencedType) {
913
- continue;
914
- }
915
- if (referencedType.name === typeState.name) {
916
- return;
917
- }
918
- findQueryPathInType(visitedTypes, supergraphState, referencedType, leafTypeName, leafFieldName, onQueryPathFound, typesInBetweenRootAndLeaf, currentPath.concat([{ typeName: member }]));
919
- }
920
- }
921
- }
922
- function printExampleQuery(supergraphState, rootTypeName, rootTypeFieldsToStartWith, leafTypeName, leafFieldName, graphId, typesInBetweenRootAndLeaf = []) {
923
- const rootType = supergraphState.objectTypes.get(rootTypeName);
924
- function visitType(typeState, descendants, visitedTypes) {
925
- if (visitedTypes.includes(typeState.name)) {
926
- return null;
927
- }
928
- if ('members' in typeState) {
929
- for (const member of typeState.members) {
930
- const result = member === leafTypeName
931
- ? visitLeafType(supergraphState.objectTypes.get(member), descendants.concat([
932
- {
933
- kind: Kind.INLINE_FRAGMENT,
934
- typeCondition: {
935
- kind: Kind.NAMED_TYPE,
936
- name: {
937
- kind: Kind.NAME,
938
- value: member,
939
- },
940
- },
941
- selectionSet: {
942
- kind: Kind.SELECTION_SET,
943
- selections: [],
944
- },
945
- },
946
- ]))
947
- : visitType(supergraphState.objectTypes.get(member), descendants.concat([
948
- {
949
- kind: Kind.INLINE_FRAGMENT,
950
- typeCondition: {
951
- kind: Kind.NAMED_TYPE,
952
- name: {
953
- kind: Kind.NAME,
954
- value: member,
955
- },
956
- },
957
- selectionSet: {
958
- kind: Kind.SELECTION_SET,
959
- selections: [],
960
- },
961
- },
962
- ]), visitedTypes.concat(typeState.name));
963
- if (result) {
964
- return result;
965
- }
966
- }
967
- return null;
968
- }
969
- for (const [fieldName, fieldState] of Array.from(typeState.fields.entries()).reverse()) {
970
- if (typeState.name === rootTypeName && !rootTypeFieldsToStartWith.includes(fieldName)) {
971
- continue;
972
- }
973
- const fieldOutputTypeName = stripTypeModifiers(fieldState.type);
974
- if (fieldOutputTypeName === leafTypeName) {
975
- return visitLeafType(supergraphState.objectTypes.get(fieldOutputTypeName), descendants.concat([
976
- {
977
- kind: Kind.FIELD,
978
- name: {
979
- kind: Kind.NAME,
980
- value: fieldName,
981
- },
982
- arguments: Array.from(fieldState.args).map(([argName, argState]) => ({
983
- kind: Kind.ARGUMENT,
984
- name: {
985
- kind: Kind.NAME,
986
- value: argName,
987
- },
988
- value: createEmptyValueNode(argState.type, supergraphState),
989
- })),
990
- },
991
- ]));
992
- }
993
- else if (typesInBetweenRootAndLeaf.includes(fieldOutputTypeName) &&
994
- !visitedTypes.includes(fieldOutputTypeName)) {
995
- const referencedType = supergraphState.objectTypes.get(fieldOutputTypeName) ??
996
- supergraphState.unionTypes.get(fieldOutputTypeName);
997
- return visitType(referencedType, descendants.concat([
998
- {
999
- kind: Kind.FIELD,
1000
- name: {
1001
- kind: Kind.NAME,
1002
- value: fieldName,
1003
- },
1004
- arguments: Array.from(fieldState.args).map(([argName, argState]) => ({
1005
- kind: Kind.ARGUMENT,
1006
- name: {
1007
- kind: Kind.NAME,
1008
- value: argName,
1009
- },
1010
- value: createEmptyValueNode(argState.type, supergraphState),
1011
- })),
1012
- },
1013
- ]), visitedTypes.concat(typeState.name));
1014
- }
1015
- }
1016
- return null;
1017
- }
1018
- function visitLeafType(objectTypeState, descendants) {
1019
- for (const [fieldName, fieldState] of objectTypeState.fields) {
1020
- if (fieldName !== leafFieldName) {
1021
- continue;
1022
- }
1023
- const fieldOutputTypeName = stripTypeModifiers(fieldState.type);
1024
- const isObjectSpreadCapable = supergraphState.objectTypes.has(fieldOutputTypeName) ||
1025
- supergraphState.interfaceTypes.has(fieldOutputTypeName) ||
1026
- supergraphState.unionTypes.has(fieldOutputTypeName);
1027
- return descendants.concat([
1028
- {
1029
- kind: Kind.FIELD,
1030
- name: {
1031
- kind: Kind.NAME,
1032
- value: fieldName,
1033
- },
1034
- arguments: Array.from(fieldState.args).map(([argName, argState]) => ({
1035
- kind: Kind.ARGUMENT,
1036
- name: {
1037
- kind: Kind.NAME,
1038
- value: argName,
1039
- },
1040
- value: createEmptyValueNode(argState.type, supergraphState),
1041
- })),
1042
- selectionSet: isObjectSpreadCapable
1043
- ?
1044
- {
1045
- kind: Kind.SELECTION_SET,
1046
- selections: [
1047
- {
1048
- kind: Kind.FRAGMENT_SPREAD,
1049
- name: {
1050
- kind: Kind.NAME,
1051
- value: '',
1052
- },
1053
- },
1054
- ],
1055
- }
1056
- : undefined,
1057
- },
1058
- ]);
1059
- }
1060
- return null;
1061
- }
1062
- const tree = visitType(rootType, [], []);
1063
- let currentField = null;
1064
- if (!tree) {
1065
- return null;
1066
- }
1067
- tree.reverse();
1068
- for (const field of tree) {
1069
- if (!currentField) {
1070
- currentField = {
1071
- ...field,
1072
- };
1073
- }
1074
- else {
1075
- currentField = {
1076
- ...field,
1077
- selectionSet: {
1078
- kind: Kind.SELECTION_SET,
1079
- selections: [currentField],
1080
- },
1081
- };
1082
- }
1083
- }
1084
- const query = {
1085
- kind: Kind.DOCUMENT,
1086
- definitions: [
1087
- {
1088
- kind: Kind.OPERATION_DEFINITION,
1089
- operation: rootTypeName === 'Query'
1090
- ? OperationTypeNode.QUERY
1091
- : rootTypeName === 'Mutation'
1092
- ? OperationTypeNode.MUTATION
1093
- : OperationTypeNode.SUBSCRIPTION,
1094
- selectionSet: {
1095
- kind: Kind.SELECTION_SET,
1096
- selections: [currentField],
1097
- },
1098
- },
1099
- ],
1100
- };
1101
- return print(query);
1102
- }
1103
128
  function createEmptyValueNode(fullType, supergraphState) {
1104
129
  if (isList(fullType)) {
1105
130
  return {
@@ -1170,64 +195,3 @@ function createEmptyValueNode(fullType, supergraphState) {
1170
195
  }
1171
196
  throw new Error(`Type "${fullType}" is not supported.`);
1172
197
  }
1173
- function resolveFieldsFromFieldSet(fields, typeName, graphId, supergraphState) {
1174
- const paths = new Set();
1175
- const coordinates = new Set();
1176
- const selectionSet = parseFields(fields);
1177
- if (!selectionSet) {
1178
- return {
1179
- coordinates,
1180
- paths,
1181
- pairs: [],
1182
- };
1183
- }
1184
- const pairs = [];
1185
- findFieldPathsFromSelectionSet(paths, coordinates, pairs, typeName, selectionSet, typeName, graphId, supergraphState);
1186
- return {
1187
- coordinates,
1188
- paths,
1189
- pairs,
1190
- };
1191
- }
1192
- function findFieldPathsFromSelectionSet(fieldPaths, coordinates, pairs, typeName, selectionSet, currentPath, graphId, supergraphState) {
1193
- for (const selection of selectionSet.selections) {
1194
- if (selection.kind === Kind.FIELD) {
1195
- const innerPath = `${currentPath}.${selection.name.value}`;
1196
- fieldPaths.add(innerPath);
1197
- if (Array.isArray(typeName)) {
1198
- for (const t of typeName) {
1199
- pairs.push([innerPath, { typeName: t, fieldName: selection.name.value }]);
1200
- coordinates.add(`${t}.${selection.name.value}`);
1201
- }
1202
- }
1203
- else {
1204
- pairs.push([innerPath, { typeName, fieldName: selection.name.value }]);
1205
- coordinates.add(`${typeName}.${selection.name.value}`);
1206
- }
1207
- if (selection.selectionSet) {
1208
- const types = (Array.isArray(typeName) ? typeName : [typeName]).map(tName => {
1209
- const outputType = supergraphState.objectTypes.get(tName) ?? supergraphState.interfaceTypes.get(tName);
1210
- if (!outputType) {
1211
- throw new Error(`Type "${tName}" is not defined.`);
1212
- }
1213
- return outputType;
1214
- });
1215
- const typesWithField = types.filter(t => t.fields.has(selection.name.value));
1216
- if (typesWithField.length === 0) {
1217
- throw new Error(`Type "${typeName.toString()}" does not have field "${selection.name.value}".`);
1218
- }
1219
- const outputTypes = typesWithField.map(t => stripTypeModifiers(t.fields.get(selection.name.value).type));
1220
- findFieldPathsFromSelectionSet(fieldPaths, coordinates, pairs, outputTypes, selection.selectionSet, innerPath, graphId, supergraphState);
1221
- }
1222
- }
1223
- else if (selection.kind === Kind.INLINE_FRAGMENT) {
1224
- if (!selection.typeCondition) {
1225
- throw new Error(`Inline fragment without type condition is not supported.`);
1226
- }
1227
- findFieldPathsFromSelectionSet(fieldPaths, coordinates, pairs, selection.typeCondition.name.value, selection.selectionSet, `${currentPath}.(${selection.typeCondition.name.value})`, graphId, supergraphState);
1228
- }
1229
- else {
1230
- throw new Error(`Fragment spread is not supported.`);
1231
- }
1232
- }
1233
- }