@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.
- package/README.md +2 -1
- package/cjs/specifications/link.js +40 -5
- package/cjs/subgraph/helpers.js +2 -2
- package/cjs/subgraph/state.js +8 -0
- package/cjs/subgraph/validation/rules/elements/provides.js +8 -6
- package/cjs/subgraph/validation/rules/elements/requires.js +9 -7
- package/cjs/subgraph/validation/validate-state.js +9 -3
- package/cjs/subgraph/validation/validate-subgraph.js +4 -2
- package/cjs/subgraph/validation/validation-context.js +10 -0
- package/cjs/supergraph/composition/directive.js +3 -0
- package/cjs/supergraph/composition/enum-type.js +3 -0
- package/cjs/supergraph/composition/input-object-type.js +3 -0
- package/cjs/supergraph/composition/interface-type.js +4 -0
- package/cjs/supergraph/composition/object-type.js +18 -20
- package/cjs/supergraph/composition/scalar-type.js +2 -0
- package/cjs/supergraph/composition/union-type.js +2 -0
- package/cjs/supergraph/state.js +24 -26
- package/cjs/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
- package/cjs/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
- package/cjs/supergraph/validation/rules/satisfiablity/constants.js +4 -0
- package/cjs/supergraph/validation/rules/satisfiablity/edge.js +64 -0
- package/cjs/supergraph/validation/rules/satisfiablity/errors.js +44 -0
- package/cjs/supergraph/validation/rules/satisfiablity/fields.js +147 -0
- package/cjs/supergraph/validation/rules/satisfiablity/finder.js +267 -0
- package/cjs/supergraph/validation/rules/satisfiablity/graph.js +675 -0
- package/cjs/supergraph/validation/rules/satisfiablity/helpers.js +41 -0
- package/cjs/supergraph/validation/rules/satisfiablity/move-validator.js +337 -0
- package/cjs/supergraph/validation/rules/satisfiablity/moves.js +52 -0
- package/cjs/supergraph/validation/rules/satisfiablity/node.js +89 -0
- package/cjs/supergraph/validation/rules/satisfiablity/operation-path.js +70 -0
- package/cjs/supergraph/validation/rules/satisfiablity/supergraph.js +37 -0
- package/cjs/supergraph/validation/rules/satisfiablity/walker.js +306 -0
- package/cjs/supergraph/validation/rules/satisfiablity-rule.js +45 -1081
- package/cjs/supergraph/validation/validate-supergraph.js +1 -1
- package/cjs/utils/logger.js +127 -0
- package/esm/specifications/link.js +40 -5
- package/esm/subgraph/helpers.js +2 -2
- package/esm/subgraph/state.js +8 -0
- package/esm/subgraph/validation/rules/elements/provides.js +8 -6
- package/esm/subgraph/validation/rules/elements/requires.js +9 -7
- package/esm/subgraph/validation/validate-state.js +9 -3
- package/esm/subgraph/validation/validate-subgraph.js +4 -2
- package/esm/subgraph/validation/validation-context.js +11 -1
- package/esm/supergraph/composition/directive.js +3 -0
- package/esm/supergraph/composition/enum-type.js +3 -0
- package/esm/supergraph/composition/input-object-type.js +3 -0
- package/esm/supergraph/composition/interface-type.js +4 -0
- package/esm/supergraph/composition/object-type.js +18 -20
- package/esm/supergraph/composition/scalar-type.js +2 -0
- package/esm/supergraph/composition/union-type.js +2 -0
- package/esm/supergraph/state.js +24 -26
- package/esm/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
- package/esm/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
- package/esm/supergraph/validation/rules/satisfiablity/constants.js +1 -0
- package/esm/supergraph/validation/rules/satisfiablity/edge.js +54 -0
- package/esm/supergraph/validation/rules/satisfiablity/errors.js +40 -0
- package/esm/supergraph/validation/rules/satisfiablity/fields.js +142 -0
- package/esm/supergraph/validation/rules/satisfiablity/finder.js +261 -0
- package/esm/supergraph/validation/rules/satisfiablity/graph.js +671 -0
- package/esm/supergraph/validation/rules/satisfiablity/helpers.js +35 -0
- package/esm/supergraph/validation/rules/satisfiablity/move-validator.js +333 -0
- package/esm/supergraph/validation/rules/satisfiablity/moves.js +46 -0
- package/esm/supergraph/validation/rules/satisfiablity/node.js +85 -0
- package/esm/supergraph/validation/rules/satisfiablity/operation-path.js +66 -0
- package/esm/supergraph/validation/rules/satisfiablity/supergraph.js +33 -0
- package/esm/supergraph/validation/rules/satisfiablity/walker.js +301 -0
- package/esm/supergraph/validation/rules/satisfiablity-rule.js +40 -1076
- package/esm/supergraph/validation/validate-supergraph.js +1 -1
- package/esm/utils/logger.js +119 -0
- package/package.json +2 -1
- package/typings/subgraph/state.d.cts +2 -0
- package/typings/subgraph/state.d.ts +2 -0
- package/typings/subgraph/validation/validate-state.d.cts +2 -1
- package/typings/subgraph/validation/validate-state.d.ts +2 -1
- package/typings/subgraph/validation/validation-context.d.cts +3 -0
- package/typings/subgraph/validation/validation-context.d.ts +3 -0
- package/typings/supergraph/composition/common.d.cts +2 -1
- package/typings/supergraph/composition/common.d.ts +2 -1
- package/typings/supergraph/composition/directive.d.cts +4 -0
- package/typings/supergraph/composition/directive.d.ts +4 -0
- package/typings/supergraph/composition/enum-type.d.cts +4 -0
- package/typings/supergraph/composition/enum-type.d.ts +4 -0
- package/typings/supergraph/composition/input-object-type.d.cts +4 -0
- package/typings/supergraph/composition/input-object-type.d.ts +4 -0
- package/typings/supergraph/composition/interface-type.d.cts +5 -0
- package/typings/supergraph/composition/interface-type.d.ts +5 -0
- package/typings/supergraph/composition/object-type.d.cts +5 -0
- package/typings/supergraph/composition/object-type.d.ts +5 -0
- package/typings/supergraph/composition/scalar-type.d.cts +3 -0
- package/typings/supergraph/composition/scalar-type.d.ts +3 -0
- package/typings/supergraph/composition/union-type.d.cts +3 -0
- package/typings/supergraph/composition/union-type.d.ts +3 -0
- package/typings/supergraph/state.d.cts +4 -4
- package/typings/supergraph/state.d.ts +4 -4
- package/typings/supergraph/validation/rules/satisfiablity/constants.d.cts +2 -0
- package/typings/supergraph/validation/rules/satisfiablity/constants.d.ts +2 -0
- package/typings/supergraph/validation/rules/satisfiablity/edge.d.cts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/edge.d.ts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/errors.d.cts +17 -0
- package/typings/supergraph/validation/rules/satisfiablity/errors.d.ts +17 -0
- package/typings/supergraph/validation/rules/satisfiablity/fields.d.cts +33 -0
- package/typings/supergraph/validation/rules/satisfiablity/fields.d.ts +33 -0
- package/typings/supergraph/validation/rules/satisfiablity/finder.d.cts +28 -0
- package/typings/supergraph/validation/rules/satisfiablity/finder.d.ts +28 -0
- package/typings/supergraph/validation/rules/satisfiablity/graph.d.cts +63 -0
- package/typings/supergraph/validation/rules/satisfiablity/graph.d.ts +63 -0
- package/typings/supergraph/validation/rules/satisfiablity/helpers.d.cts +7 -0
- package/typings/supergraph/validation/rules/satisfiablity/helpers.d.ts +7 -0
- package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.cts +25 -0
- package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.ts +25 -0
- package/typings/supergraph/validation/rules/satisfiablity/moves.d.cts +24 -0
- package/typings/supergraph/validation/rules/satisfiablity/moves.d.ts +24 -0
- package/typings/supergraph/validation/rules/satisfiablity/node.d.cts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/node.d.ts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.cts +29 -0
- package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.ts +29 -0
- package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.cts +14 -0
- package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.ts +14 -0
- package/typings/supergraph/validation/rules/satisfiablity/walker.d.cts +35 -0
- package/typings/supergraph/validation/rules/satisfiablity/walker.d.ts +35 -0
- package/typings/utils/logger.d.cts +33 -0
- package/typings/utils/logger.d.ts +33 -0
- package/cjs/utils/dependency-graph.js +0 -227
- package/esm/utils/dependency-graph.js +0 -222
- package/typings/utils/dependency-graph.d.cts +0 -31
- package/typings/utils/dependency-graph.d.ts +0 -31
|
@@ -1,778 +1,61 @@
|
|
|
1
|
-
import { GraphQLError, Kind,
|
|
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
|
-
|
|
8
|
-
|
|
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
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
765
|
-
|
|
766
|
-
|
|
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
|
-
|
|
52
|
+
queryString,
|
|
772
53
|
'cannot be satisfied by the subgraphs because:',
|
|
773
|
-
...
|
|
774
|
-
|
|
775
|
-
|
|
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
|
-
}
|