@graphql-tools/stitching-directives 3.0.0-alpha-f6c67111.0 → 3.0.0-alpha-20230517123108-df347e95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/cjs/defaultStitchingDirectiveOptions.js +10 -0
- package/cjs/extractVariables.js +52 -0
- package/cjs/federationToStitchingSDL.js +114 -0
- package/cjs/getSourcePaths.js +25 -0
- package/cjs/index.js +6 -0
- package/cjs/package.json +1 -0
- package/cjs/parseMergeArgsExpr.js +69 -0
- package/cjs/pathsFromSelectionSet.js +31 -0
- package/cjs/preparseMergeArgsExpr.js +31 -0
- package/cjs/properties.js +70 -0
- package/cjs/stitchingDirectives.js +78 -0
- package/cjs/stitchingDirectivesTransformer.js +423 -0
- package/cjs/stitchingDirectivesValidator.js +120 -0
- package/cjs/types.js +0 -0
- package/esm/defaultStitchingDirectiveOptions.js +7 -0
- package/esm/extractVariables.js +48 -0
- package/esm/federationToStitchingSDL.js +110 -0
- package/esm/getSourcePaths.js +21 -0
- package/esm/index.js +3 -0
- package/esm/parseMergeArgsExpr.js +65 -0
- package/esm/pathsFromSelectionSet.js +27 -0
- package/esm/preparseMergeArgsExpr.js +27 -0
- package/esm/properties.js +63 -0
- package/esm/stitchingDirectives.js +74 -0
- package/esm/stitchingDirectivesTransformer.js +419 -0
- package/esm/stitchingDirectivesValidator.js +116 -0
- package/esm/types.js +0 -0
- package/package.json +40 -15
- package/typings/defaultStitchingDirectiveOptions.d.cts +2 -0
- package/{defaultStitchingDirectiveOptions.d.ts → typings/defaultStitchingDirectiveOptions.d.ts} +1 -1
- package/typings/extractVariables.d.cts +7 -0
- package/{extractVariables.d.ts → typings/extractVariables.d.ts} +1 -1
- package/typings/federationToStitchingSDL.d.cts +2 -0
- package/{federationToStitchingSDL.d.ts → typings/federationToStitchingSDL.d.ts} +1 -1
- package/typings/getSourcePaths.d.cts +3 -0
- package/{getSourcePaths.d.ts → typings/getSourcePaths.d.ts} +1 -1
- package/typings/index.d.cts +3 -0
- package/typings/index.d.ts +3 -0
- package/typings/parseMergeArgsExpr.d.cts +3 -0
- package/{parseMergeArgsExpr.d.ts → typings/parseMergeArgsExpr.d.ts} +1 -1
- package/typings/pathsFromSelectionSet.d.ts +2 -0
- package/typings/preparseMergeArgsExpr.d.ts +6 -0
- package/typings/properties.d.cts +5 -0
- package/{properties.d.ts → typings/properties.d.ts} +1 -1
- package/typings/stitchingDirectives.d.cts +19 -0
- package/{stitchingDirectives.d.ts → typings/stitchingDirectives.d.ts} +1 -1
- package/typings/stitchingDirectivesTransformer.d.cts +3 -0
- package/{stitchingDirectivesTransformer.d.ts → typings/stitchingDirectivesTransformer.d.ts} +1 -1
- package/typings/stitchingDirectivesValidator.d.cts +3 -0
- package/{stitchingDirectivesValidator.d.ts → typings/stitchingDirectivesValidator.d.ts} +1 -1
- package/typings/types.d.cts +35 -0
- package/{types.d.ts → typings/types.d.ts} +3 -3
- package/index.d.ts +0 -3
- package/index.js +0 -972
- package/index.mjs +0 -967
- /package/{pathsFromSelectionSet.d.ts → typings/pathsFromSelectionSet.d.cts} +0 -0
- /package/{preparseMergeArgsExpr.d.ts → typings/preparseMergeArgsExpr.d.cts} +0 -0
package/index.js
DELETED
@@ -1,972 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
4
|
-
|
5
|
-
const graphql = require('graphql');
|
6
|
-
const utils = require('@graphql-tools/utils');
|
7
|
-
const delegate = require('@graphql-tools/delegate');
|
8
|
-
|
9
|
-
const defaultStitchingDirectiveOptions = {
|
10
|
-
keyDirectiveName: 'key',
|
11
|
-
computedDirectiveName: 'computed',
|
12
|
-
canonicalDirectiveName: 'canonical',
|
13
|
-
mergeDirectiveName: 'merge',
|
14
|
-
pathToDirectivesInExtensions: ['directives'],
|
15
|
-
};
|
16
|
-
|
17
|
-
function extractVariables(inputValue) {
|
18
|
-
const path = [];
|
19
|
-
const variablePaths = Object.create(null);
|
20
|
-
const keyPathVisitor = {
|
21
|
-
enter: (_node, key) => {
|
22
|
-
if (typeof key === 'number') {
|
23
|
-
path.push(key);
|
24
|
-
}
|
25
|
-
},
|
26
|
-
leave: (_node, key) => {
|
27
|
-
if (typeof key === 'number') {
|
28
|
-
path.pop();
|
29
|
-
}
|
30
|
-
},
|
31
|
-
};
|
32
|
-
const fieldPathVisitor = {
|
33
|
-
enter: (node) => {
|
34
|
-
path.push(node.name.value);
|
35
|
-
},
|
36
|
-
leave: () => {
|
37
|
-
path.pop();
|
38
|
-
},
|
39
|
-
};
|
40
|
-
const variableVisitor = {
|
41
|
-
enter: (node, key) => {
|
42
|
-
if (typeof key === 'number') {
|
43
|
-
variablePaths[node.name.value] = path.concat([key]);
|
44
|
-
}
|
45
|
-
else {
|
46
|
-
variablePaths[node.name.value] = path.slice();
|
47
|
-
}
|
48
|
-
return {
|
49
|
-
kind: graphql.Kind.NULL,
|
50
|
-
};
|
51
|
-
},
|
52
|
-
};
|
53
|
-
const newInputValue = graphql.visit(inputValue, {
|
54
|
-
[graphql.Kind.OBJECT]: keyPathVisitor,
|
55
|
-
[graphql.Kind.LIST]: keyPathVisitor,
|
56
|
-
[graphql.Kind.OBJECT_FIELD]: fieldPathVisitor,
|
57
|
-
[graphql.Kind.VARIABLE]: variableVisitor,
|
58
|
-
});
|
59
|
-
return {
|
60
|
-
inputValue: newInputValue,
|
61
|
-
variablePaths,
|
62
|
-
};
|
63
|
-
}
|
64
|
-
|
65
|
-
const KEY_DELIMITER = '__dot__';
|
66
|
-
const EXPANSION_PREFIX = '__exp';
|
67
|
-
function preparseMergeArgsExpr(mergeArgsExpr) {
|
68
|
-
const variableRegex = /\$[_A-Za-z][_A-Za-z0-9.]*/g;
|
69
|
-
const dotRegex = /\./g;
|
70
|
-
mergeArgsExpr = mergeArgsExpr.replace(variableRegex, variable => variable.replace(dotRegex, KEY_DELIMITER));
|
71
|
-
const segments = mergeArgsExpr.split('[[');
|
72
|
-
const expansionExpressions = Object.create(null);
|
73
|
-
if (segments.length === 1) {
|
74
|
-
return { mergeArgsExpr: mergeArgsExpr, expansionExpressions };
|
75
|
-
}
|
76
|
-
let finalSegments = [segments[0]];
|
77
|
-
for (let i = 1; i < segments.length; i++) {
|
78
|
-
const additionalSegments = segments[i].split(']]');
|
79
|
-
if (additionalSegments.length !== 2) {
|
80
|
-
throw new Error(`Each opening "[[" must be matched by a closing "]]" without nesting.`);
|
81
|
-
}
|
82
|
-
finalSegments = finalSegments.concat(additionalSegments);
|
83
|
-
}
|
84
|
-
let finalMergeArgsExpr = finalSegments[0];
|
85
|
-
for (let i = 1; i < finalSegments.length - 1; i += 2) {
|
86
|
-
const variableName = `${EXPANSION_PREFIX}${(i - 1) / 2 + 1}`;
|
87
|
-
expansionExpressions[variableName] = finalSegments[i];
|
88
|
-
finalMergeArgsExpr += `\$${variableName}${finalSegments[i + 1]}`;
|
89
|
-
}
|
90
|
-
return { mergeArgsExpr: finalMergeArgsExpr, expansionExpressions };
|
91
|
-
}
|
92
|
-
|
93
|
-
function addProperty(object, path, value) {
|
94
|
-
const initialSegment = path[0];
|
95
|
-
if (path.length === 1) {
|
96
|
-
object[initialSegment] = value;
|
97
|
-
return;
|
98
|
-
}
|
99
|
-
let field = object[initialSegment];
|
100
|
-
if (field != null) {
|
101
|
-
addProperty(field, path.slice(1), value);
|
102
|
-
return;
|
103
|
-
}
|
104
|
-
if (typeof path[1] === 'string') {
|
105
|
-
field = Object.create(null);
|
106
|
-
}
|
107
|
-
else {
|
108
|
-
field = [];
|
109
|
-
}
|
110
|
-
addProperty(field, path.slice(1), value);
|
111
|
-
object[initialSegment] = field;
|
112
|
-
}
|
113
|
-
function getProperty(object, path) {
|
114
|
-
if (!path.length || object == null) {
|
115
|
-
return object;
|
116
|
-
}
|
117
|
-
const newPath = path.slice();
|
118
|
-
const key = newPath.shift();
|
119
|
-
if (key == null) {
|
120
|
-
return;
|
121
|
-
}
|
122
|
-
const prop = object[key];
|
123
|
-
return getProperty(prop, newPath);
|
124
|
-
}
|
125
|
-
function getProperties(object, propertyTree) {
|
126
|
-
if (object == null) {
|
127
|
-
return object;
|
128
|
-
}
|
129
|
-
const newObject = Object.create(null);
|
130
|
-
for (const key in propertyTree) {
|
131
|
-
const subKey = propertyTree[key];
|
132
|
-
if (subKey == null) {
|
133
|
-
newObject[key] = object[key];
|
134
|
-
continue;
|
135
|
-
}
|
136
|
-
const prop = object[key];
|
137
|
-
newObject[key] = deepMap(prop, function deepMapFn(item) {
|
138
|
-
return getProperties(item, subKey);
|
139
|
-
});
|
140
|
-
}
|
141
|
-
return newObject;
|
142
|
-
}
|
143
|
-
function propertyTreeFromPaths(paths) {
|
144
|
-
const propertyTree = Object.create(null);
|
145
|
-
for (const path of paths) {
|
146
|
-
addProperty(propertyTree, path, null);
|
147
|
-
}
|
148
|
-
return propertyTree;
|
149
|
-
}
|
150
|
-
function deepMap(arrayOrItem, fn) {
|
151
|
-
if (Array.isArray(arrayOrItem)) {
|
152
|
-
return arrayOrItem.map(nestedArrayOrItem => deepMap(nestedArrayOrItem, fn));
|
153
|
-
}
|
154
|
-
return fn(arrayOrItem);
|
155
|
-
}
|
156
|
-
|
157
|
-
function pathsFromSelectionSet(selectionSet, path = []) {
|
158
|
-
var _a;
|
159
|
-
const paths = [];
|
160
|
-
for (const selection of selectionSet.selections) {
|
161
|
-
const additions = (_a = pathsFromSelection(selection, path)) !== null && _a !== void 0 ? _a : [];
|
162
|
-
for (const addition of additions) {
|
163
|
-
paths.push(addition);
|
164
|
-
}
|
165
|
-
}
|
166
|
-
return paths;
|
167
|
-
}
|
168
|
-
function pathsFromSelection(selection, path) {
|
169
|
-
var _a, _b;
|
170
|
-
if (selection.kind === graphql.Kind.FIELD) {
|
171
|
-
const responseKey = (_b = (_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : selection.name.value;
|
172
|
-
if (selection.selectionSet) {
|
173
|
-
return pathsFromSelectionSet(selection.selectionSet, path.concat([responseKey]));
|
174
|
-
}
|
175
|
-
else {
|
176
|
-
return [path.concat([responseKey])];
|
177
|
-
}
|
178
|
-
}
|
179
|
-
else if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
180
|
-
return pathsFromSelectionSet(selection.selectionSet, path);
|
181
|
-
}
|
182
|
-
}
|
183
|
-
|
184
|
-
function getSourcePaths(mappingInstructions, selectionSet) {
|
185
|
-
const sourcePaths = [];
|
186
|
-
for (const mappingInstruction of mappingInstructions) {
|
187
|
-
const { sourcePath } = mappingInstruction;
|
188
|
-
if (sourcePath.length) {
|
189
|
-
sourcePaths.push(sourcePath);
|
190
|
-
continue;
|
191
|
-
}
|
192
|
-
if (selectionSet == null) {
|
193
|
-
continue;
|
194
|
-
}
|
195
|
-
const paths = pathsFromSelectionSet(selectionSet);
|
196
|
-
for (const path of paths) {
|
197
|
-
sourcePaths.push(path);
|
198
|
-
}
|
199
|
-
sourcePaths.push([graphql.TypeNameMetaFieldDef.name]);
|
200
|
-
}
|
201
|
-
return sourcePaths;
|
202
|
-
}
|
203
|
-
|
204
|
-
function parseMergeArgsExpr(mergeArgsExpr, selectionSet) {
|
205
|
-
const { mergeArgsExpr: newMergeArgsExpr, expansionExpressions } = preparseMergeArgsExpr(mergeArgsExpr);
|
206
|
-
const inputValue = graphql.parseValue(`{ ${newMergeArgsExpr} }`, { noLocation: true });
|
207
|
-
const { inputValue: newInputValue, variablePaths } = extractVariables(inputValue);
|
208
|
-
if (!Object.keys(expansionExpressions).length) {
|
209
|
-
if (!Object.keys(variablePaths).length) {
|
210
|
-
throw new Error('Merge arguments must declare a key.');
|
211
|
-
}
|
212
|
-
const mappingInstructions = getMappingInstructions(variablePaths);
|
213
|
-
const usedProperties = propertyTreeFromPaths(getSourcePaths(mappingInstructions, selectionSet));
|
214
|
-
return { args: graphql.valueFromASTUntyped(newInputValue), usedProperties, mappingInstructions };
|
215
|
-
}
|
216
|
-
const expansionRegEx = new RegExp(`^${EXPANSION_PREFIX}[0-9]+$`);
|
217
|
-
for (const variableName in variablePaths) {
|
218
|
-
if (!variableName.match(expansionRegEx)) {
|
219
|
-
throw new Error('Expansions cannot be mixed with single key declarations.');
|
220
|
-
}
|
221
|
-
}
|
222
|
-
const expansions = [];
|
223
|
-
const sourcePaths = [];
|
224
|
-
for (const variableName in expansionExpressions) {
|
225
|
-
const str = expansionExpressions[variableName];
|
226
|
-
const valuePath = variablePaths[variableName];
|
227
|
-
const { inputValue: expansionInputValue, variablePaths: expansionVariablePaths } = extractVariables(graphql.parseValue(`${str}`, { noLocation: true }));
|
228
|
-
if (!Object.keys(expansionVariablePaths).length) {
|
229
|
-
throw new Error('Merge arguments must declare a key.');
|
230
|
-
}
|
231
|
-
const mappingInstructions = getMappingInstructions(expansionVariablePaths);
|
232
|
-
const value = graphql.valueFromASTUntyped(expansionInputValue);
|
233
|
-
sourcePaths.push(...getSourcePaths(mappingInstructions, selectionSet));
|
234
|
-
assertNotWithinList(valuePath);
|
235
|
-
expansions.push({
|
236
|
-
valuePath,
|
237
|
-
value,
|
238
|
-
mappingInstructions,
|
239
|
-
});
|
240
|
-
}
|
241
|
-
const usedProperties = propertyTreeFromPaths(sourcePaths);
|
242
|
-
return { args: graphql.valueFromASTUntyped(newInputValue), usedProperties, expansions };
|
243
|
-
}
|
244
|
-
function getMappingInstructions(variablePaths) {
|
245
|
-
const mappingInstructions = [];
|
246
|
-
for (const keyPath in variablePaths) {
|
247
|
-
const valuePath = variablePaths[keyPath];
|
248
|
-
const splitKeyPath = keyPath.split(KEY_DELIMITER).slice(1);
|
249
|
-
assertNotWithinList(valuePath);
|
250
|
-
mappingInstructions.push({
|
251
|
-
destinationPath: valuePath,
|
252
|
-
sourcePath: splitKeyPath,
|
253
|
-
});
|
254
|
-
}
|
255
|
-
return mappingInstructions;
|
256
|
-
}
|
257
|
-
function assertNotWithinList(path) {
|
258
|
-
for (const pathSegment of path) {
|
259
|
-
if (typeof pathSegment === 'number') {
|
260
|
-
throw new Error('Insertions cannot be made into a list.');
|
261
|
-
}
|
262
|
-
}
|
263
|
-
}
|
264
|
-
|
265
|
-
const dottedNameRegEx = /^[_A-Za-z][_0-9A-Za-z]*(.[_A-Za-z][_0-9A-Za-z]*)*$/;
|
266
|
-
function stitchingDirectivesValidator(options = {}) {
|
267
|
-
const { keyDirectiveName, computedDirectiveName, mergeDirectiveName, pathToDirectivesInExtensions } = {
|
268
|
-
...defaultStitchingDirectiveOptions,
|
269
|
-
...options,
|
270
|
-
};
|
271
|
-
return (schema) => {
|
272
|
-
var _a;
|
273
|
-
const queryTypeName = (_a = schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name;
|
274
|
-
utils.mapSchema(schema, {
|
275
|
-
[utils.MapperKind.OBJECT_TYPE]: type => {
|
276
|
-
var _a;
|
277
|
-
const keyDirective = (_a = utils.getDirective(schema, type, keyDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
278
|
-
if (keyDirective != null) {
|
279
|
-
utils.parseSelectionSet(keyDirective['selectionSet']);
|
280
|
-
}
|
281
|
-
return undefined;
|
282
|
-
},
|
283
|
-
[utils.MapperKind.OBJECT_FIELD]: (fieldConfig, _fieldName, typeName) => {
|
284
|
-
var _a, _b, _c;
|
285
|
-
const computedDirective = (_a = utils.getDirective(schema, fieldConfig, computedDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
286
|
-
if (computedDirective != null) {
|
287
|
-
utils.parseSelectionSet(computedDirective['selectionSet']);
|
288
|
-
}
|
289
|
-
const mergeDirective = (_b = utils.getDirective(schema, fieldConfig, mergeDirectiveName, pathToDirectivesInExtensions)) === null || _b === void 0 ? void 0 : _b[0];
|
290
|
-
if (mergeDirective != null) {
|
291
|
-
if (typeName !== queryTypeName) {
|
292
|
-
throw new Error('@merge directive may be used only for root fields of the root Query type.');
|
293
|
-
}
|
294
|
-
let returnType = graphql.getNullableType(fieldConfig.type);
|
295
|
-
if (graphql.isListType(returnType)) {
|
296
|
-
returnType = graphql.getNullableType(returnType.ofType);
|
297
|
-
}
|
298
|
-
if (!graphql.isNamedType(returnType)) {
|
299
|
-
throw new Error('@merge directive must be used on a field that returns an object or a list of objects.');
|
300
|
-
}
|
301
|
-
const mergeArgsExpr = mergeDirective['argsExpr'];
|
302
|
-
if (mergeArgsExpr != null) {
|
303
|
-
parseMergeArgsExpr(mergeArgsExpr);
|
304
|
-
}
|
305
|
-
const args = Object.keys((_c = fieldConfig.args) !== null && _c !== void 0 ? _c : {});
|
306
|
-
const keyArg = mergeDirective['keyArg'];
|
307
|
-
if (keyArg == null) {
|
308
|
-
if (!mergeArgsExpr && args.length !== 1) {
|
309
|
-
throw new Error('Cannot use @merge directive without `keyArg` argument if resolver takes more than one argument.');
|
310
|
-
}
|
311
|
-
}
|
312
|
-
else if (!keyArg.match(dottedNameRegEx)) {
|
313
|
-
throw new Error('`keyArg` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
|
314
|
-
// TODO: ideally we should check that the arg exists for the resolver
|
315
|
-
}
|
316
|
-
const keyField = mergeDirective['keyField'];
|
317
|
-
if (keyField != null && !keyField.match(dottedNameRegEx)) {
|
318
|
-
throw new Error('`keyField` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
|
319
|
-
// TODO: ideally we should check that it is part of the key
|
320
|
-
}
|
321
|
-
const key = mergeDirective['key'];
|
322
|
-
if (key != null) {
|
323
|
-
if (keyField != null) {
|
324
|
-
throw new Error('Cannot use @merge directive with both `keyField` and `key` arguments.');
|
325
|
-
}
|
326
|
-
for (const keyDef of key) {
|
327
|
-
let [aliasOrKeyPath, keyPath] = keyDef.split(':');
|
328
|
-
let aliasPath;
|
329
|
-
if (keyPath == null) {
|
330
|
-
keyPath = aliasPath = aliasOrKeyPath;
|
331
|
-
}
|
332
|
-
else {
|
333
|
-
aliasPath = aliasOrKeyPath;
|
334
|
-
}
|
335
|
-
if (keyPath != null && !keyPath.match(dottedNameRegEx)) {
|
336
|
-
throw new Error('Each partial key within the `key` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
|
337
|
-
// TODO: ideally we should check that it is part of the key
|
338
|
-
}
|
339
|
-
if (aliasPath != null && !aliasOrKeyPath.match(dottedNameRegEx)) {
|
340
|
-
throw new Error('Each alias within the `key` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
|
341
|
-
// TODO: ideally we should check that the arg exists within the resolver
|
342
|
-
}
|
343
|
-
}
|
344
|
-
}
|
345
|
-
const additionalArgs = mergeDirective['additionalArgs'];
|
346
|
-
if (additionalArgs != null) {
|
347
|
-
graphql.parseValue(`{ ${additionalArgs} }`, { noLocation: true });
|
348
|
-
}
|
349
|
-
if (mergeArgsExpr != null && (keyArg != null || additionalArgs != null)) {
|
350
|
-
throw new Error('Cannot use @merge directive with both `argsExpr` argument and any additional argument.');
|
351
|
-
}
|
352
|
-
if (!graphql.isInterfaceType(returnType) && !graphql.isUnionType(returnType) && !graphql.isObjectType(returnType)) {
|
353
|
-
throw new Error('@merge directive may be used only with resolver that return an object, interface, or union.');
|
354
|
-
}
|
355
|
-
const typeNames = mergeDirective['types'];
|
356
|
-
if (typeNames != null) {
|
357
|
-
if (!graphql.isAbstractType(returnType)) {
|
358
|
-
throw new Error('Types argument can only be used with a field that returns an abstract type.');
|
359
|
-
}
|
360
|
-
const implementingTypes = graphql.isInterfaceType(returnType)
|
361
|
-
? utils.getImplementingTypes(returnType.name, schema).map(typeName => schema.getType(typeName))
|
362
|
-
: returnType.getTypes();
|
363
|
-
const implementingTypeNames = implementingTypes.map(type => type === null || type === void 0 ? void 0 : type.name).filter(utils.isSome);
|
364
|
-
for (const typeName of typeNames) {
|
365
|
-
if (!implementingTypeNames.includes(typeName)) {
|
366
|
-
throw new Error(`Types argument can only include only type names that implement the field return type's abstract type.`);
|
367
|
-
}
|
368
|
-
}
|
369
|
-
}
|
370
|
-
}
|
371
|
-
return undefined;
|
372
|
-
},
|
373
|
-
});
|
374
|
-
return schema;
|
375
|
-
};
|
376
|
-
}
|
377
|
-
|
378
|
-
function stitchingDirectivesTransformer(options = {}) {
|
379
|
-
const { keyDirectiveName, computedDirectiveName, mergeDirectiveName, canonicalDirectiveName, pathToDirectivesInExtensions, } = {
|
380
|
-
...defaultStitchingDirectiveOptions,
|
381
|
-
...options,
|
382
|
-
};
|
383
|
-
return (subschemaConfig) => {
|
384
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
385
|
-
const newSubschemaConfig = delegate.cloneSubschemaConfig(subschemaConfig);
|
386
|
-
const selectionSetsByType = Object.create(null);
|
387
|
-
const computedFieldSelectionSets = Object.create(null);
|
388
|
-
const mergedTypesResolversInfo = Object.create(null);
|
389
|
-
const canonicalTypesInfo = Object.create(null);
|
390
|
-
const schema = subschemaConfig.schema;
|
391
|
-
// gateway should also run validation
|
392
|
-
stitchingDirectivesValidator(options)(schema);
|
393
|
-
function setCanonicalDefinition(typeName, fieldName) {
|
394
|
-
var _a;
|
395
|
-
canonicalTypesInfo[typeName] = canonicalTypesInfo[typeName] || Object.create(null);
|
396
|
-
if (fieldName) {
|
397
|
-
const fields = (_a = canonicalTypesInfo[typeName].fields) !== null && _a !== void 0 ? _a : Object.create(null);
|
398
|
-
canonicalTypesInfo[typeName].fields = fields;
|
399
|
-
fields[fieldName] = true;
|
400
|
-
}
|
401
|
-
else {
|
402
|
-
canonicalTypesInfo[typeName].canonical = true;
|
403
|
-
}
|
404
|
-
}
|
405
|
-
utils.mapSchema(schema, {
|
406
|
-
[utils.MapperKind.OBJECT_TYPE]: type => {
|
407
|
-
var _a, _b;
|
408
|
-
const keyDirective = (_a = utils.getDirective(schema, type, keyDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
409
|
-
if (keyDirective != null) {
|
410
|
-
const selectionSet = utils.parseSelectionSet(keyDirective['selectionSet'], { noLocation: true });
|
411
|
-
selectionSetsByType[type.name] = selectionSet;
|
412
|
-
}
|
413
|
-
const canonicalDirective = (_b = utils.getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _b === void 0 ? void 0 : _b[0];
|
414
|
-
if (canonicalDirective != null) {
|
415
|
-
setCanonicalDefinition(type.name);
|
416
|
-
}
|
417
|
-
return undefined;
|
418
|
-
},
|
419
|
-
[utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
|
420
|
-
var _a, _b, _c;
|
421
|
-
const computedDirective = (_a = utils.getDirective(schema, fieldConfig, computedDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
422
|
-
if (computedDirective != null) {
|
423
|
-
const selectionSet = utils.parseSelectionSet(computedDirective['selectionSet'], { noLocation: true });
|
424
|
-
if (!computedFieldSelectionSets[typeName]) {
|
425
|
-
computedFieldSelectionSets[typeName] = Object.create(null);
|
426
|
-
}
|
427
|
-
computedFieldSelectionSets[typeName][fieldName] = selectionSet;
|
428
|
-
}
|
429
|
-
const mergeDirective = (_b = utils.getDirective(schema, fieldConfig, mergeDirectiveName, pathToDirectivesInExtensions)) === null || _b === void 0 ? void 0 : _b[0];
|
430
|
-
if ((mergeDirective === null || mergeDirective === void 0 ? void 0 : mergeDirective['keyField']) != null) {
|
431
|
-
const mergeDirectiveKeyField = mergeDirective['keyField'];
|
432
|
-
const selectionSet = utils.parseSelectionSet(`{ ${mergeDirectiveKeyField}}`, { noLocation: true });
|
433
|
-
const typeNames = mergeDirective['types'];
|
434
|
-
const returnType = graphql.getNamedType(fieldConfig.type);
|
435
|
-
forEachConcreteType(schema, returnType, typeNames, typeName => {
|
436
|
-
if (typeNames == null || typeNames.includes(typeName)) {
|
437
|
-
const existingSelectionSet = selectionSetsByType[typeName];
|
438
|
-
selectionSetsByType[typeName] = existingSelectionSet
|
439
|
-
? mergeSelectionSets(existingSelectionSet, selectionSet)
|
440
|
-
: selectionSet;
|
441
|
-
}
|
442
|
-
});
|
443
|
-
}
|
444
|
-
const canonicalDirective = (_c = utils.getDirective(schema, fieldConfig, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _c === void 0 ? void 0 : _c[0];
|
445
|
-
if (canonicalDirective != null) {
|
446
|
-
setCanonicalDefinition(typeName, fieldName);
|
447
|
-
}
|
448
|
-
return undefined;
|
449
|
-
},
|
450
|
-
[utils.MapperKind.INTERFACE_TYPE]: type => {
|
451
|
-
var _a;
|
452
|
-
const canonicalDirective = (_a = utils.getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
453
|
-
if (canonicalDirective) {
|
454
|
-
setCanonicalDefinition(type.name);
|
455
|
-
}
|
456
|
-
return undefined;
|
457
|
-
},
|
458
|
-
[utils.MapperKind.INTERFACE_FIELD]: (fieldConfig, fieldName, typeName) => {
|
459
|
-
var _a;
|
460
|
-
const canonicalDirective = (_a = utils.getDirective(schema, fieldConfig, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
461
|
-
if (canonicalDirective) {
|
462
|
-
setCanonicalDefinition(typeName, fieldName);
|
463
|
-
}
|
464
|
-
return undefined;
|
465
|
-
},
|
466
|
-
[utils.MapperKind.INPUT_OBJECT_TYPE]: type => {
|
467
|
-
var _a;
|
468
|
-
const canonicalDirective = (_a = utils.getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
469
|
-
if (canonicalDirective) {
|
470
|
-
setCanonicalDefinition(type.name);
|
471
|
-
}
|
472
|
-
return undefined;
|
473
|
-
},
|
474
|
-
[utils.MapperKind.INPUT_OBJECT_FIELD]: (inputFieldConfig, fieldName, typeName) => {
|
475
|
-
var _a;
|
476
|
-
const canonicalDirective = (_a = utils.getDirective(schema, inputFieldConfig, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
477
|
-
if (canonicalDirective != null) {
|
478
|
-
setCanonicalDefinition(typeName, fieldName);
|
479
|
-
}
|
480
|
-
return undefined;
|
481
|
-
},
|
482
|
-
[utils.MapperKind.UNION_TYPE]: type => {
|
483
|
-
var _a;
|
484
|
-
const canonicalDirective = (_a = utils.getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
485
|
-
if (canonicalDirective != null) {
|
486
|
-
setCanonicalDefinition(type.name);
|
487
|
-
}
|
488
|
-
return undefined;
|
489
|
-
},
|
490
|
-
[utils.MapperKind.ENUM_TYPE]: type => {
|
491
|
-
var _a;
|
492
|
-
const canonicalDirective = (_a = utils.getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
493
|
-
if (canonicalDirective != null) {
|
494
|
-
setCanonicalDefinition(type.name);
|
495
|
-
}
|
496
|
-
return undefined;
|
497
|
-
},
|
498
|
-
[utils.MapperKind.SCALAR_TYPE]: type => {
|
499
|
-
var _a;
|
500
|
-
const canonicalDirective = (_a = utils.getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
501
|
-
if (canonicalDirective != null) {
|
502
|
-
setCanonicalDefinition(type.name);
|
503
|
-
}
|
504
|
-
return undefined;
|
505
|
-
},
|
506
|
-
});
|
507
|
-
if (subschemaConfig.merge) {
|
508
|
-
for (const typeName in subschemaConfig.merge) {
|
509
|
-
const mergedTypeConfig = subschemaConfig.merge[typeName];
|
510
|
-
if (mergedTypeConfig.selectionSet) {
|
511
|
-
const selectionSet = utils.parseSelectionSet(mergedTypeConfig.selectionSet, { noLocation: true });
|
512
|
-
if (selectionSet) {
|
513
|
-
if (selectionSetsByType[typeName]) {
|
514
|
-
selectionSetsByType[typeName] = mergeSelectionSets(selectionSetsByType[typeName], selectionSet);
|
515
|
-
}
|
516
|
-
else {
|
517
|
-
selectionSetsByType[typeName] = selectionSet;
|
518
|
-
}
|
519
|
-
}
|
520
|
-
}
|
521
|
-
if (mergedTypeConfig.fields) {
|
522
|
-
for (const fieldName in mergedTypeConfig.fields) {
|
523
|
-
const fieldConfig = mergedTypeConfig.fields[fieldName];
|
524
|
-
if (!fieldConfig.selectionSet)
|
525
|
-
continue;
|
526
|
-
const selectionSet = utils.parseSelectionSet(fieldConfig.selectionSet, { noLocation: true });
|
527
|
-
if (selectionSet) {
|
528
|
-
if ((_a = computedFieldSelectionSets[typeName]) === null || _a === void 0 ? void 0 : _a[fieldName]) {
|
529
|
-
computedFieldSelectionSets[typeName][fieldName] = mergeSelectionSets(computedFieldSelectionSets[typeName][fieldName], selectionSet);
|
530
|
-
}
|
531
|
-
else {
|
532
|
-
if (computedFieldSelectionSets[typeName] == null) {
|
533
|
-
computedFieldSelectionSets[typeName] = Object.create(null);
|
534
|
-
}
|
535
|
-
computedFieldSelectionSets[typeName][fieldName] = selectionSet;
|
536
|
-
}
|
537
|
-
}
|
538
|
-
}
|
539
|
-
}
|
540
|
-
}
|
541
|
-
}
|
542
|
-
const allSelectionSetsByType = Object.create(null);
|
543
|
-
for (const typeName in selectionSetsByType) {
|
544
|
-
allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || [];
|
545
|
-
const selectionSet = selectionSetsByType[typeName];
|
546
|
-
allSelectionSetsByType[typeName].push(selectionSet);
|
547
|
-
}
|
548
|
-
for (const typeName in computedFieldSelectionSets) {
|
549
|
-
const selectionSets = computedFieldSelectionSets[typeName];
|
550
|
-
for (const i in selectionSets) {
|
551
|
-
allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || [];
|
552
|
-
const selectionSet = selectionSets[i];
|
553
|
-
allSelectionSetsByType[typeName].push(selectionSet);
|
554
|
-
}
|
555
|
-
}
|
556
|
-
utils.mapSchema(schema, {
|
557
|
-
[utils.MapperKind.OBJECT_FIELD]: function objectFieldMapper(fieldConfig, fieldName) {
|
558
|
-
var _a, _b;
|
559
|
-
const mergeDirective = (_a = utils.getDirective(schema, fieldConfig, mergeDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
|
560
|
-
if (mergeDirective != null) {
|
561
|
-
const returnType = graphql.getNullableType(fieldConfig.type);
|
562
|
-
const returnsList = graphql.isListType(returnType);
|
563
|
-
const namedType = graphql.getNamedType(returnType);
|
564
|
-
let mergeArgsExpr = mergeDirective['argsExpr'];
|
565
|
-
if (mergeArgsExpr == null) {
|
566
|
-
const key = mergeDirective['key'];
|
567
|
-
const keyField = mergeDirective['keyField'];
|
568
|
-
const keyExpr = key != null ? buildKeyExpr(key) : keyField != null ? `$key.${keyField}` : '$key';
|
569
|
-
const keyArg = mergeDirective['keyArg'];
|
570
|
-
const argNames = keyArg == null ? [Object.keys((_b = fieldConfig.args) !== null && _b !== void 0 ? _b : {})[0]] : keyArg.split('.');
|
571
|
-
const lastArgName = argNames.pop();
|
572
|
-
mergeArgsExpr = returnsList ? `${lastArgName}: [[${keyExpr}]]` : `${lastArgName}: ${keyExpr}`;
|
573
|
-
for (const argName of argNames.reverse()) {
|
574
|
-
mergeArgsExpr = `${argName}: { ${mergeArgsExpr} }`;
|
575
|
-
}
|
576
|
-
}
|
577
|
-
const typeNames = mergeDirective['types'];
|
578
|
-
forEachConcreteTypeName(namedType, schema, typeNames, function generateResolveInfo(typeName) {
|
579
|
-
const parsedMergeArgsExpr = parseMergeArgsExpr(mergeArgsExpr, allSelectionSetsByType[typeName] == null
|
580
|
-
? undefined
|
581
|
-
: mergeSelectionSets(...allSelectionSetsByType[typeName]));
|
582
|
-
const additionalArgs = mergeDirective['additionalArgs'];
|
583
|
-
if (additionalArgs != null) {
|
584
|
-
parsedMergeArgsExpr.args = utils.mergeDeep([
|
585
|
-
parsedMergeArgsExpr.args,
|
586
|
-
graphql.valueFromASTUntyped(graphql.parseValue(`{ ${additionalArgs} }`, { noLocation: true })),
|
587
|
-
]);
|
588
|
-
}
|
589
|
-
mergedTypesResolversInfo[typeName] = {
|
590
|
-
fieldName,
|
591
|
-
returnsList,
|
592
|
-
...parsedMergeArgsExpr,
|
593
|
-
};
|
594
|
-
});
|
595
|
-
}
|
596
|
-
return undefined;
|
597
|
-
},
|
598
|
-
});
|
599
|
-
for (const typeName in selectionSetsByType) {
|
600
|
-
const selectionSet = selectionSetsByType[typeName];
|
601
|
-
const mergeConfig = (_b = newSubschemaConfig.merge) !== null && _b !== void 0 ? _b : Object.create(null);
|
602
|
-
newSubschemaConfig.merge = mergeConfig;
|
603
|
-
if (mergeConfig[typeName] == null) {
|
604
|
-
newSubschemaConfig.merge[typeName] = Object.create(null);
|
605
|
-
}
|
606
|
-
const mergeTypeConfig = mergeConfig[typeName];
|
607
|
-
mergeTypeConfig.selectionSet = graphql.print(selectionSet);
|
608
|
-
}
|
609
|
-
for (const typeName in computedFieldSelectionSets) {
|
610
|
-
const selectionSets = computedFieldSelectionSets[typeName];
|
611
|
-
const mergeConfig = (_c = newSubschemaConfig.merge) !== null && _c !== void 0 ? _c : Object.create(null);
|
612
|
-
newSubschemaConfig.merge = mergeConfig;
|
613
|
-
if (mergeConfig[typeName] == null) {
|
614
|
-
mergeConfig[typeName] = Object.create(null);
|
615
|
-
}
|
616
|
-
const mergeTypeConfig = newSubschemaConfig.merge[typeName];
|
617
|
-
const mergeTypeConfigFields = (_d = mergeTypeConfig.fields) !== null && _d !== void 0 ? _d : Object.create(null);
|
618
|
-
mergeTypeConfig.fields = mergeTypeConfigFields;
|
619
|
-
for (const fieldName in selectionSets) {
|
620
|
-
const selectionSet = selectionSets[fieldName];
|
621
|
-
const fieldConfig = (_e = mergeTypeConfigFields[fieldName]) !== null && _e !== void 0 ? _e : Object.create(null);
|
622
|
-
mergeTypeConfigFields[fieldName] = fieldConfig;
|
623
|
-
fieldConfig.selectionSet = graphql.print(selectionSet);
|
624
|
-
fieldConfig.computed = true;
|
625
|
-
}
|
626
|
-
}
|
627
|
-
for (const typeName in mergedTypesResolversInfo) {
|
628
|
-
const mergedTypeResolverInfo = mergedTypesResolversInfo[typeName];
|
629
|
-
const mergeConfig = (_f = newSubschemaConfig.merge) !== null && _f !== void 0 ? _f : Object.create(null);
|
630
|
-
newSubschemaConfig.merge = mergeConfig;
|
631
|
-
if (newSubschemaConfig.merge[typeName] == null) {
|
632
|
-
newSubschemaConfig.merge[typeName] = Object.create(null);
|
633
|
-
}
|
634
|
-
const mergeTypeConfig = newSubschemaConfig.merge[typeName];
|
635
|
-
mergeTypeConfig.fieldName = mergedTypeResolverInfo.fieldName;
|
636
|
-
if (mergedTypeResolverInfo.returnsList) {
|
637
|
-
mergeTypeConfig.key = generateKeyFn(mergedTypeResolverInfo);
|
638
|
-
mergeTypeConfig.argsFromKeys = generateArgsFromKeysFn(mergedTypeResolverInfo);
|
639
|
-
}
|
640
|
-
else {
|
641
|
-
mergeTypeConfig.args = generateArgsFn(mergedTypeResolverInfo);
|
642
|
-
}
|
643
|
-
}
|
644
|
-
for (const typeName in canonicalTypesInfo) {
|
645
|
-
const canonicalTypeInfo = canonicalTypesInfo[typeName];
|
646
|
-
const mergeConfig = (_g = newSubschemaConfig.merge) !== null && _g !== void 0 ? _g : Object.create(null);
|
647
|
-
newSubschemaConfig.merge = mergeConfig;
|
648
|
-
if (newSubschemaConfig.merge[typeName] == null) {
|
649
|
-
newSubschemaConfig.merge[typeName] = Object.create(null);
|
650
|
-
}
|
651
|
-
const mergeTypeConfig = newSubschemaConfig.merge[typeName];
|
652
|
-
if (canonicalTypeInfo.canonical) {
|
653
|
-
mergeTypeConfig.canonical = true;
|
654
|
-
}
|
655
|
-
if (canonicalTypeInfo.fields) {
|
656
|
-
const mergeTypeConfigFields = (_h = mergeTypeConfig.fields) !== null && _h !== void 0 ? _h : Object.create(null);
|
657
|
-
mergeTypeConfig.fields = mergeTypeConfigFields;
|
658
|
-
for (const fieldName in canonicalTypeInfo.fields) {
|
659
|
-
if (mergeTypeConfigFields[fieldName] == null) {
|
660
|
-
mergeTypeConfigFields[fieldName] = Object.create(null);
|
661
|
-
}
|
662
|
-
mergeTypeConfigFields[fieldName].canonical = true;
|
663
|
-
}
|
664
|
-
}
|
665
|
-
}
|
666
|
-
return newSubschemaConfig;
|
667
|
-
};
|
668
|
-
}
|
669
|
-
function forEachConcreteType(schema, type, typeNames, fn) {
|
670
|
-
if (graphql.isInterfaceType(type)) {
|
671
|
-
for (const typeName of utils.getImplementingTypes(type.name, schema)) {
|
672
|
-
if (typeNames == null || typeNames.includes(typeName)) {
|
673
|
-
fn(typeName);
|
674
|
-
}
|
675
|
-
}
|
676
|
-
}
|
677
|
-
else if (graphql.isUnionType(type)) {
|
678
|
-
for (const { name: typeName } of type.getTypes()) {
|
679
|
-
if (typeNames == null || typeNames.includes(typeName)) {
|
680
|
-
fn(typeName);
|
681
|
-
}
|
682
|
-
}
|
683
|
-
}
|
684
|
-
else if (graphql.isObjectType(type)) {
|
685
|
-
fn(type.name);
|
686
|
-
}
|
687
|
-
}
|
688
|
-
function generateKeyFn(mergedTypeResolverInfo) {
|
689
|
-
return function keyFn(originalResult) {
|
690
|
-
return getProperties(originalResult, mergedTypeResolverInfo.usedProperties);
|
691
|
-
};
|
692
|
-
}
|
693
|
-
function generateArgsFromKeysFn(mergedTypeResolverInfo) {
|
694
|
-
const { expansions, args } = mergedTypeResolverInfo;
|
695
|
-
return function generateArgsFromKeys(keys) {
|
696
|
-
const newArgs = utils.mergeDeep([{}, args]);
|
697
|
-
if (expansions) {
|
698
|
-
for (const expansion of expansions) {
|
699
|
-
const mappingInstructions = expansion.mappingInstructions;
|
700
|
-
const expanded = [];
|
701
|
-
for (const key of keys) {
|
702
|
-
let newValue = utils.mergeDeep([{}, expansion.valuePath]);
|
703
|
-
for (const { destinationPath, sourcePath } of mappingInstructions) {
|
704
|
-
if (destinationPath.length) {
|
705
|
-
addProperty(newValue, destinationPath, getProperty(key, sourcePath));
|
706
|
-
}
|
707
|
-
else {
|
708
|
-
newValue = getProperty(key, sourcePath);
|
709
|
-
}
|
710
|
-
}
|
711
|
-
expanded.push(newValue);
|
712
|
-
}
|
713
|
-
addProperty(newArgs, expansion.valuePath, expanded);
|
714
|
-
}
|
715
|
-
}
|
716
|
-
return newArgs;
|
717
|
-
};
|
718
|
-
}
|
719
|
-
function generateArgsFn(mergedTypeResolverInfo) {
|
720
|
-
const { mappingInstructions, args, usedProperties } = mergedTypeResolverInfo;
|
721
|
-
return function generateArgs(originalResult) {
|
722
|
-
const newArgs = utils.mergeDeep([{}, args]);
|
723
|
-
const filteredResult = getProperties(originalResult, usedProperties);
|
724
|
-
if (mappingInstructions) {
|
725
|
-
for (const mappingInstruction of mappingInstructions) {
|
726
|
-
const { destinationPath, sourcePath } = mappingInstruction;
|
727
|
-
addProperty(newArgs, destinationPath, getProperty(filteredResult, sourcePath));
|
728
|
-
}
|
729
|
-
}
|
730
|
-
return newArgs;
|
731
|
-
};
|
732
|
-
}
|
733
|
-
function buildKeyExpr(key) {
|
734
|
-
let mergedObject = {};
|
735
|
-
for (const keyDef of key) {
|
736
|
-
let [aliasOrKeyPath, keyPath] = keyDef.split(':');
|
737
|
-
let aliasPath;
|
738
|
-
if (keyPath == null) {
|
739
|
-
keyPath = aliasPath = aliasOrKeyPath;
|
740
|
-
}
|
741
|
-
else {
|
742
|
-
aliasPath = aliasOrKeyPath;
|
743
|
-
}
|
744
|
-
const aliasParts = aliasPath.split('.');
|
745
|
-
const lastAliasPart = aliasParts.pop();
|
746
|
-
if (lastAliasPart == null) {
|
747
|
-
throw new Error(`Key "${key}" is invalid, no path provided.`);
|
748
|
-
}
|
749
|
-
let object = { [lastAliasPart]: `$key.${keyPath}` };
|
750
|
-
for (const aliasPart of aliasParts.reverse()) {
|
751
|
-
object = { [aliasPart]: object };
|
752
|
-
}
|
753
|
-
mergedObject = utils.mergeDeep([mergedObject, object]);
|
754
|
-
}
|
755
|
-
return JSON.stringify(mergedObject).replace(/"/g, '');
|
756
|
-
}
|
757
|
-
function mergeSelectionSets(...selectionSets) {
|
758
|
-
const normalizedSelections = Object.create(null);
|
759
|
-
for (const selectionSet of selectionSets) {
|
760
|
-
for (const selection of selectionSet.selections) {
|
761
|
-
const normalizedSelection = graphql.print(selection);
|
762
|
-
normalizedSelections[normalizedSelection] = selection;
|
763
|
-
}
|
764
|
-
}
|
765
|
-
const newSelectionSet = {
|
766
|
-
kind: graphql.Kind.SELECTION_SET,
|
767
|
-
selections: Object.values(normalizedSelections),
|
768
|
-
};
|
769
|
-
return newSelectionSet;
|
770
|
-
}
|
771
|
-
function forEachConcreteTypeName(returnType, schema, typeNames, fn) {
|
772
|
-
if (graphql.isInterfaceType(returnType)) {
|
773
|
-
for (const typeName of utils.getImplementingTypes(returnType.name, schema)) {
|
774
|
-
if (typeNames == null || typeNames.includes(typeName)) {
|
775
|
-
fn(typeName);
|
776
|
-
}
|
777
|
-
}
|
778
|
-
}
|
779
|
-
else if (graphql.isUnionType(returnType)) {
|
780
|
-
for (const type of returnType.getTypes()) {
|
781
|
-
if (typeNames == null || typeNames.includes(type.name)) {
|
782
|
-
fn(type.name);
|
783
|
-
}
|
784
|
-
}
|
785
|
-
}
|
786
|
-
else if (graphql.isObjectType(returnType) && (typeNames == null || typeNames.includes(returnType.name))) {
|
787
|
-
fn(returnType.name);
|
788
|
-
}
|
789
|
-
}
|
790
|
-
|
791
|
-
function stitchingDirectives(options = {}) {
|
792
|
-
const finalOptions = {
|
793
|
-
...defaultStitchingDirectiveOptions,
|
794
|
-
...options,
|
795
|
-
};
|
796
|
-
const { keyDirectiveName, computedDirectiveName, mergeDirectiveName, canonicalDirectiveName } = finalOptions;
|
797
|
-
const keyDirectiveTypeDefs = /* GraphQL */ `directive @${keyDirectiveName}(selectionSet: String!) on OBJECT`;
|
798
|
-
const computedDirectiveTypeDefs = /* GraphQL */ `directive @${computedDirectiveName}(selectionSet: String!) on FIELD_DEFINITION`;
|
799
|
-
const mergeDirectiveTypeDefs = /* GraphQL */ `directive @${mergeDirectiveName}(argsExpr: String, keyArg: String, keyField: String, key: [String!], additionalArgs: String) on FIELD_DEFINITION`;
|
800
|
-
const canonicalDirectiveTypeDefs = /* GraphQL */ `directive @${canonicalDirectiveName} on OBJECT | INTERFACE | INPUT_OBJECT | UNION | ENUM | SCALAR | FIELD_DEFINITION | INPUT_FIELD_DEFINITION`;
|
801
|
-
const keyDirective = new graphql.GraphQLDirective({
|
802
|
-
name: keyDirectiveName,
|
803
|
-
locations: ['OBJECT'],
|
804
|
-
args: {
|
805
|
-
selectionSet: { type: new graphql.GraphQLNonNull(graphql.GraphQLString) },
|
806
|
-
},
|
807
|
-
});
|
808
|
-
const computedDirective = new graphql.GraphQLDirective({
|
809
|
-
name: computedDirectiveName,
|
810
|
-
locations: ['FIELD_DEFINITION'],
|
811
|
-
args: {
|
812
|
-
selectionSet: { type: new graphql.GraphQLNonNull(graphql.GraphQLString) },
|
813
|
-
},
|
814
|
-
});
|
815
|
-
const mergeDirective = new graphql.GraphQLDirective({
|
816
|
-
name: mergeDirectiveName,
|
817
|
-
locations: ['FIELD_DEFINITION'],
|
818
|
-
args: {
|
819
|
-
argsExpr: { type: graphql.GraphQLString },
|
820
|
-
keyArg: { type: graphql.GraphQLString },
|
821
|
-
keyField: { type: graphql.GraphQLString },
|
822
|
-
key: { type: new graphql.GraphQLList(new graphql.GraphQLNonNull(graphql.GraphQLString)) },
|
823
|
-
additionalArgs: { type: graphql.GraphQLString },
|
824
|
-
},
|
825
|
-
});
|
826
|
-
const canonicalDirective = new graphql.GraphQLDirective({
|
827
|
-
name: canonicalDirectiveName,
|
828
|
-
locations: [
|
829
|
-
'OBJECT',
|
830
|
-
'INTERFACE',
|
831
|
-
'INPUT_OBJECT',
|
832
|
-
'UNION',
|
833
|
-
'ENUM',
|
834
|
-
'SCALAR',
|
835
|
-
'FIELD_DEFINITION',
|
836
|
-
'INPUT_FIELD_DEFINITION',
|
837
|
-
],
|
838
|
-
});
|
839
|
-
const allStitchingDirectivesTypeDefs = [
|
840
|
-
keyDirectiveTypeDefs,
|
841
|
-
computedDirectiveTypeDefs,
|
842
|
-
mergeDirectiveTypeDefs,
|
843
|
-
canonicalDirectiveTypeDefs,
|
844
|
-
].join('\n');
|
845
|
-
return {
|
846
|
-
keyDirectiveTypeDefs,
|
847
|
-
computedDirectiveTypeDefs,
|
848
|
-
mergeDirectiveTypeDefs,
|
849
|
-
canonicalDirectiveTypeDefs,
|
850
|
-
stitchingDirectivesTypeDefs: allStitchingDirectivesTypeDefs,
|
851
|
-
allStitchingDirectivesTypeDefs,
|
852
|
-
keyDirective,
|
853
|
-
computedDirective,
|
854
|
-
mergeDirective,
|
855
|
-
canonicalDirective,
|
856
|
-
allStitchingDirectives: [keyDirective, computedDirective, mergeDirective, canonicalDirective],
|
857
|
-
stitchingDirectivesValidator: stitchingDirectivesValidator(finalOptions),
|
858
|
-
stitchingDirectivesTransformer: stitchingDirectivesTransformer(finalOptions),
|
859
|
-
};
|
860
|
-
}
|
861
|
-
|
862
|
-
// Taken from https://github.com/gmac/federation-to-stitching-sdl/blob/main/index.js
|
863
|
-
const extensionKind = /Extension$/;
|
864
|
-
const entityKinds = [
|
865
|
-
graphql.Kind.OBJECT_TYPE_DEFINITION,
|
866
|
-
graphql.Kind.OBJECT_TYPE_EXTENSION,
|
867
|
-
graphql.Kind.INTERFACE_TYPE_DEFINITION,
|
868
|
-
graphql.Kind.INTERFACE_TYPE_EXTENSION,
|
869
|
-
];
|
870
|
-
function isEntityKind(def) {
|
871
|
-
return entityKinds.includes(def.kind);
|
872
|
-
}
|
873
|
-
function getQueryTypeDef(definitions) {
|
874
|
-
var _a;
|
875
|
-
const schemaDef = definitions.find(def => def.kind === graphql.Kind.SCHEMA_DEFINITION);
|
876
|
-
const typeName = schemaDef
|
877
|
-
? (_a = schemaDef.operationTypes.find(({ operation }) => operation === 'query')) === null || _a === void 0 ? void 0 : _a.type.name.value
|
878
|
-
: 'Query';
|
879
|
-
return definitions.find(def => def.kind === graphql.Kind.OBJECT_TYPE_DEFINITION && def.name.value === typeName);
|
880
|
-
}
|
881
|
-
// Federation services are actually fairly complex,
|
882
|
-
// as the `buildFederatedSchema` helper does a fair amount
|
883
|
-
// of hidden work to setup the Federation schema specification:
|
884
|
-
// https://www.apollographql.com/docs/federation/federation-spec/#federation-schema-specification
|
885
|
-
function federationToStitchingSDL(federationSDL, stitchingConfig = stitchingDirectives()) {
|
886
|
-
const doc = graphql.parse(federationSDL);
|
887
|
-
const entityTypes = [];
|
888
|
-
const baseTypeNames = doc.definitions.reduce((memo, typeDef) => {
|
889
|
-
if (!extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name) {
|
890
|
-
memo[typeDef.name.value] = true;
|
891
|
-
}
|
892
|
-
return memo;
|
893
|
-
}, {});
|
894
|
-
doc.definitions.forEach(typeDef => {
|
895
|
-
var _a, _b, _c;
|
896
|
-
// Un-extend all types (remove "extends" keywords)...
|
897
|
-
// extended types are invalid GraphQL without a local base type to extend from.
|
898
|
-
// Stitching merges flat types in lieu of hierarchical extensions.
|
899
|
-
if (extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name && !baseTypeNames[typeDef.name.value]) {
|
900
|
-
typeDef.kind = typeDef.kind.replace(extensionKind, 'Definition');
|
901
|
-
}
|
902
|
-
if (!isEntityKind(typeDef))
|
903
|
-
return;
|
904
|
-
// Find object definitions with "@key" directives;
|
905
|
-
// these are federated entities that get turned into merged types.
|
906
|
-
const keyDirs = [];
|
907
|
-
const otherDirs = [];
|
908
|
-
(_a = typeDef.directives) === null || _a === void 0 ? void 0 : _a.forEach(dir => {
|
909
|
-
if (dir.name.value === 'key') {
|
910
|
-
keyDirs.push(dir);
|
911
|
-
}
|
912
|
-
else {
|
913
|
-
otherDirs.push(dir);
|
914
|
-
}
|
915
|
-
});
|
916
|
-
if (!keyDirs.length)
|
917
|
-
return;
|
918
|
-
// Setup stitching MergedTypeConfig for all federated entities:
|
919
|
-
const selectionSet = `{ ${keyDirs.map((dir) => dir.arguments[0].value.value).join(' ')} }`;
|
920
|
-
const keyFields = graphql.parse(selectionSet).definitions[0].selectionSet.selections.map((sel) => sel.name.value);
|
921
|
-
const keyDir = keyDirs[0];
|
922
|
-
keyDir.name.value = stitchingConfig.keyDirective.name;
|
923
|
-
keyDir.arguments[0].name.value = 'selectionSet';
|
924
|
-
keyDir.arguments[0].value.value = selectionSet;
|
925
|
-
typeDef.directives = [keyDir, ...otherDirs];
|
926
|
-
// Remove non-key "@external" fields from the type...
|
927
|
-
// the stitching query planner expects services to only publish their own fields.
|
928
|
-
// This makes "@provides" moot because the query planner can automate the logic.
|
929
|
-
typeDef.fields = (_b = typeDef.fields) === null || _b === void 0 ? void 0 : _b.filter(fieldDef => {
|
930
|
-
var _a;
|
931
|
-
return (keyFields.includes(fieldDef.name.value) || !((_a = fieldDef.directives) === null || _a === void 0 ? void 0 : _a.find(dir => dir.name.value === 'external')));
|
932
|
-
});
|
933
|
-
// Discard remaining "@external" directives and any "@provides" directives
|
934
|
-
(_c = typeDef.fields) === null || _c === void 0 ? void 0 : _c.forEach((fieldDef) => {
|
935
|
-
fieldDef.directives = fieldDef.directives.filter((dir) => !/^(external|provides)$/.test(dir.name.value));
|
936
|
-
fieldDef.directives.forEach((dir) => {
|
937
|
-
if (dir.name.value === 'requires') {
|
938
|
-
dir.name.value = stitchingConfig.computedDirective.name;
|
939
|
-
dir.arguments[0].name.value = 'selectionSet';
|
940
|
-
dir.arguments[0].value.value = `{ ${dir.arguments[0].value.value} }`;
|
941
|
-
}
|
942
|
-
});
|
943
|
-
});
|
944
|
-
if (typeDef.kind === graphql.Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === graphql.Kind.OBJECT_TYPE_EXTENSION) {
|
945
|
-
entityTypes.push(typeDef.name.value);
|
946
|
-
}
|
947
|
-
});
|
948
|
-
// Federation service SDLs are incomplete because they omit the federation spec itself...
|
949
|
-
// (https://www.apollographql.com/docs/federation/federation-spec/#federation-schema-specification)
|
950
|
-
// To make federation SDLs into valid and parsable GraphQL schemas,
|
951
|
-
// we must fill in the missing details from the specification.
|
952
|
-
if (entityTypes.length) {
|
953
|
-
const queryDef = getQueryTypeDef(doc.definitions);
|
954
|
-
const entitiesSchema = graphql.parse(/* GraphQL */ `
|
955
|
-
scalar _Any
|
956
|
-
union _Entity = ${entityTypes.filter((v, i, a) => a.indexOf(v) === i).join(' | ')}
|
957
|
-
type Query { _entities(representations: [_Any!]!): [_Entity]! @${stitchingConfig.mergeDirective.name} }
|
958
|
-
`).definitions;
|
959
|
-
doc.definitions.push(entitiesSchema[0]);
|
960
|
-
doc.definitions.push(entitiesSchema[1]);
|
961
|
-
if (queryDef) {
|
962
|
-
queryDef.fields.push(entitiesSchema[2].fields[0]);
|
963
|
-
}
|
964
|
-
else {
|
965
|
-
doc.definitions.push(entitiesSchema[2]);
|
966
|
-
}
|
967
|
-
}
|
968
|
-
return [stitchingConfig.stitchingDirectivesTypeDefs, graphql.print(doc)].join('\n');
|
969
|
-
}
|
970
|
-
|
971
|
-
exports.federationToStitchingSDL = federationToStitchingSDL;
|
972
|
-
exports.stitchingDirectives = stitchingDirectives;
|