UNPKG

17.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.composeServices = exports.addFederationMetadataToSchemaNodes = exports.buildSchemaFromDefinitionsAndExtensions = exports.buildMapsFromServiceList = void 0;
4const graphql_1 = require("graphql");
5const schema_helper_1 = require("@apollo/subgraph/dist/schema-helper");
6const directives_1 = require("@apollo/subgraph/dist/directives");
7const utils_1 = require("./utils");
8const validate_1 = require("graphql/validation/validate");
9const rules_1 = require("./rules");
10const printSupergraphSdl_1 = require("../service/printSupergraphSdl");
11const utilities_1 = require("../utilities");
12const DirectiveMetadata_1 = require("./DirectiveMetadata");
13const joinSpec_1 = require("../joinSpec");
14const coreSpec_1 = require("../coreSpec");
15const EmptyQueryDefinition = {
16 kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
17 name: { kind: graphql_1.Kind.NAME, value: utils_1.defaultRootOperationNameLookup.query },
18 fields: [],
19 serviceName: null,
20};
21const EmptyMutationDefinition = {
22 kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
23 name: { kind: graphql_1.Kind.NAME, value: utils_1.defaultRootOperationNameLookup.mutation },
24 fields: [],
25 serviceName: null,
26};
27function buildMapsFromServiceList(serviceList) {
28 const typeDefinitionsMap = Object.create(null);
29 const typeExtensionsMap = Object.create(null);
30 const directiveDefinitionsMap = Object.create(null);
31 const typeToServiceMap = Object.create(null);
32 const externalFields = [];
33 const keyDirectivesMap = Object.create(null);
34 const valueTypes = new Set();
35 const directiveMetadata = new DirectiveMetadata_1.DirectiveMetadata(serviceList);
36 for (const { typeDefs, name: serviceName } of serviceList) {
37 const { typeDefsWithoutExternalFields, strippedFields, } = (0, utils_1.stripExternalFieldsFromTypeDefs)(typeDefs, serviceName);
38 externalFields.push(...strippedFields);
39 const typeDefsWithoutTypeSystemDirectives = (0, utils_1.stripTypeSystemDirectivesFromTypeDefs)(typeDefsWithoutExternalFields);
40 for (const definition of typeDefsWithoutTypeSystemDirectives.definitions) {
41 if (definition.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
42 definition.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
43 const typeName = definition.name.value;
44 for (const keyDirective of (0, utils_1.findDirectivesOnNode)(definition, 'key')) {
45 if (keyDirective.arguments &&
46 (0, utils_1.isStringValueNode)(keyDirective.arguments[0].value)) {
47 keyDirectivesMap[typeName] = keyDirectivesMap[typeName] || {};
48 keyDirectivesMap[typeName][serviceName] =
49 keyDirectivesMap[typeName][serviceName] || [];
50 keyDirectivesMap[typeName][serviceName].push((0, utils_1.parseFieldSet)(keyDirective.arguments[0].value.value));
51 }
52 }
53 }
54 if ((0, graphql_1.isTypeDefinitionNode)(definition)) {
55 const typeName = definition.name.value;
56 if (!typeToServiceMap[typeName]) {
57 typeToServiceMap[typeName] = {
58 extensionFieldsToOwningServiceMap: Object.create(null),
59 };
60 }
61 typeToServiceMap[typeName].owningService = serviceName;
62 if (typeDefinitionsMap[typeName]) {
63 const isValueType = (0, utils_1.typeNodesAreEquivalent)(typeDefinitionsMap[typeName][typeDefinitionsMap[typeName].length - 1], definition);
64 if (isValueType) {
65 valueTypes.add(typeName);
66 }
67 typeDefinitionsMap[typeName].push({ ...definition, serviceName });
68 }
69 else {
70 typeDefinitionsMap[typeName] = [{ ...definition, serviceName }];
71 }
72 }
73 else if ((0, graphql_1.isTypeExtensionNode)(definition)) {
74 const typeName = definition.name.value;
75 if (definition.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION ||
76 definition.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION) {
77 if (!definition.fields)
78 break;
79 const fields = (0, utils_1.mapFieldNamesToServiceName)(definition.fields, serviceName);
80 if (typeToServiceMap[typeName]) {
81 typeToServiceMap[typeName].extensionFieldsToOwningServiceMap = {
82 ...typeToServiceMap[typeName].extensionFieldsToOwningServiceMap,
83 ...fields,
84 };
85 }
86 else {
87 typeToServiceMap[typeName] = {
88 extensionFieldsToOwningServiceMap: fields,
89 };
90 }
91 }
92 if (definition.kind === graphql_1.Kind.ENUM_TYPE_EXTENSION) {
93 if (!definition.values)
94 break;
95 const values = (0, utils_1.mapFieldNamesToServiceName)(definition.values, serviceName);
96 if (typeToServiceMap[typeName]) {
97 typeToServiceMap[typeName].extensionFieldsToOwningServiceMap = {
98 ...typeToServiceMap[typeName].extensionFieldsToOwningServiceMap,
99 ...values,
100 };
101 }
102 else {
103 typeToServiceMap[typeName] = {
104 extensionFieldsToOwningServiceMap: values,
105 };
106 }
107 }
108 if (typeExtensionsMap[typeName]) {
109 typeExtensionsMap[typeName].push({ ...definition, serviceName });
110 }
111 else {
112 typeExtensionsMap[typeName] = [{ ...definition, serviceName }];
113 }
114 }
115 else if ((0, utils_1.isDirectiveDefinitionNode)(definition)) {
116 const directiveName = definition.name.value;
117 const executableLocations = definition.locations.filter(location => utils_1.executableDirectiveLocations.includes(location.value));
118 if (executableLocations.length === 0)
119 continue;
120 const definitionWithExecutableLocations = {
121 ...definition,
122 locations: executableLocations,
123 };
124 if (directiveDefinitionsMap[directiveName]) {
125 directiveDefinitionsMap[directiveName][serviceName] = definitionWithExecutableLocations;
126 }
127 else {
128 directiveDefinitionsMap[directiveName] = {
129 [serviceName]: definitionWithExecutableLocations,
130 };
131 }
132 }
133 }
134 }
135 if (!typeDefinitionsMap.Query)
136 typeDefinitionsMap.Query = [EmptyQueryDefinition];
137 if (typeExtensionsMap.Mutation && !typeDefinitionsMap.Mutation)
138 typeDefinitionsMap.Mutation = [EmptyMutationDefinition];
139 return {
140 typeToServiceMap,
141 typeDefinitionsMap,
142 typeExtensionsMap,
143 directiveDefinitionsMap,
144 externalFields,
145 keyDirectivesMap,
146 valueTypes,
147 directiveMetadata
148 };
149}
150exports.buildMapsFromServiceList = buildMapsFromServiceList;
151function buildSchemaFromDefinitionsAndExtensions({ typeDefinitionsMap, typeExtensionsMap, directiveDefinitionsMap, directiveMetadata, serviceList, }) {
152 let errors = undefined;
153 const autoIncludedDirectiveDefinitions = directives_1.directivesWithAutoIncludedDefinitions.filter((directive) => directiveMetadata.hasUsages(directive.name));
154 const { FieldSetScalar, JoinFieldDirective, JoinTypeDirective, JoinOwnerDirective, JoinGraphEnum, JoinGraphDirective, } = (0, joinSpec_1.getJoinDefinitions)(serviceList);
155 let schema = new graphql_1.GraphQLSchema({
156 query: undefined,
157 directives: [
158 coreSpec_1.CoreDirective,
159 JoinFieldDirective,
160 JoinTypeDirective,
161 JoinOwnerDirective,
162 JoinGraphDirective,
163 ...graphql_1.specifiedDirectives,
164 ...directives_1.directivesWithNoDefinitionNeeded,
165 ...autoIncludedDirectiveDefinitions,
166 ],
167 types: [FieldSetScalar, JoinGraphEnum],
168 });
169 function nodeHasInterfaces(node) {
170 return 'interfaces' in node;
171 }
172 const definitionsDocument = {
173 kind: graphql_1.Kind.DOCUMENT,
174 definitions: [
175 ...Object.values(typeDefinitionsMap).flatMap((typeDefinitions) => {
176 if (!typeDefinitions.some(nodeHasInterfaces))
177 return typeDefinitions;
178 const uniqueInterfaces = typeDefinitions.reduce((map, objectTypeDef) => {
179 var _a;
180 (_a = objectTypeDef.interfaces) === null || _a === void 0 ? void 0 : _a.forEach((iface) => map.set(iface.name.value, iface));
181 return map;
182 }, new Map());
183 if (uniqueInterfaces.size === 0)
184 return typeDefinitions;
185 const [first, ...rest] = typeDefinitions;
186 return [
187 ...rest,
188 {
189 ...first,
190 interfaces: Array.from(uniqueInterfaces.values()),
191 },
192 ];
193 }),
194 ...Object.values(directiveDefinitionsMap).map((definitions) => Object.values(definitions)[0]),
195 ],
196 };
197 errors = (0, validate_1.validateSDL)(definitionsDocument, schema, rules_1.compositionRules);
198 try {
199 schema = (0, graphql_1.extendSchema)(schema, definitionsDocument, {
200 assumeValidSDL: true,
201 });
202 }
203 catch (e) { }
204 const extensionsDocument = {
205 kind: graphql_1.Kind.DOCUMENT,
206 definitions: Object.values(typeExtensionsMap).flat(),
207 };
208 errors.push(...(0, validate_1.validateSDL)(extensionsDocument, schema, rules_1.compositionRules));
209 try {
210 schema = (0, graphql_1.extendSchema)(schema, extensionsDocument, {
211 assumeValidSDL: true,
212 });
213 }
214 catch { }
215 schema = new graphql_1.GraphQLSchema({
216 ...schema.toConfig(),
217 directives: [
218 ...schema
219 .getDirectives()
220 .filter((x) => !(0, directives_1.isDirectiveWithNoDefinitionNeeded)(x)),
221 ],
222 });
223 return { schema, errors };
224}
225exports.buildSchemaFromDefinitionsAndExtensions = buildSchemaFromDefinitionsAndExtensions;
226function addFederationMetadataToSchemaNodes({ schema, typeToServiceMap, externalFields, keyDirectivesMap, valueTypes, directiveDefinitionsMap, directiveMetadata, }) {
227 var _a;
228 for (const [typeName, { owningService, extensionFieldsToOwningServiceMap },] of Object.entries(typeToServiceMap)) {
229 const namedType = schema.getType(typeName);
230 if (!namedType)
231 continue;
232 const isValueType = valueTypes.has(typeName);
233 const serviceName = isValueType ? null : owningService;
234 const federationMetadata = {
235 ...(0, utils_1.getFederationMetadata)(namedType),
236 serviceName,
237 isValueType,
238 ...(keyDirectivesMap[typeName] && {
239 keys: keyDirectivesMap[typeName],
240 }),
241 };
242 namedType.extensions = {
243 ...namedType.extensions,
244 federation: federationMetadata,
245 };
246 if ((0, graphql_1.isObjectType)(namedType)) {
247 for (const field of Object.values(namedType.getFields())) {
248 const [providesDirective] = (0, utils_1.findDirectivesOnNode)(field.astNode, 'provides');
249 if (providesDirective &&
250 providesDirective.arguments &&
251 (0, utils_1.isStringValueNode)(providesDirective.arguments[0].value)) {
252 const fieldFederationMetadata = {
253 ...(0, utils_1.getFederationMetadata)(field),
254 serviceName,
255 provides: (0, utils_1.parseFieldSet)(providesDirective.arguments[0].value.value),
256 belongsToValueType: isValueType,
257 };
258 field.extensions = {
259 ...field.extensions,
260 federation: fieldFederationMetadata,
261 };
262 }
263 }
264 }
265 for (const [fieldName, extendingServiceName] of Object.entries(extensionFieldsToOwningServiceMap)) {
266 if ((0, graphql_1.isObjectType)(namedType)) {
267 const field = namedType.getFields()[fieldName];
268 if (!field)
269 continue;
270 const fieldFederationMetadata = {
271 ...(0, utils_1.getFederationMetadata)(field),
272 serviceName: extendingServiceName,
273 };
274 field.extensions = {
275 ...field.extensions,
276 federation: fieldFederationMetadata,
277 };
278 const [requiresDirective] = (0, utils_1.findDirectivesOnNode)(field.astNode, 'requires');
279 if (requiresDirective &&
280 requiresDirective.arguments &&
281 (0, utils_1.isStringValueNode)(requiresDirective.arguments[0].value)) {
282 const fieldFederationMetadata = {
283 ...(0, utils_1.getFederationMetadata)(field),
284 requires: (0, utils_1.parseFieldSet)(requiresDirective.arguments[0].value.value),
285 };
286 field.extensions = {
287 ...field.extensions,
288 federation: fieldFederationMetadata,
289 };
290 }
291 }
292 }
293 }
294 for (const field of externalFields) {
295 const namedType = schema.getType(field.parentTypeName);
296 if (!namedType)
297 continue;
298 const existingMetadata = (0, utils_1.getFederationMetadata)(namedType);
299 const typeFederationMetadata = {
300 ...existingMetadata,
301 externals: {
302 ...existingMetadata === null || existingMetadata === void 0 ? void 0 : existingMetadata.externals,
303 [field.serviceName]: [
304 ...(((_a = existingMetadata === null || existingMetadata === void 0 ? void 0 : existingMetadata.externals) === null || _a === void 0 ? void 0 : _a[field.serviceName]) || []),
305 field,
306 ],
307 },
308 };
309 namedType.extensions = {
310 ...namedType.extensions,
311 federation: typeFederationMetadata,
312 };
313 }
314 for (const directiveName of Object.keys(directiveDefinitionsMap)) {
315 const directive = schema.getDirective(directiveName);
316 if (!directive)
317 continue;
318 const directiveFederationMetadata = {
319 ...(0, utils_1.getFederationMetadata)(directive),
320 directiveDefinitions: directiveDefinitionsMap[directiveName],
321 };
322 directive.extensions = {
323 ...directive.extensions,
324 federation: directiveFederationMetadata,
325 };
326 }
327 directiveMetadata.applyMetadataToSupergraphSchema(schema);
328}
329exports.addFederationMetadataToSchemaNodes = addFederationMetadataToSchemaNodes;
330function composeServices(services) {
331 const { typeToServiceMap, typeDefinitionsMap, typeExtensionsMap, directiveDefinitionsMap, externalFields, keyDirectivesMap, valueTypes, directiveMetadata, } = buildMapsFromServiceList(services);
332 let { schema, errors } = buildSchemaFromDefinitionsAndExtensions({
333 typeDefinitionsMap,
334 typeExtensionsMap,
335 directiveDefinitionsMap,
336 directiveMetadata,
337 serviceList: services,
338 });
339 schema = new graphql_1.GraphQLSchema({
340 ...schema.toConfig(),
341 ...(0, utilities_1.mapValues)(utils_1.defaultRootOperationNameLookup, typeName => typeName
342 ? schema.getType(typeName)
343 : undefined),
344 extensions: {
345 serviceList: services
346 }
347 });
348 schema = (0, schema_helper_1.transformSchema)(schema, type => {
349 if ((0, graphql_1.isObjectType)(type)) {
350 const config = type.toConfig();
351 return new graphql_1.GraphQLObjectType({
352 ...config,
353 interfaces: Array.from(new Set(config.interfaces)),
354 });
355 }
356 return undefined;
357 });
358 schema = (0, graphql_1.lexicographicSortSchema)(schema);
359 addFederationMetadataToSchemaNodes({
360 schema,
361 typeToServiceMap,
362 externalFields,
363 keyDirectivesMap,
364 valueTypes,
365 directiveDefinitionsMap,
366 directiveMetadata,
367 });
368 const { graphNameToEnumValueName } = (0, joinSpec_1.getJoinDefinitions)(services);
369 if (errors.length > 0) {
370 return { schema, errors };
371 }
372 else {
373 return {
374 schema,
375 supergraphSdl: (0, printSupergraphSdl_1.printSupergraphSdl)(schema, graphNameToEnumValueName),
376 };
377 }
378}
379exports.composeServices = composeServices;
380//# sourceMappingURL=compose.js.map
\No newline at end of file