UNPKG

6.04 kBJavaScriptView Raw
1"use strict";
2// Taken from https://github.com/gmac/federation-to-stitching-sdl/blob/main/index.js
3Object.defineProperty(exports, "__esModule", { value: true });
4exports.federationToStitchingSDL = void 0;
5const graphql_1 = require("graphql");
6const stitchingDirectives_js_1 = require("./stitchingDirectives.js");
7const extensionKind = /Extension$/;
8const entityKinds = [
9 graphql_1.Kind.OBJECT_TYPE_DEFINITION,
10 graphql_1.Kind.OBJECT_TYPE_EXTENSION,
11 graphql_1.Kind.INTERFACE_TYPE_DEFINITION,
12 graphql_1.Kind.INTERFACE_TYPE_EXTENSION,
13];
14function isEntityKind(def) {
15 return entityKinds.includes(def.kind);
16}
17function getQueryTypeDef(definitions) {
18 var _a;
19 const schemaDef = definitions.find(def => def.kind === graphql_1.Kind.SCHEMA_DEFINITION);
20 const typeName = schemaDef
21 ? (_a = schemaDef.operationTypes.find(({ operation }) => operation === 'query')) === null || _a === void 0 ? void 0 : _a.type.name.value
22 : 'Query';
23 return definitions.find(def => def.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION && def.name.value === typeName);
24}
25// Federation services are actually fairly complex,
26// as the `buildFederatedSchema` helper does a fair amount
27// of hidden work to setup the Federation schema specification:
28// https://www.apollographql.com/docs/federation/federation-spec/#federation-schema-specification
29function federationToStitchingSDL(federationSDL, stitchingConfig = (0, stitchingDirectives_js_1.stitchingDirectives)()) {
30 const doc = (0, graphql_1.parse)(federationSDL);
31 const entityTypes = [];
32 const baseTypeNames = doc.definitions.reduce((memo, typeDef) => {
33 if (!extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name) {
34 memo[typeDef.name.value] = true;
35 }
36 return memo;
37 }, {});
38 doc.definitions.forEach(typeDef => {
39 var _a, _b, _c;
40 // Un-extend all types (remove "extends" keywords)...
41 // extended types are invalid GraphQL without a local base type to extend from.
42 // Stitching merges flat types in lieu of hierarchical extensions.
43 if (extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name && !baseTypeNames[typeDef.name.value]) {
44 typeDef.kind = typeDef.kind.replace(extensionKind, 'Definition');
45 }
46 if (!isEntityKind(typeDef))
47 return;
48 // Find object definitions with "@key" directives;
49 // these are federated entities that get turned into merged types.
50 const keyDirs = [];
51 const otherDirs = [];
52 (_a = typeDef.directives) === null || _a === void 0 ? void 0 : _a.forEach(dir => {
53 if (dir.name.value === 'key') {
54 keyDirs.push(dir);
55 }
56 else {
57 otherDirs.push(dir);
58 }
59 });
60 if (!keyDirs.length)
61 return;
62 // Setup stitching MergedTypeConfig for all federated entities:
63 const selectionSet = `{ ${keyDirs.map((dir) => dir.arguments[0].value.value).join(' ')} }`;
64 const keyFields = (0, graphql_1.parse)(selectionSet).definitions[0].selectionSet.selections.map((sel) => sel.name.value);
65 const keyDir = keyDirs[0];
66 keyDir.name.value = stitchingConfig.keyDirective.name;
67 keyDir.arguments[0].name.value = 'selectionSet';
68 keyDir.arguments[0].value.value = selectionSet;
69 typeDef.directives = [keyDir, ...otherDirs];
70 // Remove non-key "@external" fields from the type...
71 // the stitching query planner expects services to only publish their own fields.
72 // This makes "@provides" moot because the query planner can automate the logic.
73 typeDef.fields = (_b = typeDef.fields) === null || _b === void 0 ? void 0 : _b.filter(fieldDef => {
74 var _a;
75 return (keyFields.includes(fieldDef.name.value) || !((_a = fieldDef.directives) === null || _a === void 0 ? void 0 : _a.find(dir => dir.name.value === 'external')));
76 });
77 // Discard remaining "@external" directives and any "@provides" directives
78 (_c = typeDef.fields) === null || _c === void 0 ? void 0 : _c.forEach((fieldDef) => {
79 fieldDef.directives = fieldDef.directives.filter((dir) => !/^(external|provides)$/.test(dir.name.value));
80 fieldDef.directives.forEach((dir) => {
81 if (dir.name.value === 'requires') {
82 dir.name.value = stitchingConfig.computedDirective.name;
83 dir.arguments[0].name.value = 'selectionSet';
84 dir.arguments[0].value.value = `{ ${dir.arguments[0].value.value} }`;
85 }
86 });
87 });
88 if (typeDef.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
89 entityTypes.push(typeDef.name.value);
90 }
91 });
92 // Federation service SDLs are incomplete because they omit the federation spec itself...
93 // (https://www.apollographql.com/docs/federation/federation-spec/#federation-schema-specification)
94 // To make federation SDLs into valid and parsable GraphQL schemas,
95 // we must fill in the missing details from the specification.
96 if (entityTypes.length) {
97 const queryDef = getQueryTypeDef(doc.definitions);
98 const entitiesSchema = (0, graphql_1.parse)(/* GraphQL */ `
99 scalar _Any
100 union _Entity = ${entityTypes.filter((v, i, a) => a.indexOf(v) === i).join(' | ')}
101 type Query { _entities(representations: [_Any!]!): [_Entity]! @${stitchingConfig.mergeDirective.name} }
102 `).definitions;
103 doc.definitions.push(entitiesSchema[0]);
104 doc.definitions.push(entitiesSchema[1]);
105 if (queryDef) {
106 queryDef.fields.push(entitiesSchema[2].fields[0]);
107 }
108 else {
109 doc.definitions.push(entitiesSchema[2]);
110 }
111 }
112 return [stitchingConfig.stitchingDirectivesTypeDefs, (0, graphql_1.print)(doc)].join('\n');
113}
114exports.federationToStitchingSDL = federationToStitchingSDL;