1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.getFederationMetadata = exports.assertCompositionFailure = exports.assertCompositionSuccess = exports.compositionHasErrors = exports.defaultRootOperationNameLookup = exports.reservedRootFields = exports.executableDirectiveLocations = exports.defKindToExtKind = exports.findTypeNodeInServiceList = exports.typeNodesAreEquivalent = exports.diffTypeNodes = exports.isTypeNodeAnEntity = exports.selectionIncludesField = exports.findFieldsThatReturnType = exports.findTypesContainingFieldWithReturnType = exports.errorWithCode = exports.logDirective = exports.logServiceAndType = exports.hasMatchingFieldInDirectives = exports.parseFieldSet = exports.stripTypeSystemDirectivesFromTypeDefs = exports.stripExternalFieldsFromTypeDefs = exports.findSelectionSetOnNode = exports.printFieldSet = exports.findDirectivesOnNode = exports.mapFieldNamesToServiceName = exports.isNamedTypeNode = exports.isNonNullTypeNode = exports.isDirectiveDefinitionNode = exports.isStringValueNode = void 0;
|
4 | const graphql_1 = require("graphql");
|
5 | const directives_1 = require("@apollo/subgraph/dist/directives");
|
6 | const utilities_1 = require("../utilities");
|
7 | const parser_1 = require("graphql/language/parser");
|
8 | function isStringValueNode(node) {
|
9 | return node.kind === graphql_1.Kind.STRING;
|
10 | }
|
11 | exports.isStringValueNode = isStringValueNode;
|
12 | function isDirectiveDefinitionNode(node) {
|
13 | return node.kind === graphql_1.Kind.DIRECTIVE_DEFINITION;
|
14 | }
|
15 | exports.isDirectiveDefinitionNode = isDirectiveDefinitionNode;
|
16 | function isNonNullTypeNode(node) {
|
17 | return node.kind === graphql_1.Kind.NON_NULL_TYPE;
|
18 | }
|
19 | exports.isNonNullTypeNode = isNonNullTypeNode;
|
20 | function isNamedTypeNode(node) {
|
21 | return node.kind === graphql_1.Kind.NAMED_TYPE;
|
22 | }
|
23 | exports.isNamedTypeNode = isNamedTypeNode;
|
24 | function mapFieldNamesToServiceName(fields, serviceName) {
|
25 | return fields.reduce((prev, next) => {
|
26 | prev[next.name.value] = serviceName;
|
27 | return prev;
|
28 | }, Object.create(null));
|
29 | }
|
30 | exports.mapFieldNamesToServiceName = mapFieldNamesToServiceName;
|
31 | function findDirectivesOnNode(node, directiveName) {
|
32 | var _a, _b;
|
33 | return ((_b = (_a = node === null || node === void 0 ? void 0 : node.directives) === null || _a === void 0 ? void 0 : _a.filter((directive) => directive.name.value === directiveName)) !== null && _b !== void 0 ? _b : []);
|
34 | }
|
35 | exports.findDirectivesOnNode = findDirectivesOnNode;
|
36 | function printFieldSet(selections) {
|
37 | return selections
|
38 | .map((selection) => (0, graphql_1.stripIgnoredCharacters)((0, graphql_1.print)(selection)))
|
39 | .join(' ');
|
40 | }
|
41 | exports.printFieldSet = printFieldSet;
|
42 | function findSelectionSetOnNode(node, directiveName, printedSelectionSet) {
|
43 | var _a, _b, _c, _d;
|
44 | return (_d = (_c = (_b = (_a = node === null || node === void 0 ? void 0 : node.directives) === null || _a === void 0 ? void 0 : _a.find(directive => {
|
45 | var _a;
|
46 | return directive.name.value === directiveName && ((_a = directive.arguments) === null || _a === void 0 ? void 0 : _a.some(argument => isStringValueNode(argument.value) &&
|
47 | argument.value.value === printedSelectionSet));
|
48 | })) === null || _b === void 0 ? void 0 : _b.arguments) === null || _c === void 0 ? void 0 : _c.find(argument => argument.name.value === 'fields')) === null || _d === void 0 ? void 0 : _d.value;
|
49 | }
|
50 | exports.findSelectionSetOnNode = findSelectionSetOnNode;
|
51 | function stripExternalFieldsFromTypeDefs(typeDefs, serviceName) {
|
52 | const strippedFields = [];
|
53 | const typeDefsWithoutExternalFields = (0, graphql_1.visit)(typeDefs, {
|
54 | ObjectTypeExtension: removeExternalFieldsFromExtensionVisitor(strippedFields, serviceName),
|
55 | InterfaceTypeExtension: removeExternalFieldsFromExtensionVisitor(strippedFields, serviceName),
|
56 | });
|
57 | return { typeDefsWithoutExternalFields, strippedFields };
|
58 | }
|
59 | exports.stripExternalFieldsFromTypeDefs = stripExternalFieldsFromTypeDefs;
|
60 | function stripTypeSystemDirectivesFromTypeDefs(typeDefs) {
|
61 | const typeDefsWithoutTypeSystemDirectives = (0, graphql_1.visit)(typeDefs, {
|
62 | Directive(node) {
|
63 | if (node.name.value === 'deprecated' || node.name.value === 'specifiedBy')
|
64 | return;
|
65 | const isKnownSubgraphDirective = directives_1.knownSubgraphDirectives.some(({ name }) => name === node.name.value);
|
66 | return isKnownSubgraphDirective ? undefined : null;
|
67 | },
|
68 | });
|
69 | return typeDefsWithoutTypeSystemDirectives;
|
70 | }
|
71 | exports.stripTypeSystemDirectivesFromTypeDefs = stripTypeSystemDirectivesFromTypeDefs;
|
72 | function removeExternalFieldsFromExtensionVisitor(collector, serviceName) {
|
73 | return (node) => {
|
74 | let fields = node.fields;
|
75 | if (fields) {
|
76 | fields = fields.filter(field => {
|
77 | const externalDirectives = findDirectivesOnNode(field, 'external');
|
78 | if (externalDirectives.length > 0) {
|
79 | collector.push({
|
80 | field,
|
81 | parentTypeName: node.name.value,
|
82 | serviceName,
|
83 | });
|
84 | return false;
|
85 | }
|
86 | return true;
|
87 | });
|
88 | }
|
89 | return {
|
90 | ...node,
|
91 | fields,
|
92 | };
|
93 | };
|
94 | }
|
95 | function parseFieldSet(source) {
|
96 | const parser = new parser_1.Parser(`{${source}}`);
|
97 | parser.expectToken(graphql_1.TokenKind.SOF);
|
98 | const selectionSet = parser.parseSelectionSet();
|
99 | try {
|
100 | parser.expectToken(graphql_1.TokenKind.EOF);
|
101 | }
|
102 | catch {
|
103 | throw new Error(`Invalid FieldSet provided: '${source}'. FieldSets may not contain operations within them.`);
|
104 | }
|
105 | const selections = selectionSet.selections;
|
106 | (0, utilities_1.assert)(selections.length > 0, `Field sets may not be empty`);
|
107 | (0, graphql_1.visit)(selectionSet, {
|
108 | FragmentSpread() {
|
109 | throw Error(`Field sets may not contain fragment spreads, but found: "${source}"`);
|
110 | },
|
111 | });
|
112 | return selections;
|
113 | }
|
114 | exports.parseFieldSet = parseFieldSet;
|
115 | function hasMatchingFieldInDirectives({ directives, fieldNameToMatch, namedType, }) {
|
116 | return Boolean(namedType.astNode &&
|
117 | directives
|
118 | .map(keyDirective => keyDirective.arguments &&
|
119 | isStringValueNode(keyDirective.arguments[0].value)
|
120 | ? {
|
121 | typeName: namedType.astNode.name.value,
|
122 | keyArgument: keyDirective.arguments[0].value.value,
|
123 | }
|
124 | : null)
|
125 | .filter(utilities_1.isNotNullOrUndefined)
|
126 | .flatMap(selection => parseFieldSet(selection.keyArgument))
|
127 | .some(field => field.kind === graphql_1.Kind.FIELD && field.name.value === fieldNameToMatch));
|
128 | }
|
129 | exports.hasMatchingFieldInDirectives = hasMatchingFieldInDirectives;
|
130 | const logServiceAndType = (serviceName, typeName, fieldName) => `[${serviceName}] ${typeName}${fieldName ? `.${fieldName} -> ` : ' -> '}`;
|
131 | exports.logServiceAndType = logServiceAndType;
|
132 | function logDirective(directiveName) {
|
133 | return `[@${directiveName}] -> `;
|
134 | }
|
135 | exports.logDirective = logDirective;
|
136 | function errorWithCode(code, message, nodes) {
|
137 | return new graphql_1.GraphQLError(message, nodes, undefined, undefined, undefined, undefined, {
|
138 | code,
|
139 | });
|
140 | }
|
141 | exports.errorWithCode = errorWithCode;
|
142 | function findTypesContainingFieldWithReturnType(schema, node) {
|
143 | const returnType = (0, graphql_1.getNamedType)(node.type);
|
144 | if (!(0, graphql_1.isObjectType)(returnType))
|
145 | return [];
|
146 | const containingTypes = [];
|
147 | const types = schema.getTypeMap();
|
148 | for (const selectionSetType of Object.values(types)) {
|
149 | if (!(0, graphql_1.isObjectType)(selectionSetType))
|
150 | continue;
|
151 | const allFields = selectionSetType.getFields();
|
152 | Object.values(allFields).forEach(field => {
|
153 | const fieldReturnType = (0, graphql_1.getNamedType)(field.type);
|
154 | if (fieldReturnType === returnType) {
|
155 | containingTypes.push(fieldReturnType);
|
156 | }
|
157 | });
|
158 | }
|
159 | return containingTypes;
|
160 | }
|
161 | exports.findTypesContainingFieldWithReturnType = findTypesContainingFieldWithReturnType;
|
162 | function findFieldsThatReturnType({ schema, typeToFind, }) {
|
163 | if (!(0, graphql_1.isObjectType)(typeToFind))
|
164 | return [];
|
165 | const fieldsThatReturnType = [];
|
166 | const types = schema.getTypeMap();
|
167 | for (const selectionSetType of Object.values(types)) {
|
168 | if (!(0, graphql_1.isObjectType)(selectionSetType))
|
169 | continue;
|
170 | const fieldsOnNamedType = selectionSetType.getFields();
|
171 | Object.values(fieldsOnNamedType).forEach(field => {
|
172 | const fieldReturnType = (0, graphql_1.getNamedType)(field.type);
|
173 | if (fieldReturnType === typeToFind) {
|
174 | fieldsThatReturnType.push(field);
|
175 | }
|
176 | });
|
177 | }
|
178 | return fieldsThatReturnType;
|
179 | }
|
180 | exports.findFieldsThatReturnType = findFieldsThatReturnType;
|
181 | function selectionIncludesField({ selections, selectionSetType, typeToFind, fieldToFind, }) {
|
182 | for (const selection of selections) {
|
183 | const selectionName = selection.name.value;
|
184 | if (selectionName === fieldToFind &&
|
185 | (0, graphql_1.isEqualType)(selectionSetType, typeToFind))
|
186 | return true;
|
187 | const typeIncludesField = selectionName &&
|
188 | Object.keys(selectionSetType.getFields()).includes(selectionName);
|
189 | if (!selectionName || !typeIncludesField)
|
190 | continue;
|
191 | const returnType = (0, graphql_1.getNamedType)(selectionSetType.getFields()[selectionName].type);
|
192 | if (!returnType || !(0, graphql_1.isObjectType)(returnType))
|
193 | continue;
|
194 | const subselections = selection.selectionSet && selection.selectionSet.selections;
|
195 | if (subselections) {
|
196 | const selectionDoesIncludeField = selectionIncludesField({
|
197 | selectionSetType: returnType,
|
198 | selections: subselections,
|
199 | typeToFind,
|
200 | fieldToFind,
|
201 | });
|
202 | if (selectionDoesIncludeField)
|
203 | return true;
|
204 | }
|
205 | }
|
206 | return false;
|
207 | }
|
208 | exports.selectionIncludesField = selectionIncludesField;
|
209 | function isTypeNodeAnEntity(node) {
|
210 | let isEntity = false;
|
211 | (0, graphql_1.visit)(node, {
|
212 | Directive(directive) {
|
213 | if (directive.name.value === 'key') {
|
214 | isEntity = true;
|
215 | return graphql_1.BREAK;
|
216 | }
|
217 | },
|
218 | });
|
219 | return isEntity;
|
220 | }
|
221 | exports.isTypeNodeAnEntity = isTypeNodeAnEntity;
|
222 | function diffTypeNodes(firstNode, secondNode) {
|
223 | const fieldsDiff = Object.create(null);
|
224 | const unionTypesDiff = Object.create(null);
|
225 | const locationsDiff = new Set();
|
226 | const fieldArgsDiff = Object.create(null);
|
227 | const directiveArgsDiff = Object.create(null);
|
228 | const document = {
|
229 | kind: graphql_1.Kind.DOCUMENT,
|
230 | definitions: [firstNode, secondNode],
|
231 | };
|
232 | function inputFieldVisitor(node) {
|
233 | const fieldName = node.name.value;
|
234 | const type = (0, graphql_1.print)(node.type);
|
235 | if (!fieldsDiff[fieldName]) {
|
236 | fieldsDiff[fieldName] = [type];
|
237 | return;
|
238 | }
|
239 | const fieldTypes = fieldsDiff[fieldName];
|
240 | if (fieldTypes[0] === type) {
|
241 | delete fieldsDiff[fieldName];
|
242 | }
|
243 | else {
|
244 | fieldTypes.push(type);
|
245 | }
|
246 | }
|
247 | function fieldVisitor(node) {
|
248 | const fieldName = node.name.value;
|
249 | const type = (0, graphql_1.print)(node.type);
|
250 | if (!fieldsDiff[fieldName]) {
|
251 | fieldsDiff[fieldName] = [type];
|
252 | }
|
253 | else {
|
254 | const fieldTypes = fieldsDiff[fieldName];
|
255 | if (fieldTypes[0] === type) {
|
256 | delete fieldsDiff[fieldName];
|
257 | }
|
258 | else {
|
259 | fieldTypes.push(type);
|
260 | }
|
261 | }
|
262 | if (!fieldArgsDiff[fieldName]) {
|
263 | fieldArgsDiff[fieldName] = Object.create(null);
|
264 | }
|
265 | const argumentsDiff = fieldArgsDiff[fieldName];
|
266 | const nodeArgs = Array.isArray(node.arguments) ? node.arguments : [];
|
267 | nodeArgs.forEach(argument => {
|
268 | const argumentName = argument.name.value;
|
269 | const printedType = (0, graphql_1.print)(argument.type);
|
270 | if (!argumentsDiff[argumentName]) {
|
271 | argumentsDiff[argumentName] = [printedType];
|
272 | }
|
273 | else {
|
274 | if (printedType === argumentsDiff[argumentName][0]) {
|
275 | delete argumentsDiff[argumentName];
|
276 | }
|
277 | else {
|
278 | argumentsDiff[argumentName].push(printedType);
|
279 | }
|
280 | }
|
281 | });
|
282 | if (Object.keys(argumentsDiff).length === 0) {
|
283 | delete fieldArgsDiff[fieldName];
|
284 | }
|
285 | }
|
286 | (0, graphql_1.visit)(document, {
|
287 | FieldDefinition: fieldVisitor,
|
288 | InputObjectTypeDefinition(node) {
|
289 | if (Array.isArray(node.fields)) {
|
290 | node.fields.forEach(inputFieldVisitor);
|
291 | }
|
292 | },
|
293 | UnionTypeDefinition(node) {
|
294 | if (!node.types)
|
295 | return graphql_1.BREAK;
|
296 | for (const namedTypeNode of node.types) {
|
297 | const name = namedTypeNode.name.value;
|
298 | if (unionTypesDiff[name]) {
|
299 | delete unionTypesDiff[name];
|
300 | }
|
301 | else {
|
302 | unionTypesDiff[name] = true;
|
303 | }
|
304 | }
|
305 | },
|
306 | DirectiveDefinition(node) {
|
307 | node.locations.forEach(location => {
|
308 | const locationName = location.value;
|
309 | if (locationsDiff.has(locationName)) {
|
310 | locationsDiff.delete(locationName);
|
311 | }
|
312 | else {
|
313 | locationsDiff.add(locationName);
|
314 | }
|
315 | });
|
316 | if (!node.arguments)
|
317 | return;
|
318 | node.arguments.forEach(argument => {
|
319 | const argumentName = argument.name.value;
|
320 | const printedType = (0, graphql_1.print)(argument.type);
|
321 | if (directiveArgsDiff[argumentName]) {
|
322 | if (printedType === directiveArgsDiff[argumentName][0]) {
|
323 | delete directiveArgsDiff[argumentName];
|
324 | }
|
325 | else {
|
326 | directiveArgsDiff[argumentName].push(printedType);
|
327 | }
|
328 | }
|
329 | else {
|
330 | directiveArgsDiff[argumentName] = [printedType];
|
331 | }
|
332 | });
|
333 | },
|
334 | });
|
335 | const typeNameDiff = firstNode.name.value === secondNode.name.value
|
336 | ? []
|
337 | : [firstNode.name.value, secondNode.name.value];
|
338 | const kindDiff = firstNode.kind === secondNode.kind ? [] : [firstNode.kind, secondNode.kind];
|
339 | return {
|
340 | name: typeNameDiff,
|
341 | kind: kindDiff,
|
342 | fields: fieldsDiff,
|
343 | fieldArgs: fieldArgsDiff,
|
344 | unionTypes: unionTypesDiff,
|
345 | locations: Array.from(locationsDiff),
|
346 | directiveArgs: directiveArgsDiff
|
347 | };
|
348 | }
|
349 | exports.diffTypeNodes = diffTypeNodes;
|
350 | function typeNodesAreEquivalent(firstNode, secondNode) {
|
351 | const { name, kind, fields, fieldArgs, unionTypes, locations, directiveArgs } = diffTypeNodes(firstNode, secondNode);
|
352 | return (name.length === 0 &&
|
353 | kind.length === 0 &&
|
354 | Object.keys(fields).length === 0 &&
|
355 | Object.keys(fieldArgs).length === 0 &&
|
356 | Object.keys(unionTypes).length === 0 &&
|
357 | locations.length === 0 &&
|
358 | Object.keys(directiveArgs).length === 0);
|
359 | }
|
360 | exports.typeNodesAreEquivalent = typeNodesAreEquivalent;
|
361 | function findTypeNodeInServiceList(typeName, serviceName, serviceList) {
|
362 | var _a;
|
363 | return (_a = serviceList.find(service => service.name === serviceName)) === null || _a === void 0 ? void 0 : _a.typeDefs.definitions.find(definition => {
|
364 | var _a;
|
365 | return 'name' in definition
|
366 | && ((_a = definition.name) === null || _a === void 0 ? void 0 : _a.value) === typeName;
|
367 | });
|
368 | }
|
369 | exports.findTypeNodeInServiceList = findTypeNodeInServiceList;
|
370 | exports.defKindToExtKind = {
|
371 | [graphql_1.Kind.SCALAR_TYPE_DEFINITION]: graphql_1.Kind.SCALAR_TYPE_EXTENSION,
|
372 | [graphql_1.Kind.OBJECT_TYPE_DEFINITION]: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
|
373 | [graphql_1.Kind.INTERFACE_TYPE_DEFINITION]: graphql_1.Kind.INTERFACE_TYPE_EXTENSION,
|
374 | [graphql_1.Kind.UNION_TYPE_DEFINITION]: graphql_1.Kind.UNION_TYPE_EXTENSION,
|
375 | [graphql_1.Kind.ENUM_TYPE_DEFINITION]: graphql_1.Kind.ENUM_TYPE_EXTENSION,
|
376 | [graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION]: graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
377 | };
|
378 | exports.executableDirectiveLocations = [
|
379 | 'QUERY',
|
380 | 'MUTATION',
|
381 | 'SUBSCRIPTION',
|
382 | 'FIELD',
|
383 | 'FRAGMENT_DEFINITION',
|
384 | 'FRAGMENT_SPREAD',
|
385 | 'INLINE_FRAGMENT',
|
386 | 'VARIABLE_DEFINITION',
|
387 | ];
|
388 | exports.reservedRootFields = ['_service', '_entities'];
|
389 | exports.defaultRootOperationNameLookup = {
|
390 | query: 'Query',
|
391 | mutation: 'Mutation',
|
392 | subscription: 'Subscription',
|
393 | };
|
394 | function compositionHasErrors(compositionResult) {
|
395 | return 'errors' in compositionResult && !!compositionResult.errors;
|
396 | }
|
397 | exports.compositionHasErrors = compositionHasErrors;
|
398 | function assertCompositionSuccess(compositionResult, message) {
|
399 | if (compositionHasErrors(compositionResult)) {
|
400 | throw new Error(message || 'Unexpected test failure');
|
401 | }
|
402 | }
|
403 | exports.assertCompositionSuccess = assertCompositionSuccess;
|
404 | function assertCompositionFailure(compositionResult, message) {
|
405 | if (!compositionHasErrors(compositionResult)) {
|
406 | throw new Error(message || 'Unexpected test failure');
|
407 | }
|
408 | }
|
409 | exports.assertCompositionFailure = assertCompositionFailure;
|
410 | function getFederationMetadata(obj) {
|
411 | var _a, _b, _c;
|
412 | if (typeof obj === "undefined")
|
413 | return undefined;
|
414 | else if ((0, graphql_1.isNamedType)(obj))
|
415 | return (_a = obj.extensions) === null || _a === void 0 ? void 0 : _a.federation;
|
416 | else if ((0, graphql_1.isDirective)(obj))
|
417 | return (_b = obj.extensions) === null || _b === void 0 ? void 0 : _b.federation;
|
418 | else
|
419 | return (_c = obj.extensions) === null || _c === void 0 ? void 0 : _c.federation;
|
420 | }
|
421 | exports.getFederationMetadata = getFederationMetadata;
|
422 |
|
\ | No newline at end of file |