UNPKG

18.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.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;
4const graphql_1 = require("graphql");
5const directives_1 = require("@apollo/subgraph/dist/directives");
6const utilities_1 = require("../utilities");
7const parser_1 = require("graphql/language/parser");
8function isStringValueNode(node) {
9 return node.kind === graphql_1.Kind.STRING;
10}
11exports.isStringValueNode = isStringValueNode;
12function isDirectiveDefinitionNode(node) {
13 return node.kind === graphql_1.Kind.DIRECTIVE_DEFINITION;
14}
15exports.isDirectiveDefinitionNode = isDirectiveDefinitionNode;
16function isNonNullTypeNode(node) {
17 return node.kind === graphql_1.Kind.NON_NULL_TYPE;
18}
19exports.isNonNullTypeNode = isNonNullTypeNode;
20function isNamedTypeNode(node) {
21 return node.kind === graphql_1.Kind.NAMED_TYPE;
22}
23exports.isNamedTypeNode = isNamedTypeNode;
24function mapFieldNamesToServiceName(fields, serviceName) {
25 return fields.reduce((prev, next) => {
26 prev[next.name.value] = serviceName;
27 return prev;
28 }, Object.create(null));
29}
30exports.mapFieldNamesToServiceName = mapFieldNamesToServiceName;
31function 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}
35exports.findDirectivesOnNode = findDirectivesOnNode;
36function printFieldSet(selections) {
37 return selections
38 .map((selection) => (0, graphql_1.stripIgnoredCharacters)((0, graphql_1.print)(selection)))
39 .join(' ');
40}
41exports.printFieldSet = printFieldSet;
42function 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}
50exports.findSelectionSetOnNode = findSelectionSetOnNode;
51function 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}
59exports.stripExternalFieldsFromTypeDefs = stripExternalFieldsFromTypeDefs;
60function 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}
71exports.stripTypeSystemDirectivesFromTypeDefs = stripTypeSystemDirectivesFromTypeDefs;
72function 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}
95function 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}
114exports.parseFieldSet = parseFieldSet;
115function 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}
129exports.hasMatchingFieldInDirectives = hasMatchingFieldInDirectives;
130const logServiceAndType = (serviceName, typeName, fieldName) => `[${serviceName}] ${typeName}${fieldName ? `.${fieldName} -> ` : ' -> '}`;
131exports.logServiceAndType = logServiceAndType;
132function logDirective(directiveName) {
133 return `[@${directiveName}] -> `;
134}
135exports.logDirective = logDirective;
136function errorWithCode(code, message, nodes) {
137 return new graphql_1.GraphQLError(message, nodes, undefined, undefined, undefined, undefined, {
138 code,
139 });
140}
141exports.errorWithCode = errorWithCode;
142function 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}
161exports.findTypesContainingFieldWithReturnType = findTypesContainingFieldWithReturnType;
162function 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}
180exports.findFieldsThatReturnType = findFieldsThatReturnType;
181function 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}
208exports.selectionIncludesField = selectionIncludesField;
209function 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}
221exports.isTypeNodeAnEntity = isTypeNodeAnEntity;
222function 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}
349exports.diffTypeNodes = diffTypeNodes;
350function 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}
360exports.typeNodesAreEquivalent = typeNodesAreEquivalent;
361function 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}
369exports.findTypeNodeInServiceList = findTypeNodeInServiceList;
370exports.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};
378exports.executableDirectiveLocations = [
379 'QUERY',
380 'MUTATION',
381 'SUBSCRIPTION',
382 'FIELD',
383 'FRAGMENT_DEFINITION',
384 'FRAGMENT_SPREAD',
385 'INLINE_FRAGMENT',
386 'VARIABLE_DEFINITION',
387];
388exports.reservedRootFields = ['_service', '_entities'];
389exports.defaultRootOperationNameLookup = {
390 query: 'Query',
391 mutation: 'Mutation',
392 subscription: 'Subscription',
393};
394function compositionHasErrors(compositionResult) {
395 return 'errors' in compositionResult && !!compositionResult.errors;
396}
397exports.compositionHasErrors = compositionHasErrors;
398function assertCompositionSuccess(compositionResult, message) {
399 if (compositionHasErrors(compositionResult)) {
400 throw new Error(message || 'Unexpected test failure');
401 }
402}
403exports.assertCompositionSuccess = assertCompositionSuccess;
404function assertCompositionFailure(compositionResult, message) {
405 if (!compositionHasErrors(compositionResult)) {
406 throw new Error(message || 'Unexpected test failure');
407 }
408}
409exports.assertCompositionFailure = assertCompositionFailure;
410function 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}
421exports.getFederationMetadata = getFederationMetadata;
422//# sourceMappingURL=utils.js.map
\No newline at end of file