@wundergraph/composition 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 -22
- package/dist/federation/federation-factory.js +611 -429
- 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 +13 -1
- package/dist/utils/string-constants.js +15 -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,78 +1,36 @@
|
|
|
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();
|
|
75
32
|
graphEdges = new Set();
|
|
33
|
+
graphPaths = new Map();
|
|
76
34
|
inputFieldTypeNameSet = new Set();
|
|
77
35
|
isCurrentParentEntity = false;
|
|
78
36
|
isCurrentParentInterface = false;
|
|
@@ -81,10 +39,8 @@ class FederationFactory {
|
|
|
81
39
|
isParentRootType = false;
|
|
82
40
|
isParentInputObject = false;
|
|
83
41
|
outputFieldTypeNameSet = new Set();
|
|
84
|
-
|
|
85
|
-
rootTypeFieldsByResponseTypeName = new Map();
|
|
42
|
+
parents = new Map();
|
|
86
43
|
rootTypeNames = new Set([string_constants_1.DEFAULT_MUTATION, string_constants_1.DEFAULT_QUERY, string_constants_1.DEFAULT_SUBSCRIPTION]);
|
|
87
|
-
sharedRootTypeFieldDependentResponses = new Map();
|
|
88
44
|
subgraphs = [];
|
|
89
45
|
shareableErrorTypeNames = new Map();
|
|
90
46
|
constructor(subgraphs) {
|
|
@@ -95,28 +51,25 @@ class FederationFactory {
|
|
|
95
51
|
}
|
|
96
52
|
upsertEntity(node) {
|
|
97
53
|
const typeName = node.name.value;
|
|
98
|
-
const entity = this.
|
|
54
|
+
const entity = this.entities.get(typeName);
|
|
99
55
|
if (entity) {
|
|
100
|
-
(0, utils_1.extractEntityKeys)(node, entity.keys);
|
|
56
|
+
(0, utils_1.extractEntityKeys)(node, entity.keys, this.errors);
|
|
101
57
|
entity.subgraphs.add(this.currentSubgraphName);
|
|
102
58
|
return;
|
|
103
59
|
}
|
|
104
|
-
this.
|
|
60
|
+
this.entities.set(typeName, {
|
|
105
61
|
fields: new Set(),
|
|
106
|
-
keys: (0, utils_1.extractEntityKeys)(node, new Set()),
|
|
62
|
+
keys: (0, utils_1.extractEntityKeys)(node, new Set(), this.errors),
|
|
107
63
|
subgraphs: new Set([this.currentSubgraphName]),
|
|
108
64
|
});
|
|
109
65
|
}
|
|
110
66
|
populateMultiGraphAndRenameOperations(subgraphs) {
|
|
111
67
|
for (const subgraph of subgraphs) {
|
|
112
68
|
this.currentSubgraphName = subgraph.name;
|
|
113
|
-
(0, subgraph_1.
|
|
114
|
-
(0, subgraph_1.
|
|
69
|
+
(0, subgraph_1.walkSubgraphToCollectObjectLikesAndDirectiveDefinitions)(this, subgraph);
|
|
70
|
+
(0, subgraph_1.walkSubgraphToCollectFields)(this, subgraph);
|
|
115
71
|
}
|
|
116
72
|
}
|
|
117
|
-
isParentInterface(parent) {
|
|
118
|
-
return parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
|
|
119
|
-
}
|
|
120
73
|
isFieldEntityKey(parent) {
|
|
121
74
|
if (parent.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || parent.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
122
75
|
return parent.entityKeys.has(this.childName);
|
|
@@ -126,11 +79,11 @@ class FederationFactory {
|
|
|
126
79
|
getEnumMergeMethod(enumName) {
|
|
127
80
|
if (this.inputFieldTypeNameSet.has(enumName) || this.argumentTypeNameSet.has(enumName)) {
|
|
128
81
|
if (this.outputFieldTypeNameSet.has(enumName)) {
|
|
129
|
-
return
|
|
82
|
+
return utils_2.MergeMethod.CONSISTENT;
|
|
130
83
|
}
|
|
131
|
-
return
|
|
84
|
+
return utils_2.MergeMethod.INTERSECTION;
|
|
132
85
|
}
|
|
133
|
-
return
|
|
86
|
+
return utils_2.MergeMethod.UNION;
|
|
134
87
|
}
|
|
135
88
|
validateArgumentDefaultValues(argName, existingDefaultValue, newDefaultValue) {
|
|
136
89
|
if (existingDefaultValue.kind !== newDefaultValue.kind) {
|
|
@@ -148,6 +101,7 @@ class FederationFactory {
|
|
|
148
101
|
existingArg.includeDefaultValue = false;
|
|
149
102
|
return;
|
|
150
103
|
}
|
|
104
|
+
const argumentName = existingArg.node.name.value;
|
|
151
105
|
const existingDefaultValue = existingArg.node.defaultValue;
|
|
152
106
|
switch (existingDefaultValue.kind) {
|
|
153
107
|
case graphql_1.Kind.LIST: // TODO
|
|
@@ -162,92 +116,54 @@ class FederationFactory {
|
|
|
162
116
|
case graphql_1.Kind.FLOAT:
|
|
163
117
|
case graphql_1.Kind.INT:
|
|
164
118
|
case graphql_1.Kind.STRING:
|
|
165
|
-
this.validateArgumentDefaultValues(
|
|
119
|
+
this.validateArgumentDefaultValues(argumentName, existingDefaultValue, newDefaultValue);
|
|
166
120
|
break;
|
|
167
121
|
default:
|
|
168
|
-
throw
|
|
122
|
+
throw (0, errors_1.unexpectedArgumentKindFatalError)(argumentName, this.childName);
|
|
169
123
|
}
|
|
170
124
|
}
|
|
171
|
-
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
for (const arg of node.arguments) {
|
|
176
|
-
const argName = arg.name.value;
|
|
177
|
-
const argPath = `${node.name.value}(${argName}...)`;
|
|
178
|
-
this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
|
|
179
|
-
const existingArg = existingFieldNode.arguments.get(argName);
|
|
180
|
-
if (existingArg) {
|
|
181
|
-
existingArg.appearances += 1;
|
|
182
|
-
existingArg.node.description = existingArg.node.description || arg.description;
|
|
183
|
-
const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArg.node.type, arg.type, this.childName, argName);
|
|
184
|
-
if (typeNode) {
|
|
185
|
-
existingArg.node.type = typeNode;
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
if (!typeErrors || typeErrors.length < 2) {
|
|
189
|
-
throw new Error(''); // TODO this should never happen
|
|
190
|
-
}
|
|
191
|
-
this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
192
|
-
}
|
|
193
|
-
this.compareAndValidateArgumentDefaultValues(existingArg, arg);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
const newNode = (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName);
|
|
197
|
-
// TODO validation of default values
|
|
198
|
-
existingFieldNode.arguments.set(argName, {
|
|
199
|
-
appearances: 1,
|
|
200
|
-
includeDefaultValue: !!arg.defaultValue,
|
|
201
|
-
node: newNode,
|
|
202
|
-
});
|
|
125
|
+
upsertRequiredSubgraph(set, isRequired) {
|
|
126
|
+
if (isRequired) {
|
|
127
|
+
set.add(this.currentSubgraphName);
|
|
203
128
|
}
|
|
129
|
+
return set;
|
|
204
130
|
}
|
|
205
|
-
|
|
131
|
+
// TODO validation of default values
|
|
132
|
+
upsertArguments(node, argumentMap) {
|
|
206
133
|
if (!node.arguments) {
|
|
207
|
-
return;
|
|
134
|
+
return argumentMap;
|
|
208
135
|
}
|
|
209
|
-
for (const
|
|
210
|
-
const argName =
|
|
136
|
+
for (const argumentNode of node.arguments) {
|
|
137
|
+
const argName = argumentNode.name.value;
|
|
211
138
|
const argPath = `${node.name.value}(${argName}...)`;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
const concreteTypeName = node.name.value;
|
|
225
|
-
for (const iFace of node.interfaces) {
|
|
226
|
-
const interfaceName = iFace.name.value;
|
|
227
|
-
const concreteTypes = this.abstractToConcreteTypeNames.get(interfaceName);
|
|
228
|
-
if (concreteTypes) {
|
|
229
|
-
concreteTypes.add(concreteTypeName);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
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;
|
|
233
150
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const unionName = node.name.value;
|
|
241
|
-
for (const member of node.types) {
|
|
242
|
-
const memberName = member.name.value;
|
|
243
|
-
const concreteTypes = this.abstractToConcreteTypeNames.get(memberName);
|
|
244
|
-
if (concreteTypes) {
|
|
245
|
-
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;
|
|
246
157
|
}
|
|
247
158
|
else {
|
|
248
|
-
|
|
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]));
|
|
249
163
|
}
|
|
164
|
+
this.compareAndValidateArgumentDefaultValues(existingArgumentContainer, argumentNode);
|
|
250
165
|
}
|
|
166
|
+
return argumentMap;
|
|
251
167
|
}
|
|
252
168
|
isFieldShareable(node, parent) {
|
|
253
169
|
return (!this.isCurrentSubgraphVersionTwo ||
|
|
@@ -255,112 +171,66 @@ class FederationFactory {
|
|
|
255
171
|
(0, utils_1.isNodeShareable)(node) ||
|
|
256
172
|
(this.isCurrentParentEntity && this.isFieldEntityKey(parent)));
|
|
257
173
|
}
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (responseTypeName === this.parentTypeName) {
|
|
264
|
-
paths.push([this.parentTypeName]);
|
|
265
|
-
}
|
|
266
|
-
else if (paths.length < 1) {
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
// Construct all possible paths to the unresolvable field but with the fieldName relationship between nodes
|
|
270
|
-
const partialResolverPaths = [];
|
|
271
|
-
for (const path of paths) {
|
|
272
|
-
let hasEntityAncestor = false;
|
|
273
|
-
let resolverPath = '';
|
|
274
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
275
|
-
const pathParent = path[i];
|
|
276
|
-
// The field in question is resolvable if it has an entity ancestor within the same subgraph
|
|
277
|
-
// Unresolvable fields further up the chain will be handled elsewhere
|
|
278
|
-
const entity = this.entityMap.get(pathParent);
|
|
279
|
-
if (entity && entity.subgraphs.has(this.currentSubgraphName)) {
|
|
280
|
-
hasEntityAncestor = true;
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
const edges = this.graph.edges(pathParent, path[i + 1]);
|
|
284
|
-
// If there are multiple edges, pick the first one
|
|
285
|
-
const inlineFragment = this.graph.getEdgeAttribute(edges[0], string_constants_1.INLINE_FRAGMENT);
|
|
286
|
-
const edgeName = this.graph.getEdgeAttribute(edges[0], string_constants_1.FIELD_NAME);
|
|
287
|
-
// If the parent field is an abstract type, the child should be proceeded by an inline fragment
|
|
288
|
-
resolverPath += edgeName + (inlineFragment || '.');
|
|
289
|
-
}
|
|
290
|
-
if (hasEntityAncestor) {
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
293
|
-
// Add the unresolvable field to each path
|
|
294
|
-
resolverPath += fieldName;
|
|
295
|
-
// If the field could have fields itself, add ellipsis
|
|
296
|
-
if (this.graph.hasNode(fieldContainer.rootTypeName)) {
|
|
297
|
-
resolverPath += ' { ... }';
|
|
298
|
-
}
|
|
299
|
-
partialResolverPaths.push(resolverPath);
|
|
300
|
-
}
|
|
301
|
-
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)) {
|
|
302
179
|
return;
|
|
303
180
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
const fullResolverPaths = [];
|
|
311
|
-
// The field is still resolvable if it's defined and resolved in another graph (but that isn't yet known)
|
|
312
|
-
// Consequently, the subgraphs must be compared later to determine that the field is always resolvable
|
|
313
|
-
for (const partialResolverPath of partialResolverPaths) {
|
|
314
|
-
fullResolverPaths.push(`${operationFieldPath}${operationField.inlineFragment}${partialResolverPath}`);
|
|
315
|
-
}
|
|
316
|
-
const potentiallyUnresolvableField = {
|
|
317
|
-
fieldContainer,
|
|
318
|
-
fullResolverPaths,
|
|
319
|
-
rootTypeField: operationField,
|
|
320
|
-
};
|
|
321
|
-
// The parent might already have unresolvable fields that have already been added
|
|
322
|
-
const dependentResponsesByFieldName = this.sharedRootTypeFieldDependentResponses.get(this.parentTypeName);
|
|
323
|
-
if (dependentResponsesByFieldName) {
|
|
324
|
-
dependentResponsesByFieldName.push(potentiallyUnresolvableField);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
this.sharedRootTypeFieldDependentResponses.set(this.parentTypeName, [potentiallyUnresolvableField]);
|
|
181
|
+
if ((0, utils_1.mergeExecutableDirectiveLocations)(node.locations, directiveDefinition).size < 1) {
|
|
182
|
+
this.executableDirectives.delete(directiveName);
|
|
183
|
+
return;
|
|
328
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);
|
|
329
200
|
}
|
|
330
201
|
}
|
|
331
202
|
upsertFieldNode(node) {
|
|
332
203
|
const parent = this.isCurrentParentExtensionType
|
|
333
|
-
? (0,
|
|
334
|
-
: (0,
|
|
335
|
-
if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION
|
|
336
|
-
parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION
|
|
337
|
-
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) {
|
|
338
209
|
throw (0, errors_1.unexpectedKindFatalError)(this.parentTypeName);
|
|
339
210
|
}
|
|
340
211
|
const fieldMap = parent.fields;
|
|
341
212
|
const isFieldShareable = this.isFieldShareable(node, parent);
|
|
342
213
|
const fieldPath = `${this.parentTypeName}.${this.childName}`;
|
|
343
214
|
const fieldRootTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
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);
|
|
352
222
|
if (typeNode) {
|
|
353
|
-
|
|
223
|
+
existingFieldContainer.node.type = typeNode;
|
|
354
224
|
}
|
|
355
225
|
else {
|
|
356
226
|
if (!typeErrors || typeErrors.length < 2) {
|
|
357
|
-
throw
|
|
227
|
+
throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
|
|
358
228
|
}
|
|
359
229
|
this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
360
230
|
}
|
|
361
|
-
this.
|
|
231
|
+
this.upsertArguments(node, existingFieldContainer.arguments);
|
|
362
232
|
// If the parent is not an interface and both fields are not shareable, is it is a shareable error
|
|
363
|
-
if (!this.isCurrentParentInterface && (!
|
|
233
|
+
if (!this.isCurrentParentInterface && (!existingFieldContainer.isShareable || !isFieldShareable)) {
|
|
364
234
|
const shareableErrorTypeNames = this.shareableErrorTypeNames.get(this.parentTypeName);
|
|
365
235
|
if (shareableErrorTypeNames) {
|
|
366
236
|
shareableErrorTypeNames.add(this.childName);
|
|
@@ -371,27 +241,22 @@ class FederationFactory {
|
|
|
371
241
|
}
|
|
372
242
|
return;
|
|
373
243
|
}
|
|
374
|
-
const args = new Map();
|
|
375
|
-
this.extractArgumentsFromFieldNode(node, args);
|
|
376
244
|
this.outputFieldTypeNameSet.add(fieldRootTypeName);
|
|
377
245
|
fieldMap.set(this.childName, {
|
|
378
|
-
|
|
379
|
-
|
|
246
|
+
arguments: this.upsertArguments(node, new Map()),
|
|
247
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
248
|
+
directives: new Map(),
|
|
249
|
+
tags: new Map(),
|
|
250
|
+
}),
|
|
380
251
|
isShareable: isFieldShareable,
|
|
381
252
|
node: (0, ast_1.fieldDefinitionNodeToMutable)(node, this.parentTypeName),
|
|
382
|
-
|
|
253
|
+
namedTypeName: fieldRootTypeName,
|
|
383
254
|
subgraphs: new Set([this.currentSubgraphName]),
|
|
384
255
|
subgraphsByShareable: new Map([[this.currentSubgraphName, isFieldShareable]]),
|
|
385
256
|
});
|
|
386
|
-
if (this.isParentRootType ||
|
|
387
|
-
entityParent?.fields.has(this.childName) ||
|
|
388
|
-
parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
this.addPotentiallyUnresolvableField(parent, this.childName);
|
|
392
257
|
}
|
|
393
258
|
upsertValueNode(node) {
|
|
394
|
-
const parent = this.
|
|
259
|
+
const parent = this.parents.get(this.parentTypeName);
|
|
395
260
|
switch (node.kind) {
|
|
396
261
|
case graphql_1.Kind.ENUM_VALUE_DEFINITION:
|
|
397
262
|
if (!parent) {
|
|
@@ -402,37 +267,43 @@ class FederationFactory {
|
|
|
402
267
|
throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.ENUM_TYPE_DEFINITION, parent.kind);
|
|
403
268
|
}
|
|
404
269
|
const enumValues = parent.values;
|
|
405
|
-
const
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
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;
|
|
409
275
|
return;
|
|
410
276
|
}
|
|
411
277
|
enumValues.set(this.childName, {
|
|
412
278
|
appearances: 1,
|
|
279
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
280
|
+
directives: new Map(),
|
|
281
|
+
tags: new Map(),
|
|
282
|
+
}),
|
|
413
283
|
node: (0, ast_1.enumValueDefinitionNodeToMutable)(node),
|
|
414
284
|
});
|
|
415
285
|
return;
|
|
416
286
|
case graphql_1.Kind.INPUT_VALUE_DEFINITION:
|
|
417
287
|
if (!parent || !this.isParentInputObject) {
|
|
418
|
-
//
|
|
288
|
+
// these are arguments to a directive
|
|
419
289
|
return;
|
|
420
290
|
}
|
|
421
291
|
if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
|
|
422
292
|
throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, parent.kind);
|
|
423
293
|
}
|
|
424
294
|
const inputValues = parent.fields;
|
|
425
|
-
const
|
|
426
|
-
if (
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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);
|
|
430
301
|
if (typeNode) {
|
|
431
|
-
|
|
302
|
+
inputValueContainer.node.type = typeNode;
|
|
432
303
|
}
|
|
433
304
|
else {
|
|
434
305
|
if (!typeErrors || typeErrors.length < 2) {
|
|
435
|
-
throw
|
|
306
|
+
throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
|
|
436
307
|
}
|
|
437
308
|
this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
|
|
438
309
|
}
|
|
@@ -443,6 +314,10 @@ class FederationFactory {
|
|
|
443
314
|
this.inputFieldTypeNameSet.add(inputValueNamedType);
|
|
444
315
|
inputValues.set(this.childName, {
|
|
445
316
|
appearances: 1,
|
|
317
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
318
|
+
directives: new Map(),
|
|
319
|
+
tags: new Map(),
|
|
320
|
+
}),
|
|
446
321
|
includeDefaultValue: !!node.defaultValue,
|
|
447
322
|
node: (0, ast_1.inputValueDefinitionNodeToMutable)(node, this.parentTypeName),
|
|
448
323
|
});
|
|
@@ -453,10 +328,10 @@ class FederationFactory {
|
|
|
453
328
|
}
|
|
454
329
|
upsertParentNode(node) {
|
|
455
330
|
const parentTypeName = node.name.value;
|
|
456
|
-
const parent = this.
|
|
331
|
+
const parent = this.parents.get(parentTypeName);
|
|
457
332
|
if (parent) {
|
|
458
|
-
|
|
459
|
-
|
|
333
|
+
(0, utils_1.setLongestDescriptionForNode)(parent.node, node.description);
|
|
334
|
+
this.extractPersistedDirectives(node.directives || [], parent.directives);
|
|
460
335
|
}
|
|
461
336
|
switch (node.kind) {
|
|
462
337
|
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
@@ -464,10 +339,15 @@ class FederationFactory {
|
|
|
464
339
|
if (parent.kind !== node.kind) {
|
|
465
340
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
|
|
466
341
|
}
|
|
342
|
+
parent.appearances += 1;
|
|
467
343
|
return;
|
|
468
344
|
}
|
|
469
|
-
this.
|
|
345
|
+
this.parents.set(parentTypeName, {
|
|
470
346
|
appearances: 1,
|
|
347
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
348
|
+
directives: new Map(),
|
|
349
|
+
tags: new Map(),
|
|
350
|
+
}),
|
|
471
351
|
values: new Map(),
|
|
472
352
|
kind: node.kind,
|
|
473
353
|
node: (0, ast_1.enumTypeDefinitionNodeToMutable)(node),
|
|
@@ -478,10 +358,15 @@ class FederationFactory {
|
|
|
478
358
|
if (parent.kind !== node.kind) {
|
|
479
359
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
|
|
480
360
|
}
|
|
361
|
+
parent.appearances += 1;
|
|
481
362
|
return;
|
|
482
363
|
}
|
|
483
|
-
this.
|
|
364
|
+
this.parents.set(parentTypeName, {
|
|
484
365
|
appearances: 1,
|
|
366
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
367
|
+
directives: new Map(),
|
|
368
|
+
tags: new Map(),
|
|
369
|
+
}),
|
|
485
370
|
fields: new Map(),
|
|
486
371
|
kind: node.kind,
|
|
487
372
|
node: (0, ast_1.inputObjectTypeDefinitionNodeToMutable)(node),
|
|
@@ -498,8 +383,11 @@ class FederationFactory {
|
|
|
498
383
|
}
|
|
499
384
|
const nestedInterfaces = new Set();
|
|
500
385
|
(0, utils_1.extractInterfaces)(node, nestedInterfaces);
|
|
501
|
-
this.
|
|
502
|
-
|
|
386
|
+
this.parents.set(parentTypeName, {
|
|
387
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
388
|
+
directives: new Map(),
|
|
389
|
+
tags: new Map(),
|
|
390
|
+
}),
|
|
503
391
|
fields: new Map(),
|
|
504
392
|
interfaces: nestedInterfaces,
|
|
505
393
|
kind: node.kind,
|
|
@@ -514,8 +402,11 @@ class FederationFactory {
|
|
|
514
402
|
}
|
|
515
403
|
return;
|
|
516
404
|
}
|
|
517
|
-
this.
|
|
518
|
-
|
|
405
|
+
this.parents.set(parentTypeName, {
|
|
406
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
407
|
+
directives: new Map(),
|
|
408
|
+
tags: new Map(),
|
|
409
|
+
}),
|
|
519
410
|
kind: node.kind,
|
|
520
411
|
node: (0, ast_1.scalarTypeDefinitionNodeToMutable)(node),
|
|
521
412
|
});
|
|
@@ -526,16 +417,19 @@ class FederationFactory {
|
|
|
526
417
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
|
|
527
418
|
}
|
|
528
419
|
(0, utils_1.extractInterfaces)(node, parent.interfaces);
|
|
529
|
-
(0, utils_1.extractEntityKeys)(node, parent.entityKeys);
|
|
420
|
+
(0, utils_1.extractEntityKeys)(node, parent.entityKeys, this.errors);
|
|
530
421
|
parent.subgraphs.add(this.currentSubgraphName);
|
|
531
422
|
return;
|
|
532
423
|
}
|
|
533
424
|
const interfaces = new Set();
|
|
534
425
|
(0, utils_1.extractInterfaces)(node, interfaces);
|
|
535
426
|
const entityKeys = new Set();
|
|
536
|
-
(0, utils_1.extractEntityKeys)(node, entityKeys);
|
|
537
|
-
this.
|
|
538
|
-
|
|
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
|
+
}),
|
|
539
433
|
fields: new Map(),
|
|
540
434
|
entityKeys,
|
|
541
435
|
interfaces,
|
|
@@ -557,8 +451,11 @@ class FederationFactory {
|
|
|
557
451
|
node.types?.forEach((member) => parent.members.add(member.name.value));
|
|
558
452
|
return;
|
|
559
453
|
}
|
|
560
|
-
this.
|
|
561
|
-
|
|
454
|
+
this.parents.set(parentTypeName, {
|
|
455
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
456
|
+
directives: new Map(),
|
|
457
|
+
tags: new Map(),
|
|
458
|
+
}),
|
|
562
459
|
kind: node.kind,
|
|
563
460
|
members: new Set(node.types?.map((member) => member.name.value)),
|
|
564
461
|
node: (0, ast_1.unionTypeDefinitionNodeToMutable)(node),
|
|
@@ -566,98 +463,66 @@ class FederationFactory {
|
|
|
566
463
|
return;
|
|
567
464
|
}
|
|
568
465
|
}
|
|
569
|
-
upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName = fieldTypeName, hasAbstractParent = false) {
|
|
570
|
-
const operationFields = this.rootTypeFieldsByResponseTypeName.get(concreteTypeName);
|
|
571
|
-
if (!operationFields) {
|
|
572
|
-
this.rootTypeFieldsByResponseTypeName.set(concreteTypeName, new Map([
|
|
573
|
-
[
|
|
574
|
-
operationFieldPath,
|
|
575
|
-
{
|
|
576
|
-
inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
|
|
577
|
-
name: fieldName,
|
|
578
|
-
parentTypeName: this.parentTypeName,
|
|
579
|
-
path: operationFieldPath,
|
|
580
|
-
responseType,
|
|
581
|
-
rootTypeName: fieldTypeName,
|
|
582
|
-
subgraphs: new Set([this.currentSubgraphName]),
|
|
583
|
-
},
|
|
584
|
-
],
|
|
585
|
-
]));
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
const operationField = operationFields.get(operationFieldPath);
|
|
589
|
-
if (operationField) {
|
|
590
|
-
operationField.subgraphs.add(this.currentSubgraphName);
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
operationFields.set(operationFieldPath, {
|
|
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
|
-
upsertAbstractObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeNames) {
|
|
604
|
-
for (const concreteTypeName of concreteTypeNames) {
|
|
605
|
-
if (!this.graph.hasNode(concreteTypeName)) {
|
|
606
|
-
throw (0, errors_1.invalidMultiGraphNodeFatalError)(concreteTypeName); // should never happen
|
|
607
|
-
}
|
|
608
|
-
if (!this.graphEdges.has(operationFieldPath)) {
|
|
609
|
-
this.graph.addEdge(this.parentTypeName, concreteTypeName, { fieldName });
|
|
610
|
-
}
|
|
611
|
-
// Always upsert the operation field node to record subgraph appearances
|
|
612
|
-
this.upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName, true);
|
|
613
|
-
}
|
|
614
|
-
// Add the path so the edges are not added again
|
|
615
|
-
this.graphEdges.add(operationFieldPath);
|
|
616
|
-
}
|
|
617
|
-
validatePotentiallyUnresolvableFields() {
|
|
618
|
-
if (this.sharedRootTypeFieldDependentResponses.size < 1) {
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
for (const [parentTypeName, potentiallyUnresolvableFields] of this.sharedRootTypeFieldDependentResponses) {
|
|
622
|
-
for (const potentiallyUnresolvableField of potentiallyUnresolvableFields) {
|
|
623
|
-
// There is no issue if the field is resolvable from at least one subgraph
|
|
624
|
-
const operationField = potentiallyUnresolvableField.rootTypeField;
|
|
625
|
-
const fieldContainer = potentiallyUnresolvableField.fieldContainer;
|
|
626
|
-
if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
const fieldSubgraphs = [...fieldContainer.subgraphs].join('", "');
|
|
630
|
-
this.errors.push((0, errors_1.unresolvableFieldError)(operationField, fieldContainer.node.name.value, potentiallyUnresolvableField.fullResolverPaths, fieldSubgraphs, parentTypeName));
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
466
|
upsertExtensionNode(node) {
|
|
635
467
|
const extension = this.extensions.get(this.parentTypeName);
|
|
636
468
|
if (extension) {
|
|
637
469
|
if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
|
|
638
470
|
throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.OBJECT_TYPE_EXTENSION, extension.kind);
|
|
639
471
|
}
|
|
640
|
-
extension.appearances += 1;
|
|
641
472
|
extension.subgraphs.add(this.currentSubgraphName);
|
|
642
473
|
(0, utils_1.extractInterfaces)(node, extension.interfaces);
|
|
474
|
+
this.extractPersistedDirectives(node.directives || [], extension.directives);
|
|
643
475
|
return;
|
|
644
476
|
}
|
|
645
477
|
// build a new extension
|
|
646
|
-
const interfaces = new Set();
|
|
647
|
-
(0, utils_1.
|
|
648
|
-
const entityKeys = new Set();
|
|
649
|
-
(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);
|
|
650
480
|
this.extensions.set(this.parentTypeName, {
|
|
651
|
-
|
|
652
|
-
|
|
481
|
+
directives: this.extractPersistedDirectives(node.directives || [], {
|
|
482
|
+
directives: new Map(),
|
|
483
|
+
tags: new Map(),
|
|
484
|
+
}),
|
|
485
|
+
entityKeys,
|
|
486
|
+
fields: new Map(),
|
|
487
|
+
interfaces,
|
|
653
488
|
isRootType: this.isParentRootType,
|
|
654
489
|
kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
|
|
655
490
|
node: (0, ast_1.objectTypeExtensionNodeToMutable)(node),
|
|
656
|
-
|
|
657
|
-
entityKeys,
|
|
658
|
-
interfaces,
|
|
491
|
+
subgraphs: new Set([this.currentSubgraphName]),
|
|
659
492
|
});
|
|
660
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
|
+
}
|
|
661
526
|
getAndValidateImplementedInterfaces(container) {
|
|
662
527
|
const interfaces = [];
|
|
663
528
|
if (container.interfaces.size < 1) {
|
|
@@ -666,7 +531,11 @@ class FederationFactory {
|
|
|
666
531
|
const implementationErrorsMap = new Map();
|
|
667
532
|
for (const interfaceName of container.interfaces) {
|
|
668
533
|
interfaces.push((0, utils_1.stringToNamedTypeNode)(interfaceName));
|
|
669
|
-
const interfaceContainer =
|
|
534
|
+
const interfaceContainer = this.parents.get(interfaceName);
|
|
535
|
+
if (!interfaceContainer) {
|
|
536
|
+
this.errors.push((0, errors_1.undefinedTypeError)(interfaceName));
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
670
539
|
if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
|
|
671
540
|
throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
|
|
672
541
|
}
|
|
@@ -690,7 +559,7 @@ class FederationFactory {
|
|
|
690
559
|
unimplementedArguments: new Set(),
|
|
691
560
|
};
|
|
692
561
|
// The implemented field type must be equally or more restrictive than the original interface field type
|
|
693
|
-
if (!
|
|
562
|
+
if (!this.isTypeValidImplementation(interfaceField.node.type, containerField.node.type)) {
|
|
694
563
|
hasErrors = true;
|
|
695
564
|
hasNestedErrors = true;
|
|
696
565
|
invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
|
|
@@ -738,10 +607,233 @@ class FederationFactory {
|
|
|
738
607
|
}
|
|
739
608
|
}
|
|
740
609
|
if (implementationErrorsMap.size) {
|
|
741
|
-
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));
|
|
742
611
|
}
|
|
743
612
|
return interfaces;
|
|
744
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
|
+
}
|
|
745
837
|
federate() {
|
|
746
838
|
this.populateMultiGraphAndRenameOperations(this.subgraphs);
|
|
747
839
|
const factory = this;
|
|
@@ -750,16 +842,20 @@ class FederationFactory {
|
|
|
750
842
|
this.currentSubgraphName = subgraph.name;
|
|
751
843
|
(0, subgraph_1.walkSubgraphToFederate)(subgraph.definitions, factory);
|
|
752
844
|
}
|
|
753
|
-
this.validatePotentiallyUnresolvableFields();
|
|
754
845
|
const definitions = [];
|
|
755
|
-
for (const
|
|
756
|
-
|
|
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);
|
|
757
853
|
}
|
|
758
854
|
for (const [typeName, extension] of this.extensions) {
|
|
759
|
-
if (extension.isRootType && !this.
|
|
855
|
+
if (extension.isRootType && !this.parents.has(typeName)) {
|
|
760
856
|
this.upsertParentNode((0, ast_1.objectTypeExtensionNodeToMutableDefinitionNode)(extension.node));
|
|
761
857
|
}
|
|
762
|
-
const baseObject = this.
|
|
858
|
+
const baseObject = this.parents.get(typeName);
|
|
763
859
|
if (!baseObject) {
|
|
764
860
|
this.errors.push((0, errors_1.noBaseTypeExtensionError)(typeName));
|
|
765
861
|
continue;
|
|
@@ -767,152 +863,238 @@ class FederationFactory {
|
|
|
767
863
|
if (baseObject.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
768
864
|
throw (0, errors_1.incompatibleParentKindFatalError)(typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, baseObject.kind);
|
|
769
865
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
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);
|
|
775
870
|
continue;
|
|
776
871
|
}
|
|
777
|
-
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);
|
|
778
875
|
continue;
|
|
779
876
|
}
|
|
780
877
|
const parent = this.shareableErrorTypeNames.get(typeName);
|
|
781
878
|
if (parent) {
|
|
782
|
-
parent.add(
|
|
879
|
+
parent.add(extensionFieldName);
|
|
783
880
|
continue;
|
|
784
881
|
}
|
|
785
|
-
this.shareableErrorTypeNames.set(typeName, new Set([
|
|
882
|
+
this.shareableErrorTypeNames.set(typeName, new Set([extensionFieldName]));
|
|
786
883
|
}
|
|
787
884
|
for (const interfaceName of extension.interfaces) {
|
|
788
885
|
baseObject.interfaces.add(interfaceName);
|
|
789
886
|
}
|
|
790
887
|
}
|
|
791
888
|
for (const [parentTypeName, children] of this.shareableErrorTypeNames) {
|
|
792
|
-
const parent = (0,
|
|
889
|
+
const parent = (0, utils_3.getOrThrowError)(this.parents, parentTypeName, string_constants_1.PARENTS);
|
|
793
890
|
if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
794
891
|
throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, parent.kind);
|
|
795
892
|
}
|
|
796
893
|
this.errors.push((0, errors_1.shareableFieldDefinitionsError)(parent, children));
|
|
797
894
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
895
|
+
const objectLikeContainersWithInterfaces = [];
|
|
896
|
+
for (const parentContainer of this.parents.values()) {
|
|
897
|
+
const parentTypeName = parentContainer.node.name.value;
|
|
898
|
+
switch (parentContainer.kind) {
|
|
801
899
|
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
802
900
|
const values = [];
|
|
803
|
-
const mergeMethod = this.getEnumMergeMethod(
|
|
804
|
-
for (const
|
|
901
|
+
const mergeMethod = this.getEnumMergeMethod(parentTypeName);
|
|
902
|
+
for (const enumValueContainer of parentContainer.values.values()) {
|
|
903
|
+
(0, utils_1.pushPersistedDirectivesToNode)(enumValueContainer);
|
|
805
904
|
switch (mergeMethod) {
|
|
806
|
-
case
|
|
807
|
-
if (
|
|
808
|
-
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));
|
|
809
908
|
}
|
|
810
|
-
values.push(
|
|
909
|
+
values.push(enumValueContainer.node);
|
|
811
910
|
break;
|
|
812
|
-
case
|
|
813
|
-
if (
|
|
814
|
-
values.push(
|
|
911
|
+
case utils_2.MergeMethod.INTERSECTION:
|
|
912
|
+
if (enumValueContainer.appearances === parentContainer.appearances) {
|
|
913
|
+
values.push(enumValueContainer.node);
|
|
815
914
|
}
|
|
816
915
|
break;
|
|
817
916
|
default:
|
|
818
|
-
values.push(
|
|
917
|
+
values.push(enumValueContainer.node);
|
|
819
918
|
break;
|
|
820
919
|
}
|
|
821
920
|
}
|
|
822
|
-
|
|
823
|
-
definitions.push(
|
|
921
|
+
parentContainer.node.values = values;
|
|
922
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
824
923
|
break;
|
|
825
924
|
case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
826
925
|
const inputValues = [];
|
|
827
|
-
for (const
|
|
828
|
-
|
|
829
|
-
|
|
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);
|
|
830
930
|
}
|
|
831
|
-
else if ((0, type_merging_1.isTypeRequired)(
|
|
832
|
-
|
|
833
|
-
|
|
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;
|
|
834
934
|
}
|
|
835
935
|
}
|
|
836
|
-
|
|
837
|
-
definitions.push(
|
|
936
|
+
parentContainer.node.fields = inputValues;
|
|
937
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
838
938
|
break;
|
|
839
939
|
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
|
|
840
940
|
const interfaceFields = [];
|
|
841
|
-
for (const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
}
|
|
850
|
-
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);
|
|
851
949
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
otherInterfaces.push({
|
|
855
|
-
kind: graphql_1.Kind.NAMED_TYPE,
|
|
856
|
-
name: {
|
|
857
|
-
kind: graphql_1.Kind.NAME,
|
|
858
|
-
value: iFace,
|
|
859
|
-
},
|
|
860
|
-
});
|
|
950
|
+
else {
|
|
951
|
+
definitions.push(parentContainer.node);
|
|
861
952
|
}
|
|
862
|
-
parent.node.interfaces = otherInterfaces;
|
|
863
|
-
parent.node.fields = interfaceFields;
|
|
864
|
-
definitions.push(parent.node);
|
|
865
953
|
break;
|
|
866
954
|
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
|
|
867
955
|
const fields = [];
|
|
868
|
-
for (const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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);
|
|
878
967
|
}
|
|
879
|
-
parent.node.fields = fields;
|
|
880
|
-
parent.node.interfaces = this.getAndValidateImplementedInterfaces(parent);
|
|
881
|
-
definitions.push(parent.node);
|
|
882
968
|
break;
|
|
883
969
|
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
|
|
884
|
-
definitions.push(
|
|
970
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
885
971
|
break;
|
|
886
972
|
case graphql_1.Kind.UNION_TYPE_DEFINITION:
|
|
887
973
|
const types = [];
|
|
888
|
-
for (const
|
|
889
|
-
types.push(
|
|
890
|
-
kind: graphql_1.Kind.NAMED_TYPE,
|
|
891
|
-
name: {
|
|
892
|
-
kind: graphql_1.Kind.NAME,
|
|
893
|
-
value: member,
|
|
894
|
-
},
|
|
895
|
-
});
|
|
974
|
+
for (const memberName of parentContainer.members) {
|
|
975
|
+
types.push((0, utils_1.stringToNamedTypeNode)(memberName));
|
|
896
976
|
}
|
|
897
|
-
|
|
898
|
-
definitions.push(
|
|
977
|
+
parentContainer.node.types = types;
|
|
978
|
+
definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
|
|
899
979
|
break;
|
|
900
980
|
}
|
|
901
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
|
|
902
990
|
if (this.errors.length > 0) {
|
|
903
|
-
return {
|
|
904
|
-
|
|
905
|
-
|
|
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 };
|
|
906
1038
|
}
|
|
907
1039
|
const newAst = {
|
|
908
1040
|
kind: graphql_1.Kind.DOCUMENT,
|
|
909
1041
|
definitions,
|
|
910
1042
|
};
|
|
911
1043
|
return {
|
|
912
|
-
|
|
913
|
-
|
|
1044
|
+
federationResult: {
|
|
1045
|
+
argumentConfigurations: this.argumentConfigurations,
|
|
1046
|
+
federatedGraphAST: newAst,
|
|
1047
|
+
federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
|
|
1048
|
+
}
|
|
914
1049
|
};
|
|
915
1050
|
}
|
|
916
1051
|
}
|
|
917
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;
|
|
918
1100
|
//# sourceMappingURL=federation-factory.js.map
|