@wundergraph/composition 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ast/ast.d.ts +21 -3
- package/dist/ast/ast.js +23 -11
- package/dist/ast/ast.js.map +1 -1
- package/dist/ast/utils.d.ts +16 -103
- package/dist/ast/utils.js +132 -16
- package/dist/ast/utils.js.map +1 -1
- package/dist/errors/errors.d.ts +16 -4
- package/dist/errors/errors.js +97 -21
- package/dist/errors/errors.js.map +1 -1
- package/dist/federation/federation-factory.d.ts +28 -24
- package/dist/federation/federation-factory.js +610 -446
- package/dist/federation/federation-factory.js.map +1 -1
- package/dist/federation/utils.d.ts +130 -0
- package/dist/federation/utils.js +15 -0
- package/dist/federation/utils.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/normalization/normalization-factory.d.ts +9 -3
- package/dist/normalization/normalization-factory.js +221 -75
- package/dist/normalization/normalization-factory.js.map +1 -1
- package/dist/normalization/utils.d.ts +4 -0
- package/dist/normalization/utils.js +12 -5
- package/dist/normalization/utils.js.map +1 -1
- package/dist/subgraph/field-configuration.d.ts +12 -1
- package/dist/subgraph/subgraph.d.ts +2 -2
- package/dist/subgraph/subgraph.js +45 -82
- package/dist/subgraph/subgraph.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/constants.js +26 -8
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/string-constants.d.ts +12 -1
- package/dist/utils/string-constants.js +14 -2
- package/dist/utils/string-constants.js.map +1 -1
- package/dist/utils/utils.d.ts +15 -3
- package/dist/utils/utils.js +58 -23
- package/dist/utils/utils.js.map +1 -1
- package/package.json +4 -3
- package/dist/federation/federation-result.d.ts +0 -6
- package/dist/federation/federation-result.js +0 -3
- package/dist/federation/federation-result.js.map +0 -1
|
@@ -1,74 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.federateSubgraphs = exports.FederationFactory = void 0;
|
|
4
4
|
const graphology_1 = require("graphology");
|
|
5
|
-
const graphology_simple_path_1 = require("graphology-simple-path");
|
|
6
5
|
const graphql_1 = require("graphql");
|
|
7
6
|
const ast_1 = require("../ast/ast");
|
|
8
7
|
const utils_1 = require("../ast/utils");
|
|
9
8
|
const errors_1 = require("../errors/errors");
|
|
10
9
|
const type_merging_1 = require("../type-merging/type-merging");
|
|
10
|
+
const utils_2 = require("./utils");
|
|
11
11
|
const subgraph_1 = require("../subgraph/subgraph");
|
|
12
12
|
const string_constants_1 = require("../utils/string-constants");
|
|
13
|
-
const
|
|
14
|
-
const normalization_factory_1 = require("../normalization/normalization-factory");
|
|
13
|
+
const utils_3 = require("../utils/utils");
|
|
15
14
|
const merge_1 = require("@graphql-tools/merge");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
throw errors_1.minimumSubgraphRequirementError;
|
|
19
|
-
}
|
|
20
|
-
const normalizedSubgraphs = [];
|
|
21
|
-
const validationErrors = [];
|
|
22
|
-
const subgraphNames = new Set();
|
|
23
|
-
const nonUniqueSubgraphNames = new Set();
|
|
24
|
-
const invalidNameErrorMessages = [];
|
|
25
|
-
for (let i = 0; i < subgraphs.length; i++) {
|
|
26
|
-
const subgraph = subgraphs[i];
|
|
27
|
-
const name = subgraph.name || `subgraph-${i}-${Date.now()}`;
|
|
28
|
-
if (!subgraph.name) {
|
|
29
|
-
invalidNameErrorMessages.push((0, errors_1.invalidSubgraphNameErrorMessage)(i, name));
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
(0, subgraph_1.validateSubgraphName)(subgraph.name, subgraphNames, nonUniqueSubgraphNames);
|
|
33
|
-
}
|
|
34
|
-
const { errors, normalizationResult } = (0, normalization_factory_1.normalizeSubgraph)(subgraph.definitions);
|
|
35
|
-
if (errors) {
|
|
36
|
-
validationErrors.push((0, errors_1.subgraphValidationError)(name, errors));
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (!normalizationResult) {
|
|
40
|
-
validationErrors.push((0, errors_1.subgraphValidationError)(name, [errors_1.subgraphValidationFailureErrorMessage]));
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
normalizedSubgraphs.push({
|
|
44
|
-
definitions: normalizationResult.subgraphAST,
|
|
45
|
-
isVersionTwo: normalizationResult.isVersionTwo,
|
|
46
|
-
name,
|
|
47
|
-
operationTypes: normalizationResult.operationTypes,
|
|
48
|
-
url: subgraph.url,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
const allErrors = [];
|
|
52
|
-
if (invalidNameErrorMessages.length > 0 || nonUniqueSubgraphNames.size > 0) {
|
|
53
|
-
allErrors.push((0, errors_1.invalidSubgraphNamesError)([...nonUniqueSubgraphNames], invalidNameErrorMessages));
|
|
54
|
-
}
|
|
55
|
-
allErrors.push(...validationErrors);
|
|
56
|
-
if (allErrors.length > 0) {
|
|
57
|
-
return { errors: allErrors };
|
|
58
|
-
}
|
|
59
|
-
const federationFactory = new FederationFactory(normalizedSubgraphs);
|
|
60
|
-
return federationFactory.federate();
|
|
61
|
-
}
|
|
62
|
-
exports.federateSubgraphs = federateSubgraphs;
|
|
15
|
+
const constants_1 = require("../utils/constants");
|
|
16
|
+
const normalization_factory_1 = require("../normalization/normalization-factory");
|
|
63
17
|
class FederationFactory {
|
|
64
18
|
abstractToConcreteTypeNames = new Map();
|
|
65
19
|
areFieldsShareable = false;
|
|
66
20
|
argumentTypeNameSet = new Set();
|
|
21
|
+
argumentConfigurations = [];
|
|
22
|
+
executableDirectives = new Set();
|
|
67
23
|
parentTypeName = '';
|
|
24
|
+
persistedDirectives = new Set([string_constants_1.DEPRECATED, string_constants_1.INACCESSIBLE, string_constants_1.TAG]);
|
|
68
25
|
currentSubgraphName = '';
|
|
69
26
|
childName = '';
|
|
70
27
|
directiveDefinitions = new Map();
|
|
71
|
-
|
|
28
|
+
entities = new Map();
|
|
72
29
|
errors = [];
|
|
73
30
|
extensions = new Map();
|
|
74
31
|
graph = new graphology_1.MultiGraph();
|
|
@@ -82,10 +39,8 @@ class FederationFactory {
|
|
|
82
39
|
isParentRootType = false;
|
|
83
40
|
isParentInputObject = false;
|
|
84
41
|
outputFieldTypeNameSet = new Set();
|
|
85
|
-
|
|
86
|
-
rootTypeFieldsByResponseTypeName = new Map();
|
|
42
|
+
parents = new Map();
|
|
87
43
|
rootTypeNames = new Set([string_constants_1.DEFAULT_MUTATION, string_constants_1.DEFAULT_QUERY, string_constants_1.DEFAULT_SUBSCRIPTION]);
|
|
88
|
-
sharedRootTypeFieldDependentResponses = new Map();
|
|
89
44
|
subgraphs = [];
|
|
90
45
|
shareableErrorTypeNames = new Map();
|
|
91
46
|
constructor(subgraphs) {
|
|
@@ -96,28 +51,25 @@ class FederationFactory {
|
|
|
96
51
|
}
|
|
97
52
|
upsertEntity(node) {
|
|
98
53
|
const typeName = node.name.value;
|
|
99
|
-
const entity = this.
|
|
54
|
+
const entity = this.entities.get(typeName);
|
|
100
55
|
if (entity) {
|
|
101
|
-
(0, utils_1.extractEntityKeys)(node, entity.keys);
|
|
56
|
+
(0, utils_1.extractEntityKeys)(node, entity.keys, this.errors);
|
|
102
57
|
entity.subgraphs.add(this.currentSubgraphName);
|
|
103
58
|
return;
|
|
104
59
|
}
|
|
105
|
-
this.
|
|
60
|
+
this.entities.set(typeName, {
|
|
106
61
|
fields: new Set(),
|
|
107
|
-
keys: (0, utils_1.extractEntityKeys)(node, new Set()),
|
|
62
|
+
keys: (0, utils_1.extractEntityKeys)(node, new Set(), this.errors),
|
|
108
63
|
subgraphs: new Set([this.currentSubgraphName]),
|
|
109
64
|
});
|
|
110
65
|
}
|
|
111
66
|
populateMultiGraphAndRenameOperations(subgraphs) {
|
|
112
67
|
for (const subgraph of subgraphs) {
|
|
113
68
|
this.currentSubgraphName = subgraph.name;
|
|
114
|
-
(0, subgraph_1.
|
|
115
|
-
(0, subgraph_1.
|
|
69
|
+
(0, subgraph_1.walkSubgraphToCollectObjectLikesAndDirectiveDefinitions)(this, subgraph);
|
|
70
|
+
(0, subgraph_1.walkSubgraphToCollectFields)(this, subgraph);
|
|
116
71
|
}
|
|
117
72
|
}
|
|
118
|
-
isParentInterface(parent) {
|
|
119
|
-
return parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
|
|
120
|
-
}
|
|
121
73
|
isFieldEntityKey(parent) {
|
|
122
74
|
if (parent.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || parent.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
123
75
|
return parent.entityKeys.has(this.childName);
|
|
@@ -127,11 +79,11 @@ class FederationFactory {
|
|
|
127
79
|
getEnumMergeMethod(enumName) {
|
|
128
80
|
if (this.inputFieldTypeNameSet.has(enumName) || this.argumentTypeNameSet.has(enumName)) {
|
|
129
81
|
if (this.outputFieldTypeNameSet.has(enumName)) {
|
|
130
|
-
return
|
|
82
|
+
return utils_2.MergeMethod.CONSISTENT;
|
|
131
83
|
}
|
|
132
|
-
return
|
|
84
|
+
return utils_2.MergeMethod.INTERSECTION;
|
|
133
85
|
}
|
|
134
|
-
return
|
|
86
|
+
return utils_2.MergeMethod.UNION;
|
|
135
87
|
}
|
|
136
88
|
validateArgumentDefaultValues(argName, existingDefaultValue, newDefaultValue) {
|
|
137
89
|
if (existingDefaultValue.kind !== newDefaultValue.kind) {
|
|
@@ -149,6 +101,7 @@ class FederationFactory {
|
|
|
149
101
|
existingArg.includeDefaultValue = false;
|
|
150
102
|
return;
|
|
151
103
|
}
|
|
104
|
+
const argumentName = existingArg.node.name.value;
|
|
152
105
|
const existingDefaultValue = existingArg.node.defaultValue;
|
|
153
106
|
switch (existingDefaultValue.kind) {
|
|
154
107
|
case graphql_1.Kind.LIST: // TODO
|
|
@@ -163,92 +116,54 @@ class FederationFactory {
|
|
|
163
116
|
case graphql_1.Kind.FLOAT:
|
|
164
117
|
case graphql_1.Kind.INT:
|
|
165
118
|
case graphql_1.Kind.STRING:
|
|
166
|
-
this.validateArgumentDefaultValues(
|
|
119
|
+
this.validateArgumentDefaultValues(argumentName, existingDefaultValue, newDefaultValue);
|
|
167
120
|
break;
|
|
168
121
|
default:
|
|
169
|
-
throw
|
|
122
|
+
throw (0, errors_1.unexpectedArgumentKindFatalError)(argumentName, this.childName);
|
|
170
123
|
}
|
|
171
124
|
}
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
for (const arg of node.arguments) {
|
|
177
|
-
const argName = arg.name.value;
|
|
178
|
-
const argPath = `${node.name.value}(${argName}...)`;
|
|
179
|
-
this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
|
|
180
|
-
const existingArg = existingFieldNode.arguments.get(argName);
|
|
181
|
-
if (existingArg) {
|
|
182
|
-
existingArg.appearances += 1;
|
|
183
|
-
existingArg.node.description = existingArg.node.description || arg.description;
|
|
184
|
-
const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArg.node.type, arg.type, this.childName, argName);
|
|
185
|
-
if (typeNode) {
|
|
186
|
-
existingArg.node.type = typeNode;
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
if (!typeErrors || typeErrors.length < 2) {
|
|
190
|
-
throw new Error(''); // TODO this should never happen
|
|
191
|
-
}
|
|
192
|
-
this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
193
|
-
}
|
|
194
|
-
this.compareAndValidateArgumentDefaultValues(existingArg, arg);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
const newNode = (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName);
|
|
198
|
-
// TODO validation of default values
|
|
199
|
-
existingFieldNode.arguments.set(argName, {
|
|
200
|
-
appearances: 1,
|
|
201
|
-
includeDefaultValue: !!arg.defaultValue,
|
|
202
|
-
node: newNode,
|
|
203
|
-
});
|
|
125
|
+
upsertRequiredSubgraph(set, isRequired) {
|
|
126
|
+
if (isRequired) {
|
|
127
|
+
set.add(this.currentSubgraphName);
|
|
204
128
|
}
|
|
129
|
+
return set;
|
|
205
130
|
}
|
|
206
|
-
|
|
131
|
+
// TODO validation of default values
|
|
132
|
+
upsertArguments(node, argumentMap) {
|
|
207
133
|
if (!node.arguments) {
|
|
208
|
-
return;
|
|
134
|
+
return argumentMap;
|
|
209
135
|
}
|
|
210
|
-
for (const
|
|
211
|
-
const argName =
|
|
136
|
+
for (const argumentNode of node.arguments) {
|
|
137
|
+
const argName = argumentNode.name.value;
|
|
212
138
|
const argPath = `${node.name.value}(${argName}...)`;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
const concreteTypeName = node.name.value;
|
|
226
|
-
for (const iFace of node.interfaces) {
|
|
227
|
-
const interfaceName = iFace.name.value;
|
|
228
|
-
const concreteTypes = this.abstractToConcreteTypeNames.get(interfaceName);
|
|
229
|
-
if (concreteTypes) {
|
|
230
|
-
concreteTypes.add(concreteTypeName);
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
this.abstractToConcreteTypeNames.set(interfaceName, new Set([concreteTypeName]));
|
|
139
|
+
this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, argumentNode.type));
|
|
140
|
+
const isRequired = (0, type_merging_1.isTypeRequired)(argumentNode.type);
|
|
141
|
+
const existingArgumentContainer = argumentMap.get(argName);
|
|
142
|
+
if (!existingArgumentContainer) {
|
|
143
|
+
argumentMap.set(argName, {
|
|
144
|
+
includeDefaultValue: !!argumentNode.defaultValue,
|
|
145
|
+
node: (0, ast_1.inputValueDefinitionNodeToMutable)(argumentNode, this.childName),
|
|
146
|
+
requiredSubgraphs: this.upsertRequiredSubgraph(new Set(), isRequired),
|
|
147
|
+
subgraphs: new Set([this.currentSubgraphName]),
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
234
150
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const unionName = node.name.value;
|
|
242
|
-
for (const member of node.types) {
|
|
243
|
-
const memberName = member.name.value;
|
|
244
|
-
const concreteTypes = this.abstractToConcreteTypeNames.get(memberName);
|
|
245
|
-
if (concreteTypes) {
|
|
246
|
-
concreteTypes.add(memberName);
|
|
151
|
+
(0, utils_1.setLongestDescriptionForNode)(existingArgumentContainer.node, argumentNode.description);
|
|
152
|
+
this.upsertRequiredSubgraph(existingArgumentContainer.requiredSubgraphs, isRequired);
|
|
153
|
+
existingArgumentContainer.subgraphs.add(this.currentSubgraphName);
|
|
154
|
+
const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArgumentContainer.node.type, argumentNode.type, this.childName, argName);
|
|
155
|
+
if (typeNode) {
|
|
156
|
+
existingArgumentContainer.node.type = typeNode;
|
|
247
157
|
}
|
|
248
158
|
else {
|
|
249
|
-
|
|
159
|
+
if (!typeErrors || typeErrors.length < 2) {
|
|
160
|
+
throw (0, errors_1.argumentTypeMergeFatalError)(argName, this.childName);
|
|
161
|
+
}
|
|
162
|
+
this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
250
163
|
}
|
|
164
|
+
this.compareAndValidateArgumentDefaultValues(existingArgumentContainer, argumentNode);
|
|
251
165
|
}
|
|
166
|
+
return argumentMap;
|
|
252
167
|
}
|
|
253
168
|
isFieldShareable(node, parent) {
|
|
254
169
|
return (!this.isCurrentSubgraphVersionTwo ||
|
|
@@ -256,129 +171,66 @@ class FederationFactory {
|
|
|
256
171
|
(0, utils_1.isNodeShareable)(node) ||
|
|
257
172
|
(this.isCurrentParentEntity && this.isFieldEntityKey(parent)));
|
|
258
173
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (!responsePaths) {
|
|
265
|
-
const allPaths = (0, graphology_simple_path_1.allSimplePaths)(this.graph, responseTypeName, this.parentTypeName);
|
|
266
|
-
this.graphPaths.set(responseTypeName, new Map([
|
|
267
|
-
[this.parentTypeName, allPaths]
|
|
268
|
-
]));
|
|
269
|
-
return allPaths;
|
|
270
|
-
}
|
|
271
|
-
const pathsToParent = responsePaths.get(this.parentTypeName);
|
|
272
|
-
if (pathsToParent) {
|
|
273
|
-
return pathsToParent;
|
|
274
|
-
}
|
|
275
|
-
const allParentPaths = (0, graphology_simple_path_1.allSimplePaths)(this.graph, responseTypeName, this.parentTypeName);
|
|
276
|
-
responsePaths.set(this.parentTypeName, allParentPaths);
|
|
277
|
-
return allParentPaths;
|
|
278
|
-
}
|
|
279
|
-
addPotentiallyUnresolvableField(parent, fieldName) {
|
|
280
|
-
const fieldContainer = (0, utils_2.getOrThrowError)(parent.fields, fieldName);
|
|
281
|
-
for (const [responseTypeName, rootTypeFields] of this.rootTypeFieldsByResponseTypeName) {
|
|
282
|
-
const paths = this.getAllSimplePaths(responseTypeName);
|
|
283
|
-
// If the rootTypeFields response type has no path to the parent type, continue
|
|
284
|
-
if (paths.length < 1) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
// Construct all possible paths to the unresolvable field but with the fieldName relationship between nodes
|
|
288
|
-
const partialResolverPaths = [];
|
|
289
|
-
for (const path of paths) {
|
|
290
|
-
let hasEntityAncestor = false;
|
|
291
|
-
let resolverPath = '';
|
|
292
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
293
|
-
const pathParent = path[i];
|
|
294
|
-
// The field in question is resolvable if it has an entity ancestor within the same subgraph
|
|
295
|
-
// Unresolvable fields further up the chain will be handled elsewhere
|
|
296
|
-
const entity = this.entityMap.get(pathParent);
|
|
297
|
-
if (entity && entity.subgraphs.has(this.currentSubgraphName)) {
|
|
298
|
-
hasEntityAncestor = true;
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
const edges = this.graph.edges(pathParent, path[i + 1]);
|
|
302
|
-
// If there are multiple edges, pick the first one
|
|
303
|
-
const inlineFragment = this.graph.getEdgeAttribute(edges[0], string_constants_1.INLINE_FRAGMENT);
|
|
304
|
-
const edgeName = this.graph.getEdgeAttribute(edges[0], string_constants_1.FIELD_NAME);
|
|
305
|
-
// If the parent field is an abstract type, the child should be proceeded by an inline fragment
|
|
306
|
-
resolverPath += edgeName + (inlineFragment || '.');
|
|
307
|
-
}
|
|
308
|
-
if (hasEntityAncestor) {
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
// Add the unresolvable field to each path
|
|
312
|
-
resolverPath += fieldName;
|
|
313
|
-
// If the field could have fields itself, add ellipsis
|
|
314
|
-
if (this.graph.hasNode(fieldContainer.rootTypeName)) {
|
|
315
|
-
resolverPath += string_constants_1.FRAGMENT_REPRESENTATION;
|
|
316
|
-
}
|
|
317
|
-
partialResolverPaths.push(resolverPath);
|
|
318
|
-
}
|
|
319
|
-
if (partialResolverPaths.length < 1) {
|
|
174
|
+
upsertDirectiveNode(node) {
|
|
175
|
+
const directiveName = node.name.value;
|
|
176
|
+
const directiveDefinition = this.directiveDefinitions.get(directiveName);
|
|
177
|
+
if (directiveDefinition) {
|
|
178
|
+
if (!this.executableDirectives.has(directiveName)) {
|
|
320
179
|
return;
|
|
321
180
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, rootTypeField.subgraphs)) {
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
const fullResolverPaths = [];
|
|
329
|
-
// The field is still resolvable if it's defined and resolved in another graph (but that isn't yet known)
|
|
330
|
-
// Consequently, the subgraphs must be compared later to determine that the field is always resolvable
|
|
331
|
-
for (const partialResolverPath of partialResolverPaths) {
|
|
332
|
-
fullResolverPaths.push(`${rootTypeFieldPath}${rootTypeField.inlineFragment}${partialResolverPath}`);
|
|
333
|
-
}
|
|
334
|
-
const potentiallyUnresolvableField = {
|
|
335
|
-
fieldContainer,
|
|
336
|
-
fullResolverPaths,
|
|
337
|
-
rootTypeField: rootTypeField,
|
|
338
|
-
};
|
|
339
|
-
// The parent might already have unresolvable fields that have already been added
|
|
340
|
-
const dependentResponsesByFieldName = this.sharedRootTypeFieldDependentResponses.get(this.parentTypeName);
|
|
341
|
-
if (dependentResponsesByFieldName) {
|
|
342
|
-
dependentResponsesByFieldName.push(potentiallyUnresolvableField);
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
this.sharedRootTypeFieldDependentResponses.set(this.parentTypeName, [potentiallyUnresolvableField]);
|
|
181
|
+
if ((0, utils_1.mergeExecutableDirectiveLocations)(node.locations, directiveDefinition).size < 1) {
|
|
182
|
+
this.executableDirectives.delete(directiveName);
|
|
183
|
+
return;
|
|
346
184
|
}
|
|
185
|
+
this.upsertArguments(node, directiveDefinition.arguments);
|
|
186
|
+
(0, utils_1.setLongestDescriptionForNode)(directiveDefinition.node, node.description);
|
|
187
|
+
directiveDefinition.node.repeatable = directiveDefinition.node.repeatable && node.repeatable;
|
|
188
|
+
directiveDefinition.subgraphs.add(this.currentSubgraphName);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const executableLocations = (0, utils_1.extractExecutableDirectiveLocations)(node.locations, new Set());
|
|
192
|
+
this.directiveDefinitions.set(directiveName, {
|
|
193
|
+
arguments: this.upsertArguments(node, new Map()),
|
|
194
|
+
executableLocations,
|
|
195
|
+
node: (0, ast_1.directiveDefinitionNodeToMutable)(node),
|
|
196
|
+
subgraphs: new Set([this.currentSubgraphName]),
|
|
197
|
+
});
|
|
198
|
+
if (executableLocations.size > 0) {
|
|
199
|
+
this.executableDirectives.add(directiveName);
|
|
347
200
|
}
|
|
348
201
|
}
|
|
349
202
|
upsertFieldNode(node) {
|
|
350
203
|
const parent = this.isCurrentParentExtensionType
|
|
351
|
-
? (0,
|
|
352
|
-
: (0,
|
|
353
|
-
if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION
|
|
354
|
-
parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION
|
|
355
|
-
parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
204
|
+
? (0, utils_3.getOrThrowError)(this.extensions, this.parentTypeName, string_constants_1.EXTENSIONS)
|
|
205
|
+
: (0, utils_3.getOrThrowError)(this.parents, this.parentTypeName, string_constants_1.PARENTS);
|
|
206
|
+
if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION
|
|
207
|
+
&& parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION
|
|
208
|
+
&& parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
356
209
|
throw (0, errors_1.unexpectedKindFatalError)(this.parentTypeName);
|
|
357
210
|
}
|
|
358
211
|
const fieldMap = parent.fields;
|
|
359
212
|
const isFieldShareable = this.isFieldShareable(node, parent);
|
|
360
213
|
const fieldPath = `${this.parentTypeName}.${this.childName}`;
|
|
361
214
|
const fieldRootTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const { typeErrors, typeNode } = (0, type_merging_1.getLeastRestrictiveMergedTypeNode)(existingFieldNode.node.type, node.type, this.parentTypeName, this.childName);
|
|
215
|
+
const existingFieldContainer = fieldMap.get(this.childName);
|
|
216
|
+
if (existingFieldContainer) {
|
|
217
|
+
this.extractPersistedDirectives(node.directives || [], existingFieldContainer.directives);
|
|
218
|
+
(0, utils_1.setLongestDescriptionForNode)(existingFieldContainer.node, node.description);
|
|
219
|
+
existingFieldContainer.subgraphs.add(this.currentSubgraphName);
|
|
220
|
+
existingFieldContainer.subgraphsByShareable.set(this.currentSubgraphName, isFieldShareable);
|
|
221
|
+
const { typeErrors, typeNode } = (0, type_merging_1.getLeastRestrictiveMergedTypeNode)(existingFieldContainer.node.type, node.type, this.parentTypeName, this.childName);
|
|
370
222
|
if (typeNode) {
|
|
371
|
-
|
|
223
|
+
existingFieldContainer.node.type = typeNode;
|
|
372
224
|
}
|
|
373
225
|
else {
|
|
374
226
|
if (!typeErrors || typeErrors.length < 2) {
|
|
375
|
-
throw
|
|
227
|
+
throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
|
|
376
228
|
}
|
|
377
229
|
this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
378
230
|
}
|
|
379
|
-
this.
|
|
231
|
+
this.upsertArguments(node, existingFieldContainer.arguments);
|
|
380
232
|
// If the parent is not an interface and both fields are not shareable, is it is a shareable error
|
|
381
|
-
if (!this.isCurrentParentInterface && (!
|
|
233
|
+
if (!this.isCurrentParentInterface && (!existingFieldContainer.isShareable || !isFieldShareable)) {
|
|
382
234
|
const shareableErrorTypeNames = this.shareableErrorTypeNames.get(this.parentTypeName);
|
|
383
235
|
if (shareableErrorTypeNames) {
|
|
384
236
|
shareableErrorTypeNames.add(this.childName);
|
|
@@ -389,27 +241,22 @@ class FederationFactory {
|
|
|
389
241
|
}
|
|
390
242
|
return;
|
|
391
243
|
}
|
|
392
|
-
const args = new Map();
|
|
393
|
-
this.extractArgumentsFromFieldNode(node, args);
|
|
394
244
|
this.outputFieldTypeNameSet.add(fieldRootTypeName);
|
|
395
245
|
fieldMap.set(this.childName, {
|
|
396
|
-
|
|
397
|
-
|
|
246
|
+
arguments: this.upsertArguments(node, new Map()),
|
|
247
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
248
|
+
directives: new Map(),
|
|
249
|
+
tags: new Map(),
|
|
250
|
+
}),
|
|
398
251
|
isShareable: isFieldShareable,
|
|
399
252
|
node: (0, ast_1.fieldDefinitionNodeToMutable)(node, this.parentTypeName),
|
|
400
|
-
|
|
253
|
+
namedTypeName: fieldRootTypeName,
|
|
401
254
|
subgraphs: new Set([this.currentSubgraphName]),
|
|
402
255
|
subgraphsByShareable: new Map([[this.currentSubgraphName, isFieldShareable]]),
|
|
403
256
|
});
|
|
404
|
-
if (this.isParentRootType ||
|
|
405
|
-
entityParent?.fields.has(this.childName) ||
|
|
406
|
-
parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
this.addPotentiallyUnresolvableField(parent, this.childName);
|
|
410
257
|
}
|
|
411
258
|
upsertValueNode(node) {
|
|
412
|
-
const parent = this.
|
|
259
|
+
const parent = this.parents.get(this.parentTypeName);
|
|
413
260
|
switch (node.kind) {
|
|
414
261
|
case graphql_1.Kind.ENUM_VALUE_DEFINITION:
|
|
415
262
|
if (!parent) {
|
|
@@ -420,37 +267,43 @@ class FederationFactory {
|
|
|
420
267
|
throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.ENUM_TYPE_DEFINITION, parent.kind);
|
|
421
268
|
}
|
|
422
269
|
const enumValues = parent.values;
|
|
423
|
-
const
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
|
|
270
|
+
const enumValueContainer = enumValues.get(this.childName);
|
|
271
|
+
if (enumValueContainer) {
|
|
272
|
+
this.extractPersistedDirectives(node.directives || [], enumValueContainer.directives);
|
|
273
|
+
(0, utils_1.setLongestDescriptionForNode)(enumValueContainer.node, node.description);
|
|
274
|
+
enumValueContainer.appearances += 1;
|
|
427
275
|
return;
|
|
428
276
|
}
|
|
429
277
|
enumValues.set(this.childName, {
|
|
430
278
|
appearances: 1,
|
|
279
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
280
|
+
directives: new Map(),
|
|
281
|
+
tags: new Map(),
|
|
282
|
+
}),
|
|
431
283
|
node: (0, ast_1.enumValueDefinitionNodeToMutable)(node),
|
|
432
284
|
});
|
|
433
285
|
return;
|
|
434
286
|
case graphql_1.Kind.INPUT_VALUE_DEFINITION:
|
|
435
287
|
if (!parent || !this.isParentInputObject) {
|
|
436
|
-
//
|
|
288
|
+
// these are arguments to a directive
|
|
437
289
|
return;
|
|
438
290
|
}
|
|
439
291
|
if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
|
|
440
292
|
throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, parent.kind);
|
|
441
293
|
}
|
|
442
294
|
const inputValues = parent.fields;
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
295
|
+
const inputValueContainer = inputValues.get(this.childName);
|
|
296
|
+
if (inputValueContainer) {
|
|
297
|
+
this.extractPersistedDirectives(node.directives || [], inputValueContainer.directives);
|
|
298
|
+
inputValueContainer.appearances += 1;
|
|
299
|
+
(0, utils_1.setLongestDescriptionForNode)(inputValueContainer.node, node.description);
|
|
300
|
+
const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(inputValueContainer.node.type, node.type, this.parentTypeName, this.childName);
|
|
448
301
|
if (typeNode) {
|
|
449
|
-
|
|
302
|
+
inputValueContainer.node.type = typeNode;
|
|
450
303
|
}
|
|
451
304
|
else {
|
|
452
305
|
if (!typeErrors || typeErrors.length < 2) {
|
|
453
|
-
throw
|
|
306
|
+
throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
|
|
454
307
|
}
|
|
455
308
|
this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
456
309
|
}
|
|
@@ -461,6 +314,10 @@ class FederationFactory {
|
|
|
461
314
|
this.inputFieldTypeNameSet.add(inputValueNamedType);
|
|
462
315
|
inputValues.set(this.childName, {
|
|
463
316
|
appearances: 1,
|
|
317
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
318
|
+
directives: new Map(),
|
|
319
|
+
tags: new Map(),
|
|
320
|
+
}),
|
|
464
321
|
includeDefaultValue: !!node.defaultValue,
|
|
465
322
|
node: (0, ast_1.inputValueDefinitionNodeToMutable)(node, this.parentTypeName),
|
|
466
323
|
});
|
|
@@ -471,10 +328,10 @@ class FederationFactory {
|
|
|
471
328
|
}
|
|
472
329
|
upsertParentNode(node) {
|
|
473
330
|
const parentTypeName = node.name.value;
|
|
474
|
-
const parent = this.
|
|
331
|
+
const parent = this.parents.get(parentTypeName);
|
|
475
332
|
if (parent) {
|
|
476
|
-
|
|
477
|
-
|
|
333
|
+
(0, utils_1.setLongestDescriptionForNode)(parent.node, node.description);
|
|
334
|
+
this.extractPersistedDirectives(node.directives || [], parent.directives);
|
|
478
335
|
}
|
|
479
336
|
switch (node.kind) {
|
|
480
337
|
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
@@ -482,10 +339,15 @@ class FederationFactory {
|
|
|
482
339
|
if (parent.kind !== node.kind) {
|
|
483
340
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
|
|
484
341
|
}
|
|
342
|
+
parent.appearances += 1;
|
|
485
343
|
return;
|
|
486
344
|
}
|
|
487
|
-
this.
|
|
345
|
+
this.parents.set(parentTypeName, {
|
|
488
346
|
appearances: 1,
|
|
347
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
348
|
+
directives: new Map(),
|
|
349
|
+
tags: new Map(),
|
|
350
|
+
}),
|
|
489
351
|
values: new Map(),
|
|
490
352
|
kind: node.kind,
|
|
491
353
|
node: (0, ast_1.enumTypeDefinitionNodeToMutable)(node),
|
|
@@ -496,10 +358,15 @@ class FederationFactory {
|
|
|
496
358
|
if (parent.kind !== node.kind) {
|
|
497
359
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
|
|
498
360
|
}
|
|
361
|
+
parent.appearances += 1;
|
|
499
362
|
return;
|
|
500
363
|
}
|
|
501
|
-
this.
|
|
364
|
+
this.parents.set(parentTypeName, {
|
|
502
365
|
appearances: 1,
|
|
366
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
367
|
+
directives: new Map(),
|
|
368
|
+
tags: new Map(),
|
|
369
|
+
}),
|
|
503
370
|
fields: new Map(),
|
|
504
371
|
kind: node.kind,
|
|
505
372
|
node: (0, ast_1.inputObjectTypeDefinitionNodeToMutable)(node),
|
|
@@ -516,8 +383,11 @@ class FederationFactory {
|
|
|
516
383
|
}
|
|
517
384
|
const nestedInterfaces = new Set();
|
|
518
385
|
(0, utils_1.extractInterfaces)(node, nestedInterfaces);
|
|
519
|
-
this.
|
|
520
|
-
|
|
386
|
+
this.parents.set(parentTypeName, {
|
|
387
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
388
|
+
directives: new Map(),
|
|
389
|
+
tags: new Map(),
|
|
390
|
+
}),
|
|
521
391
|
fields: new Map(),
|
|
522
392
|
interfaces: nestedInterfaces,
|
|
523
393
|
kind: node.kind,
|
|
@@ -532,8 +402,11 @@ class FederationFactory {
|
|
|
532
402
|
}
|
|
533
403
|
return;
|
|
534
404
|
}
|
|
535
|
-
this.
|
|
536
|
-
|
|
405
|
+
this.parents.set(parentTypeName, {
|
|
406
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
407
|
+
directives: new Map(),
|
|
408
|
+
tags: new Map(),
|
|
409
|
+
}),
|
|
537
410
|
kind: node.kind,
|
|
538
411
|
node: (0, ast_1.scalarTypeDefinitionNodeToMutable)(node),
|
|
539
412
|
});
|
|
@@ -544,16 +417,19 @@ class FederationFactory {
|
|
|
544
417
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
|
|
545
418
|
}
|
|
546
419
|
(0, utils_1.extractInterfaces)(node, parent.interfaces);
|
|
547
|
-
(0, utils_1.extractEntityKeys)(node, parent.entityKeys);
|
|
420
|
+
(0, utils_1.extractEntityKeys)(node, parent.entityKeys, this.errors);
|
|
548
421
|
parent.subgraphs.add(this.currentSubgraphName);
|
|
549
422
|
return;
|
|
550
423
|
}
|
|
551
424
|
const interfaces = new Set();
|
|
552
425
|
(0, utils_1.extractInterfaces)(node, interfaces);
|
|
553
426
|
const entityKeys = new Set();
|
|
554
|
-
(0, utils_1.extractEntityKeys)(node, entityKeys);
|
|
555
|
-
this.
|
|
556
|
-
|
|
427
|
+
(0, utils_1.extractEntityKeys)(node, entityKeys, this.errors);
|
|
428
|
+
this.parents.set(parentTypeName, {
|
|
429
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
430
|
+
directives: new Map(),
|
|
431
|
+
tags: new Map(),
|
|
432
|
+
}),
|
|
557
433
|
fields: new Map(),
|
|
558
434
|
entityKeys,
|
|
559
435
|
interfaces,
|
|
@@ -575,8 +451,11 @@ class FederationFactory {
|
|
|
575
451
|
node.types?.forEach((member) => parent.members.add(member.name.value));
|
|
576
452
|
return;
|
|
577
453
|
}
|
|
578
|
-
this.
|
|
579
|
-
|
|
454
|
+
this.parents.set(parentTypeName, {
|
|
455
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
456
|
+
directives: new Map(),
|
|
457
|
+
tags: new Map(),
|
|
458
|
+
}),
|
|
580
459
|
kind: node.kind,
|
|
581
460
|
members: new Set(node.types?.map((member) => member.name.value)),
|
|
582
461
|
node: (0, ast_1.unionTypeDefinitionNodeToMutable)(node),
|
|
@@ -584,98 +463,66 @@ class FederationFactory {
|
|
|
584
463
|
return;
|
|
585
464
|
}
|
|
586
465
|
}
|
|
587
|
-
upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName = fieldTypeName, hasAbstractParent = false) {
|
|
588
|
-
const operationFields = this.rootTypeFieldsByResponseTypeName.get(concreteTypeName);
|
|
589
|
-
if (!operationFields) {
|
|
590
|
-
this.rootTypeFieldsByResponseTypeName.set(concreteTypeName, new Map([
|
|
591
|
-
[
|
|
592
|
-
operationFieldPath,
|
|
593
|
-
{
|
|
594
|
-
inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
|
|
595
|
-
name: fieldName,
|
|
596
|
-
parentTypeName: this.parentTypeName,
|
|
597
|
-
path: operationFieldPath,
|
|
598
|
-
responseType,
|
|
599
|
-
rootTypeName: fieldTypeName,
|
|
600
|
-
subgraphs: new Set([this.currentSubgraphName]),
|
|
601
|
-
},
|
|
602
|
-
],
|
|
603
|
-
]));
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
const operationField = operationFields.get(operationFieldPath);
|
|
607
|
-
if (operationField) {
|
|
608
|
-
operationField.subgraphs.add(this.currentSubgraphName);
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
operationFields.set(operationFieldPath, {
|
|
612
|
-
inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
|
|
613
|
-
name: fieldName,
|
|
614
|
-
parentTypeName: this.parentTypeName,
|
|
615
|
-
path: operationFieldPath,
|
|
616
|
-
responseType,
|
|
617
|
-
rootTypeName: fieldTypeName,
|
|
618
|
-
subgraphs: new Set([this.currentSubgraphName]),
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
upsertAbstractObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeNames) {
|
|
622
|
-
for (const concreteTypeName of concreteTypeNames) {
|
|
623
|
-
if (!this.graph.hasNode(concreteTypeName)) {
|
|
624
|
-
throw (0, errors_1.invalidMultiGraphNodeFatalError)(concreteTypeName); // should never happen
|
|
625
|
-
}
|
|
626
|
-
if (!this.graphEdges.has(operationFieldPath)) {
|
|
627
|
-
this.graph.addEdge(this.parentTypeName, concreteTypeName, { fieldName });
|
|
628
|
-
}
|
|
629
|
-
// Always upsert the operation field node to record subgraph appearances
|
|
630
|
-
this.upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName, true);
|
|
631
|
-
}
|
|
632
|
-
// Add the path so the edges are not added again
|
|
633
|
-
this.graphEdges.add(operationFieldPath);
|
|
634
|
-
}
|
|
635
|
-
validatePotentiallyUnresolvableFields() {
|
|
636
|
-
if (this.sharedRootTypeFieldDependentResponses.size < 1) {
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
for (const [parentTypeName, potentiallyUnresolvableFields] of this.sharedRootTypeFieldDependentResponses) {
|
|
640
|
-
for (const potentiallyUnresolvableField of potentiallyUnresolvableFields) {
|
|
641
|
-
// There is no issue if the field is resolvable from at least one subgraph
|
|
642
|
-
const operationField = potentiallyUnresolvableField.rootTypeField;
|
|
643
|
-
const fieldContainer = potentiallyUnresolvableField.fieldContainer;
|
|
644
|
-
if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
|
|
645
|
-
continue;
|
|
646
|
-
}
|
|
647
|
-
const fieldSubgraphs = [...fieldContainer.subgraphs].join('", "');
|
|
648
|
-
this.errors.push((0, errors_1.unresolvableFieldError)(operationField, fieldContainer.node.name.value, potentiallyUnresolvableField.fullResolverPaths, fieldSubgraphs, parentTypeName));
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
466
|
upsertExtensionNode(node) {
|
|
653
467
|
const extension = this.extensions.get(this.parentTypeName);
|
|
654
468
|
if (extension) {
|
|
655
469
|
if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
656
470
|
throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.OBJECT_TYPE_EXTENSION, extension.kind);
|
|
657
471
|
}
|
|
658
|
-
extension.appearances += 1;
|
|
659
472
|
extension.subgraphs.add(this.currentSubgraphName);
|
|
660
473
|
(0, utils_1.extractInterfaces)(node, extension.interfaces);
|
|
474
|
+
this.extractPersistedDirectives(node.directives || [], extension.directives);
|
|
661
475
|
return;
|
|
662
476
|
}
|
|
663
477
|
// build a new extension
|
|
664
|
-
const interfaces = new Set();
|
|
665
|
-
(0, utils_1.
|
|
666
|
-
const entityKeys = new Set();
|
|
667
|
-
(0, utils_1.extractEntityKeys)(node, entityKeys);
|
|
478
|
+
const interfaces = (0, utils_1.extractInterfaces)(node, new Set());
|
|
479
|
+
const entityKeys = (0, utils_1.extractEntityKeys)(node, new Set(), this.errors);
|
|
668
480
|
this.extensions.set(this.parentTypeName, {
|
|
669
|
-
|
|
670
|
-
|
|
481
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
482
|
+
directives: new Map(),
|
|
483
|
+
tags: new Map(),
|
|
484
|
+
}),
|
|
485
|
+
entityKeys,
|
|
486
|
+
fields: new Map(),
|
|
487
|
+
interfaces,
|
|
671
488
|
isRootType: this.isParentRootType,
|
|
672
489
|
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
|
|
673
490
|
node: (0, ast_1.objectTypeExtensionNodeToMutable)(node),
|
|
674
|
-
|
|
675
|
-
entityKeys,
|
|
676
|
-
interfaces,
|
|
491
|
+
subgraphs: new Set([this.currentSubgraphName]),
|
|
677
492
|
});
|
|
678
493
|
}
|
|
494
|
+
isTypeValidImplementation(originalType, implementationType) {
|
|
495
|
+
if (originalType.kind === graphql_1.Kind.NON_NULL_TYPE) {
|
|
496
|
+
if (implementationType.kind !== graphql_1.Kind.NON_NULL_TYPE) {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
return this.isTypeValidImplementation(originalType.type, implementationType.type);
|
|
500
|
+
}
|
|
501
|
+
if (implementationType.kind === graphql_1.Kind.NON_NULL_TYPE) {
|
|
502
|
+
return this.isTypeValidImplementation(originalType, implementationType.type);
|
|
503
|
+
}
|
|
504
|
+
switch (originalType.kind) {
|
|
505
|
+
case graphql_1.Kind.NAMED_TYPE:
|
|
506
|
+
if (implementationType.kind === graphql_1.Kind.NAMED_TYPE) {
|
|
507
|
+
const originalTypeName = originalType.name.value;
|
|
508
|
+
const implementationTypeName = implementationType.name.value;
|
|
509
|
+
if (originalTypeName === implementationTypeName) {
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
const concreteTypes = this.abstractToConcreteTypeNames.get(originalTypeName);
|
|
513
|
+
if (!concreteTypes) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
return concreteTypes.has(implementationTypeName);
|
|
517
|
+
}
|
|
518
|
+
return false;
|
|
519
|
+
default:
|
|
520
|
+
if (implementationType.kind === graphql_1.Kind.LIST_TYPE) {
|
|
521
|
+
return this.isTypeValidImplementation(originalType.type, implementationType.type);
|
|
522
|
+
}
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
679
526
|
getAndValidateImplementedInterfaces(container) {
|
|
680
527
|
const interfaces = [];
|
|
681
528
|
if (container.interfaces.size < 1) {
|
|
@@ -684,7 +531,11 @@ class FederationFactory {
|
|
|
684
531
|
const implementationErrorsMap = new Map();
|
|
685
532
|
for (const interfaceName of container.interfaces) {
|
|
686
533
|
interfaces.push((0, utils_1.stringToNamedTypeNode)(interfaceName));
|
|
687
|
-
const interfaceContainer =
|
|
534
|
+
const interfaceContainer = this.parents.get(interfaceName);
|
|
535
|
+
if (!interfaceContainer) {
|
|
536
|
+
this.errors.push((0, errors_1.undefinedTypeError)(interfaceName));
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
688
539
|
if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
|
|
689
540
|
throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
|
|
690
541
|
}
|
|
@@ -708,7 +559,7 @@ class FederationFactory {
|
|
|
708
559
|
unimplementedArguments: new Set(),
|
|
709
560
|
};
|
|
710
561
|
// The implemented field type must be equally or more restrictive than the original interface field type
|
|
711
|
-
if (!
|
|
562
|
+
if (!this.isTypeValidImplementation(interfaceField.node.type, containerField.node.type)) {
|
|
712
563
|
hasErrors = true;
|
|
713
564
|
hasNestedErrors = true;
|
|
714
565
|
invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
|
|
@@ -756,10 +607,233 @@ class FederationFactory {
|
|
|
756
607
|
}
|
|
757
608
|
}
|
|
758
609
|
if (implementationErrorsMap.size) {
|
|
759
|
-
this.errors.push((0, errors_1.unimplementedInterfaceFieldsError)(container.node.name.value, (0,
|
|
610
|
+
this.errors.push((0, errors_1.unimplementedInterfaceFieldsError)(container.node.name.value, (0, utils_3.kindToTypeString)(container.kind), implementationErrorsMap));
|
|
760
611
|
}
|
|
761
612
|
return interfaces;
|
|
762
613
|
}
|
|
614
|
+
mergeArguments(container, args, errors, argumentNames) {
|
|
615
|
+
for (const argumentContainer of container.arguments.values()) {
|
|
616
|
+
const missingSubgraphs = (0, utils_3.getEntriesNotInHashSet)(container.subgraphs, argumentContainer.subgraphs);
|
|
617
|
+
const argumentName = argumentContainer.node.name.value;
|
|
618
|
+
if (missingSubgraphs.length > 0) {
|
|
619
|
+
// Required arguments must be defined in all subgraphs that define the field
|
|
620
|
+
if (argumentContainer.requiredSubgraphs.size > 0) {
|
|
621
|
+
errors.push({
|
|
622
|
+
argumentName,
|
|
623
|
+
missingSubgraphs,
|
|
624
|
+
requiredSubgraphs: [...argumentContainer.requiredSubgraphs]
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
// If the argument is always optional, but it's not defined in all subgraphs that define the field,
|
|
628
|
+
// the argument should not be included in the federated graph
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
argumentContainer.node.defaultValue = argumentContainer.includeDefaultValue
|
|
632
|
+
? argumentContainer.node.defaultValue : undefined;
|
|
633
|
+
args.push(argumentContainer.node);
|
|
634
|
+
if (argumentNames) {
|
|
635
|
+
argumentNames.push(argumentName);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
addValidExecutableDirectiveDefinition(directiveName, directiveContainer, definitions) {
|
|
640
|
+
if (!this.executableDirectives.has(directiveName)) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (this.subgraphs.length !== directiveContainer.subgraphs.size) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
directiveContainer.node.locations = (0, utils_1.setToNameNodeArray)(directiveContainer.executableLocations);
|
|
647
|
+
if (!directiveContainer.arguments) {
|
|
648
|
+
definitions.push(directiveContainer.node);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const args = [];
|
|
652
|
+
const errors = [];
|
|
653
|
+
this.mergeArguments(directiveContainer, args, errors);
|
|
654
|
+
if (errors.length > 0) {
|
|
655
|
+
this.errors.push((0, errors_1.invalidRequiredArgumentsError)(string_constants_1.DIRECTIVE_DEFINITION, directiveName, errors));
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
directiveContainer.node.arguments = args;
|
|
659
|
+
definitions.push(directiveContainer.node);
|
|
660
|
+
}
|
|
661
|
+
getMergedFieldDefinitionNode(fieldContainer, parentTypeName) {
|
|
662
|
+
if (!fieldContainer.arguments) {
|
|
663
|
+
return fieldContainer.node;
|
|
664
|
+
}
|
|
665
|
+
(0, utils_1.pushPersistedDirectivesToNode)(fieldContainer);
|
|
666
|
+
const fieldName = fieldContainer.node.name.value;
|
|
667
|
+
const fieldPath = `${parentTypeName}.${fieldName}`;
|
|
668
|
+
const args = [];
|
|
669
|
+
const errors = [];
|
|
670
|
+
const argumentNames = [];
|
|
671
|
+
this.mergeArguments(fieldContainer, args, errors, argumentNames);
|
|
672
|
+
if (errors.length > 0) {
|
|
673
|
+
this.errors.push((0, errors_1.invalidRequiredArgumentsError)(string_constants_1.FIELD, fieldPath, errors));
|
|
674
|
+
}
|
|
675
|
+
else if (argumentNames.length > 0) {
|
|
676
|
+
this.argumentConfigurations.push({
|
|
677
|
+
argumentNames,
|
|
678
|
+
fieldName,
|
|
679
|
+
typeName: parentTypeName
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
fieldContainer.node.arguments = args;
|
|
683
|
+
return fieldContainer.node;
|
|
684
|
+
}
|
|
685
|
+
// tags with the same name string are merged
|
|
686
|
+
mergeTagDirectives(directive, map) {
|
|
687
|
+
// the directive has been validated in the normalizer
|
|
688
|
+
if (!directive.arguments || directive.arguments.length !== 1) {
|
|
689
|
+
this.errors.push(errors_1.invalidTagDirectiveError); // should never happen
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const nameArgument = directive.arguments[0].value;
|
|
693
|
+
if (nameArgument.kind !== graphql_1.Kind.STRING) {
|
|
694
|
+
this.errors.push(errors_1.invalidTagDirectiveError); // should never happen
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
map.set(nameArgument.value, directive);
|
|
698
|
+
}
|
|
699
|
+
extractPersistedDirectives(directives, container) {
|
|
700
|
+
if (directives.length < 1) {
|
|
701
|
+
return container;
|
|
702
|
+
}
|
|
703
|
+
for (const directive of directives) {
|
|
704
|
+
const directiveName = directive.name.value;
|
|
705
|
+
if (!this.persistedDirectives.has(directiveName)) {
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (directiveName === string_constants_1.TAG) {
|
|
709
|
+
this.mergeTagDirectives(directive, container.tags);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const existingDirectives = container.directives.get(directiveName);
|
|
713
|
+
if (!existingDirectives) {
|
|
714
|
+
container.directives.set(directiveName, [directive]);
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
// Naïvely ignore non-repeatable directives
|
|
718
|
+
const definition = (0, utils_3.getOrThrowError)(this.directiveDefinitions, directiveName, 'directiveDefinitions');
|
|
719
|
+
if (!definition.node.repeatable) {
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
existingDirectives.push(directive);
|
|
723
|
+
}
|
|
724
|
+
return container;
|
|
725
|
+
}
|
|
726
|
+
entityAncestor(entityAncestors, fieldSubgraphs, parentTypeName) {
|
|
727
|
+
if (!this.graph.hasNode(parentTypeName)) {
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
for (const entityAncestorName of entityAncestors) {
|
|
731
|
+
const path = `${entityAncestorName}.${parentTypeName}`;
|
|
732
|
+
if (this.graphPaths.get(path)) {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
if (entityAncestorName === parentTypeName) {
|
|
736
|
+
const hasOverlap = (0, utils_3.doSetsHaveAnyOverlap)(fieldSubgraphs, (0, utils_3.getOrThrowError)(this.entities, entityAncestorName, string_constants_1.ENTITIES).subgraphs);
|
|
737
|
+
this.graphPaths.set(path, hasOverlap);
|
|
738
|
+
return hasOverlap;
|
|
739
|
+
}
|
|
740
|
+
if ((0, utils_3.hasSimplePath)(this.graph, entityAncestorName, parentTypeName)) {
|
|
741
|
+
this.graphPaths.set(path, true);
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
this.graphPaths.set(path, false);
|
|
745
|
+
}
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
evaluateResolvabilityOfObject(parentContainer, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors, isParentAbstract = false) {
|
|
749
|
+
const parentTypeName = parentContainer.node.name.value;
|
|
750
|
+
if (evaluatedObjectLikes.has(parentTypeName)) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
for (const [fieldName, fieldContainer] of parentContainer.fields) {
|
|
754
|
+
const fieldNamedTypeName = fieldContainer.namedTypeName;
|
|
755
|
+
if (string_constants_1.ROOT_TYPES.has(fieldNamedTypeName)) {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
// Avoid an infinite loop with self-referential objects
|
|
759
|
+
if (evaluatedObjectLikes.has(fieldNamedTypeName)) {
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const isFieldResolvable = (0, utils_3.doSetsHaveAnyOverlap)(rootTypeFieldData.subgraphs, fieldContainer.subgraphs);
|
|
763
|
+
const newCurrentFieldPath = currentFieldPath + (isParentAbstract ? ' ' : '.') + fieldName;
|
|
764
|
+
const entity = this.entities.get(fieldNamedTypeName);
|
|
765
|
+
if (isFieldResolvable || this.entityAncestor(entityAncestors, fieldContainer.subgraphs, parentTypeName)) {
|
|
766
|
+
// The base scalars are not in this.parentMap
|
|
767
|
+
if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
const childContainer = (0, utils_3.getOrThrowError)(this.parents, fieldNamedTypeName, string_constants_1.PARENTS);
|
|
771
|
+
switch (childContainer.kind) {
|
|
772
|
+
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
773
|
+
// intentional fallthrough
|
|
774
|
+
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
775
|
+
continue;
|
|
776
|
+
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
777
|
+
this.evaluateResolvabilityOfObject(childContainer, rootTypeFieldData, newCurrentFieldPath, new Set([...evaluatedObjectLikes, parentTypeName]), entity ? [...entityAncestors, fieldNamedTypeName] : [...entityAncestors]);
|
|
778
|
+
continue;
|
|
779
|
+
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
780
|
+
// intentional fallthrough
|
|
781
|
+
case graphql_1.Kind.UNION_TYPE_DEFINITION:
|
|
782
|
+
this.evaluateResolvabilityOfAbstractType(fieldNamedTypeName, childContainer.kind, rootTypeFieldData, newCurrentFieldPath, new Set([...evaluatedObjectLikes, parentTypeName]), entity ? [...entityAncestors, fieldNamedTypeName] : [...entityAncestors], fieldContainer.subgraphs);
|
|
783
|
+
continue;
|
|
784
|
+
default:
|
|
785
|
+
this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
|
|
790
|
+
this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath, parentTypeName));
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const childContainer = (0, utils_3.getOrThrowError)(this.parents, fieldNamedTypeName, string_constants_1.PARENTS);
|
|
794
|
+
switch (childContainer.kind) {
|
|
795
|
+
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
796
|
+
// intentional fallthrough
|
|
797
|
+
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
798
|
+
this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath, parentTypeName));
|
|
799
|
+
continue;
|
|
800
|
+
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
801
|
+
// intentional fallthrough
|
|
802
|
+
case graphql_1.Kind.UNION_TYPE_DEFINITION:
|
|
803
|
+
// intentional fallthrough
|
|
804
|
+
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
805
|
+
this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath + string_constants_1.SELECTION_REPRESENTATION, parentTypeName));
|
|
806
|
+
continue;
|
|
807
|
+
default:
|
|
808
|
+
this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
evaluateResolvabilityOfAbstractType(fieldNamedTypeName, fieldNamedTypeKind, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors, parentSubgraphs) {
|
|
813
|
+
if (evaluatedObjectLikes.has(fieldNamedTypeName)) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
const concreteTypeNames = this.abstractToConcreteTypeNames.get(fieldNamedTypeName);
|
|
817
|
+
if (!concreteTypeNames) {
|
|
818
|
+
(0, errors_1.noConcreteTypesForAbstractTypeError)((0, utils_3.kindToTypeString)(fieldNamedTypeKind), fieldNamedTypeName);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
for (const concreteTypeName of concreteTypeNames) {
|
|
822
|
+
if (evaluatedObjectLikes.has(concreteTypeName)) {
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
const concreteParentContainer = (0, utils_3.getOrThrowError)(this.parents, concreteTypeName, string_constants_1.PARENTS);
|
|
826
|
+
if (concreteParentContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
827
|
+
// throw
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (!(0, utils_3.doSetsHaveAnyOverlap)(concreteParentContainer.subgraphs, parentSubgraphs)) {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
const entity = this.entities.get(concreteTypeName);
|
|
834
|
+
this.evaluateResolvabilityOfObject(concreteParentContainer, rootTypeFieldData, currentFieldPath + ` ... on ` + concreteTypeName, new Set([...evaluatedObjectLikes, fieldNamedTypeName]), entity ? [...entityAncestors, concreteTypeName] : [...entityAncestors], true);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
763
837
|
federate() {
|
|
764
838
|
this.populateMultiGraphAndRenameOperations(this.subgraphs);
|
|
765
839
|
const factory = this;
|
|
@@ -768,16 +842,20 @@ class FederationFactory {
|
|
|
768
842
|
this.currentSubgraphName = subgraph.name;
|
|
769
843
|
(0, subgraph_1.walkSubgraphToFederate)(subgraph.definitions, factory);
|
|
770
844
|
}
|
|
771
|
-
this.validatePotentiallyUnresolvableFields();
|
|
772
845
|
const definitions = [];
|
|
773
|
-
for (const
|
|
774
|
-
|
|
846
|
+
for (const [directiveName, directiveContainer] of this.directiveDefinitions) {
|
|
847
|
+
if (this.persistedDirectives.has(directiveName)) {
|
|
848
|
+
definitions.push(directiveContainer.node);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
// The definitions must be present in all subgraphs to kept in the federated graph
|
|
852
|
+
this.addValidExecutableDirectiveDefinition(directiveName, directiveContainer, definitions);
|
|
775
853
|
}
|
|
776
854
|
for (const [typeName, extension] of this.extensions) {
|
|
777
|
-
if (extension.isRootType && !this.
|
|
855
|
+
if (extension.isRootType && !this.parents.has(typeName)) {
|
|
778
856
|
this.upsertParentNode((0, ast_1.objectTypeExtensionNodeToMutableDefinitionNode)(extension.node));
|
|
779
857
|
}
|
|
780
|
-
const baseObject = this.
|
|
858
|
+
const baseObject = this.parents.get(typeName);
|
|
781
859
|
if (!baseObject) {
|
|
782
860
|
this.errors.push((0, errors_1.noBaseTypeExtensionError)(typeName));
|
|
783
861
|
continue;
|
|
@@ -785,152 +863,238 @@ class FederationFactory {
|
|
|
785
863
|
if (baseObject.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
786
864
|
throw (0, errors_1.incompatibleParentKindFatalError)(typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, baseObject.kind);
|
|
787
865
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
baseObject.fields.set(fieldName, field);
|
|
866
|
+
for (const [extensionFieldName, extensionFieldContainer] of extension.fields) {
|
|
867
|
+
const baseFieldContainer = baseObject.fields.get(extensionFieldName);
|
|
868
|
+
if (!baseFieldContainer) {
|
|
869
|
+
baseObject.fields.set(extensionFieldName, extensionFieldContainer);
|
|
793
870
|
continue;
|
|
794
871
|
}
|
|
795
|
-
if (
|
|
872
|
+
if (baseFieldContainer.isShareable && extensionFieldContainer.isShareable) {
|
|
873
|
+
(0, utils_1.setLongestDescriptionForNode)(baseFieldContainer.node, extensionFieldContainer.node.description);
|
|
874
|
+
(0, utils_3.addIterableValuesToSet)(extensionFieldContainer.subgraphs, baseFieldContainer.subgraphs);
|
|
796
875
|
continue;
|
|
797
876
|
}
|
|
798
877
|
const parent = this.shareableErrorTypeNames.get(typeName);
|
|
799
878
|
if (parent) {
|
|
800
|
-
parent.add(
|
|
879
|
+
parent.add(extensionFieldName);
|
|
801
880
|
continue;
|
|
802
881
|
}
|
|
803
|
-
this.shareableErrorTypeNames.set(typeName, new Set([
|
|
882
|
+
this.shareableErrorTypeNames.set(typeName, new Set([extensionFieldName]));
|
|
804
883
|
}
|
|
805
884
|
for (const interfaceName of extension.interfaces) {
|
|
806
885
|
baseObject.interfaces.add(interfaceName);
|
|
807
886
|
}
|
|
808
887
|
}
|
|
809
888
|
for (const [parentTypeName, children] of this.shareableErrorTypeNames) {
|
|
810
|
-
const parent = (0,
|
|
889
|
+
const parent = (0, utils_3.getOrThrowError)(this.parents, parentTypeName, string_constants_1.PARENTS);
|
|
811
890
|
if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
812
891
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, parent.kind);
|
|
813
892
|
}
|
|
814
893
|
this.errors.push((0, errors_1.shareableFieldDefinitionsError)(parent, children));
|
|
815
894
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
895
|
+
const objectLikeContainersWithInterfaces = [];
|
|
896
|
+
for (const parentContainer of this.parents.values()) {
|
|
897
|
+
const parentTypeName = parentContainer.node.name.value;
|
|
898
|
+
switch (parentContainer.kind) {
|
|
819
899
|
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
820
900
|
const values = [];
|
|
821
|
-
const mergeMethod = this.getEnumMergeMethod(
|
|
822
|
-
for (const
|
|
901
|
+
const mergeMethod = this.getEnumMergeMethod(parentTypeName);
|
|
902
|
+
for (const enumValueContainer of parentContainer.values.values()) {
|
|
903
|
+
(0, utils_1.pushPersistedDirectivesToNode)(enumValueContainer);
|
|
823
904
|
switch (mergeMethod) {
|
|
824
|
-
case
|
|
825
|
-
if (
|
|
826
|
-
this.errors.push((0, errors_1.incompatibleSharedEnumError)(
|
|
905
|
+
case utils_2.MergeMethod.CONSISTENT:
|
|
906
|
+
if (enumValueContainer.appearances < parentContainer.appearances) {
|
|
907
|
+
this.errors.push((0, errors_1.incompatibleSharedEnumError)(parentTypeName));
|
|
827
908
|
}
|
|
828
|
-
values.push(
|
|
909
|
+
values.push(enumValueContainer.node);
|
|
829
910
|
break;
|
|
830
|
-
case
|
|
831
|
-
if (
|
|
832
|
-
values.push(
|
|
911
|
+
case utils_2.MergeMethod.INTERSECTION:
|
|
912
|
+
if (enumValueContainer.appearances === parentContainer.appearances) {
|
|
913
|
+
values.push(enumValueContainer.node);
|
|
833
914
|
}
|
|
834
915
|
break;
|
|
835
916
|
default:
|
|
836
|
-
values.push(
|
|
917
|
+
values.push(enumValueContainer.node);
|
|
837
918
|
break;
|
|
838
919
|
}
|
|
839
920
|
}
|
|
840
|
-
|
|
841
|
-
definitions.push(
|
|
921
|
+
parentContainer.node.values = values;
|
|
922
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
842
923
|
break;
|
|
843
924
|
case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
844
925
|
const inputValues = [];
|
|
845
|
-
for (const
|
|
846
|
-
|
|
847
|
-
|
|
926
|
+
for (const inputValueContainer of parentContainer.fields.values()) {
|
|
927
|
+
(0, utils_1.pushPersistedDirectivesToNode)(inputValueContainer);
|
|
928
|
+
if (parentContainer.appearances === inputValueContainer.appearances) {
|
|
929
|
+
inputValues.push(inputValueContainer.node);
|
|
848
930
|
}
|
|
849
|
-
else if ((0, type_merging_1.isTypeRequired)(
|
|
850
|
-
|
|
851
|
-
|
|
931
|
+
else if ((0, type_merging_1.isTypeRequired)(inputValueContainer.node.type)) {
|
|
932
|
+
this.errors.push((0, errors_1.federationRequiredInputFieldError)(parentTypeName, inputValueContainer.node.name.value));
|
|
933
|
+
break;
|
|
852
934
|
}
|
|
853
935
|
}
|
|
854
|
-
|
|
855
|
-
definitions.push(
|
|
936
|
+
parentContainer.node.fields = inputValues;
|
|
937
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
856
938
|
break;
|
|
857
939
|
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
858
940
|
const interfaceFields = [];
|
|
859
|
-
for (const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
}
|
|
868
|
-
interfaceFields.push(field.node);
|
|
941
|
+
for (const fieldContainer of parentContainer.fields.values()) {
|
|
942
|
+
interfaceFields.push(this.getMergedFieldDefinitionNode(fieldContainer, parentTypeName));
|
|
943
|
+
}
|
|
944
|
+
parentContainer.node.fields = interfaceFields;
|
|
945
|
+
(0, utils_1.pushPersistedDirectivesToNode)(parentContainer);
|
|
946
|
+
// Interface implementations can only be evaluated after they've been fully merged
|
|
947
|
+
if (parentContainer.interfaces.size > 0) {
|
|
948
|
+
objectLikeContainersWithInterfaces.push(parentContainer);
|
|
869
949
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
otherInterfaces.push({
|
|
873
|
-
kind: graphql_1.Kind.NAMED_TYPE,
|
|
874
|
-
name: {
|
|
875
|
-
kind: graphql_1.Kind.NAME,
|
|
876
|
-
value: iFace,
|
|
877
|
-
},
|
|
878
|
-
});
|
|
950
|
+
else {
|
|
951
|
+
definitions.push(parentContainer.node);
|
|
879
952
|
}
|
|
880
|
-
parent.node.interfaces = otherInterfaces;
|
|
881
|
-
parent.node.fields = interfaceFields;
|
|
882
|
-
definitions.push(parent.node);
|
|
883
953
|
break;
|
|
884
954
|
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
885
955
|
const fields = [];
|
|
886
|
-
for (const
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
956
|
+
for (const fieldContainer of parentContainer.fields.values()) {
|
|
957
|
+
fields.push(this.getMergedFieldDefinitionNode(fieldContainer, parentTypeName));
|
|
958
|
+
}
|
|
959
|
+
parentContainer.node.fields = fields;
|
|
960
|
+
(0, utils_1.pushPersistedDirectivesToNode)(parentContainer);
|
|
961
|
+
// Interface implementations can only be evaluated after they've been fully merged
|
|
962
|
+
if (parentContainer.interfaces.size > 0) {
|
|
963
|
+
objectLikeContainersWithInterfaces.push(parentContainer);
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
definitions.push(parentContainer.node);
|
|
896
967
|
}
|
|
897
|
-
parent.node.fields = fields;
|
|
898
|
-
parent.node.interfaces = this.getAndValidateImplementedInterfaces(parent);
|
|
899
|
-
definitions.push(parent.node);
|
|
900
968
|
break;
|
|
901
969
|
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
902
|
-
definitions.push(
|
|
970
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
903
971
|
break;
|
|
904
972
|
case graphql_1.Kind.UNION_TYPE_DEFINITION:
|
|
905
973
|
const types = [];
|
|
906
|
-
for (const
|
|
907
|
-
types.push(
|
|
908
|
-
kind: graphql_1.Kind.NAMED_TYPE,
|
|
909
|
-
name: {
|
|
910
|
-
kind: graphql_1.Kind.NAME,
|
|
911
|
-
value: member,
|
|
912
|
-
},
|
|
913
|
-
});
|
|
974
|
+
for (const memberName of parentContainer.members) {
|
|
975
|
+
types.push((0, utils_1.stringToNamedTypeNode)(memberName));
|
|
914
976
|
}
|
|
915
|
-
|
|
916
|
-
definitions.push(
|
|
977
|
+
parentContainer.node.types = types;
|
|
978
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
917
979
|
break;
|
|
918
980
|
}
|
|
919
981
|
}
|
|
982
|
+
for (const container of objectLikeContainersWithInterfaces) {
|
|
983
|
+
container.node.interfaces = this.getAndValidateImplementedInterfaces(container);
|
|
984
|
+
definitions.push(container.node);
|
|
985
|
+
}
|
|
986
|
+
if (!this.parents.has(string_constants_1.QUERY)) {
|
|
987
|
+
this.errors.push(errors_1.noQueryRootTypeError);
|
|
988
|
+
}
|
|
989
|
+
// return any composition errors before checking whether all fields are resolvable
|
|
920
990
|
if (this.errors.length > 0) {
|
|
921
|
-
return {
|
|
922
|
-
|
|
923
|
-
|
|
991
|
+
return { errors: this.errors };
|
|
992
|
+
}
|
|
993
|
+
for (const rootTypeName of string_constants_1.ROOT_TYPES) {
|
|
994
|
+
const rootTypeContainer = this.parents.get(rootTypeName);
|
|
995
|
+
if (!rootTypeContainer || rootTypeContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
// If a root type field returns a Scalar or Enum, track it so that it is not evaluated it again
|
|
999
|
+
const evaluatedNamedTypes = new Set(constants_1.BASE_SCALARS);
|
|
1000
|
+
for (const [rootTypeFieldName, rootTypeFieldContainer] of rootTypeContainer.fields) {
|
|
1001
|
+
const rootTypeFieldNamedTypeName = rootTypeFieldContainer.namedTypeName;
|
|
1002
|
+
if (evaluatedNamedTypes.has(rootTypeFieldNamedTypeName)) {
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
const childContainer = (0, utils_3.getOrThrowError)(this.parents, rootTypeFieldNamedTypeName, string_constants_1.PARENTS);
|
|
1006
|
+
const fieldPath = `${rootTypeName}.${rootTypeFieldName}`;
|
|
1007
|
+
const rootTypeFieldData = {
|
|
1008
|
+
fieldName: rootTypeFieldName,
|
|
1009
|
+
fieldTypeNodeString: (0, merge_1.printTypeNode)(rootTypeFieldContainer.node.type),
|
|
1010
|
+
path: fieldPath,
|
|
1011
|
+
typeName: rootTypeName,
|
|
1012
|
+
subgraphs: rootTypeFieldContainer.subgraphs,
|
|
1013
|
+
};
|
|
1014
|
+
const evaluatedObjectLikes = new Set();
|
|
1015
|
+
switch (childContainer.kind) {
|
|
1016
|
+
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
1017
|
+
// intentional fallthrough
|
|
1018
|
+
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
1019
|
+
// Root type fields whose response type is an Enums and Scalars will always be resolvable
|
|
1020
|
+
// Consequently, subsequent checks can be skipped
|
|
1021
|
+
evaluatedNamedTypes.add(rootTypeFieldNamedTypeName);
|
|
1022
|
+
continue;
|
|
1023
|
+
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
1024
|
+
this.evaluateResolvabilityOfObject(childContainer, rootTypeFieldData, fieldPath, evaluatedObjectLikes, this.entities.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : []);
|
|
1025
|
+
continue;
|
|
1026
|
+
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
1027
|
+
// intentional fallthrough
|
|
1028
|
+
case graphql_1.Kind.UNION_TYPE_DEFINITION:
|
|
1029
|
+
this.evaluateResolvabilityOfAbstractType(rootTypeFieldNamedTypeName, childContainer.kind, rootTypeFieldData, fieldPath, evaluatedObjectLikes, this.entities.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : [], rootTypeFieldContainer.subgraphs);
|
|
1030
|
+
continue;
|
|
1031
|
+
default:
|
|
1032
|
+
this.errors.push((0, errors_1.unexpectedObjectResponseType)(fieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
if (this.errors.length > 0) {
|
|
1037
|
+
return { errors: this.errors };
|
|
924
1038
|
}
|
|
925
1039
|
const newAst = {
|
|
926
1040
|
kind: graphql_1.Kind.DOCUMENT,
|
|
927
1041
|
definitions,
|
|
928
1042
|
};
|
|
929
1043
|
return {
|
|
930
|
-
|
|
931
|
-
|
|
1044
|
+
federationResult: {
|
|
1045
|
+
argumentConfigurations: this.argumentConfigurations,
|
|
1046
|
+
federatedGraphAST: newAst,
|
|
1047
|
+
federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
|
|
1048
|
+
}
|
|
932
1049
|
};
|
|
933
1050
|
}
|
|
934
1051
|
}
|
|
935
1052
|
exports.FederationFactory = FederationFactory;
|
|
1053
|
+
function federateSubgraphs(subgraphs) {
|
|
1054
|
+
if (subgraphs.length < 1) {
|
|
1055
|
+
throw errors_1.minimumSubgraphRequirementError;
|
|
1056
|
+
}
|
|
1057
|
+
const normalizedSubgraphs = [];
|
|
1058
|
+
const validationErrors = [];
|
|
1059
|
+
const subgraphNames = new Set();
|
|
1060
|
+
const nonUniqueSubgraphNames = new Set();
|
|
1061
|
+
const invalidNameErrorMessages = [];
|
|
1062
|
+
for (let i = 0; i < subgraphs.length; i++) {
|
|
1063
|
+
const subgraph = subgraphs[i];
|
|
1064
|
+
const name = subgraph.name || `subgraph-${i}-${Date.now()}`;
|
|
1065
|
+
if (!subgraph.name) {
|
|
1066
|
+
invalidNameErrorMessages.push((0, errors_1.invalidSubgraphNameErrorMessage)(i, name));
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
(0, subgraph_1.validateSubgraphName)(subgraph.name, subgraphNames, nonUniqueSubgraphNames);
|
|
1070
|
+
}
|
|
1071
|
+
const { errors, normalizationResult } = (0, normalization_factory_1.normalizeSubgraph)(subgraph.definitions);
|
|
1072
|
+
if (errors) {
|
|
1073
|
+
validationErrors.push((0, errors_1.subgraphValidationError)(name, errors));
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
if (!normalizationResult) {
|
|
1077
|
+
validationErrors.push((0, errors_1.subgraphValidationError)(name, [errors_1.subgraphValidationFailureErrorMessage]));
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
normalizedSubgraphs.push({
|
|
1081
|
+
definitions: normalizationResult.subgraphAST,
|
|
1082
|
+
isVersionTwo: normalizationResult.isVersionTwo,
|
|
1083
|
+
name,
|
|
1084
|
+
operationTypes: normalizationResult.operationTypes,
|
|
1085
|
+
url: subgraph.url,
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
const allErrors = [];
|
|
1089
|
+
if (invalidNameErrorMessages.length > 0 || nonUniqueSubgraphNames.size > 0) {
|
|
1090
|
+
allErrors.push((0, errors_1.invalidSubgraphNamesError)([...nonUniqueSubgraphNames], invalidNameErrorMessages));
|
|
1091
|
+
}
|
|
1092
|
+
allErrors.push(...validationErrors);
|
|
1093
|
+
if (allErrors.length > 0) {
|
|
1094
|
+
return { errors: allErrors };
|
|
1095
|
+
}
|
|
1096
|
+
const federationFactory = new FederationFactory(normalizedSubgraphs);
|
|
1097
|
+
return federationFactory.federate();
|
|
1098
|
+
}
|
|
1099
|
+
exports.federateSubgraphs = federateSubgraphs;
|
|
936
1100
|
//# sourceMappingURL=federation-factory.js.map
|