1 |
|
2 | import { print, Kind, parse, } from 'graphql';
|
3 | import { stitchingDirectives } from './stitchingDirectives.js';
|
4 | const extensionKind = /Extension$/;
|
5 | const entityKinds = [
|
6 | Kind.OBJECT_TYPE_DEFINITION,
|
7 | Kind.OBJECT_TYPE_EXTENSION,
|
8 | Kind.INTERFACE_TYPE_DEFINITION,
|
9 | Kind.INTERFACE_TYPE_EXTENSION,
|
10 | ];
|
11 | function isEntityKind(def) {
|
12 | return entityKinds.includes(def.kind);
|
13 | }
|
14 | function getQueryTypeDef(definitions) {
|
15 | var _a;
|
16 | const schemaDef = definitions.find(def => def.kind === Kind.SCHEMA_DEFINITION);
|
17 | const typeName = schemaDef
|
18 | ? (_a = schemaDef.operationTypes.find(({ operation }) => operation === 'query')) === null || _a === void 0 ? void 0 : _a.type.name.value
|
19 | : 'Query';
|
20 | return definitions.find(def => def.kind === Kind.OBJECT_TYPE_DEFINITION && def.name.value === typeName);
|
21 | }
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | export function federationToStitchingSDL(federationSDL, stitchingConfig = stitchingDirectives()) {
|
27 | const doc = parse(federationSDL);
|
28 | const entityTypes = [];
|
29 | const baseTypeNames = doc.definitions.reduce((memo, typeDef) => {
|
30 | if (!extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name) {
|
31 | memo[typeDef.name.value] = true;
|
32 | }
|
33 | return memo;
|
34 | }, {});
|
35 | doc.definitions.forEach(typeDef => {
|
36 | var _a, _b, _c;
|
37 |
|
38 |
|
39 |
|
40 | if (extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name && !baseTypeNames[typeDef.name.value]) {
|
41 | typeDef.kind = typeDef.kind.replace(extensionKind, 'Definition');
|
42 | }
|
43 | if (!isEntityKind(typeDef))
|
44 | return;
|
45 |
|
46 |
|
47 | const keyDirs = [];
|
48 | const otherDirs = [];
|
49 | (_a = typeDef.directives) === null || _a === void 0 ? void 0 : _a.forEach(dir => {
|
50 | if (dir.name.value === 'key') {
|
51 | keyDirs.push(dir);
|
52 | }
|
53 | else {
|
54 | otherDirs.push(dir);
|
55 | }
|
56 | });
|
57 | if (!keyDirs.length)
|
58 | return;
|
59 |
|
60 | const selectionSet = `{ ${keyDirs.map((dir) => dir.arguments[0].value.value).join(' ')} }`;
|
61 | const keyFields = parse(selectionSet).definitions[0].selectionSet.selections.map((sel) => sel.name.value);
|
62 | const keyDir = keyDirs[0];
|
63 | keyDir.name.value = stitchingConfig.keyDirective.name;
|
64 | keyDir.arguments[0].name.value = 'selectionSet';
|
65 | keyDir.arguments[0].value.value = selectionSet;
|
66 | typeDef.directives = [keyDir, ...otherDirs];
|
67 |
|
68 |
|
69 |
|
70 | typeDef.fields = (_b = typeDef.fields) === null || _b === void 0 ? void 0 : _b.filter(fieldDef => {
|
71 | var _a;
|
72 | return (keyFields.includes(fieldDef.name.value) || !((_a = fieldDef.directives) === null || _a === void 0 ? void 0 : _a.find(dir => dir.name.value === 'external')));
|
73 | });
|
74 |
|
75 | (_c = typeDef.fields) === null || _c === void 0 ? void 0 : _c.forEach((fieldDef) => {
|
76 | fieldDef.directives = fieldDef.directives.filter((dir) => !/^(external|provides)$/.test(dir.name.value));
|
77 | fieldDef.directives.forEach((dir) => {
|
78 | if (dir.name.value === 'requires') {
|
79 | dir.name.value = stitchingConfig.computedDirective.name;
|
80 | dir.arguments[0].name.value = 'selectionSet';
|
81 | dir.arguments[0].value.value = `{ ${dir.arguments[0].value.value} }`;
|
82 | }
|
83 | });
|
84 | });
|
85 | if (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
|
86 | entityTypes.push(typeDef.name.value);
|
87 | }
|
88 | });
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | if (entityTypes.length) {
|
94 | const queryDef = getQueryTypeDef(doc.definitions);
|
95 | const entitiesSchema = parse( `
|
96 | scalar _Any
|
97 | union _Entity = ${entityTypes.filter((v, i, a) => a.indexOf(v) === i).join(' | ')}
|
98 | type Query { _entities(representations: [_Any!]!): [_Entity]! @${stitchingConfig.mergeDirective.name} }
|
99 | `).definitions;
|
100 | doc.definitions.push(entitiesSchema[0]);
|
101 | doc.definitions.push(entitiesSchema[1]);
|
102 | if (queryDef) {
|
103 | queryDef.fields.push(entitiesSchema[2].fields[0]);
|
104 | }
|
105 | else {
|
106 | doc.definitions.push(entitiesSchema[2]);
|
107 | }
|
108 | }
|
109 | return [stitchingConfig.stitchingDirectivesTypeDefs, print(doc)].join('\n');
|
110 | }
|