@graphql-mesh/fusion-composition
Version:
Basic composition utility for Fusion spec
166 lines (165 loc) • 7.09 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.addFederation2DirectivesToSubgraph = addFederation2DirectivesToSubgraph;
exports.importFederationDirectives = importFederationDirectives;
exports.importMeshDirectives = importMeshDirectives;
exports.detectAndAddMeshDirectives = detectAndAddMeshDirectives;
exports.normalizeDirectiveExtensions = normalizeDirectiveExtensions;
exports.convertSubgraphToFederationv2 = convertSubgraphToFederationv2;
const graphql_1 = require("graphql");
const utils_1 = require("@graphql-tools/utils");
function addFederation2DirectivesToSubgraph(subgraph) {
const schemaDirectives = (0, utils_1.getDirectiveExtensions)(subgraph);
const linkDirectives = (schemaDirectives.link ||= []);
if (!linkDirectives.some(linkDirectiveArgs => linkDirectiveArgs.url?.startsWith('https://specs.apollo.dev/link/'))) {
linkDirectives.push({
url: 'https://specs.apollo.dev/link/v1.0',
});
}
if (!linkDirectives.some(linkDirectiveArgs => linkDirectiveArgs.url?.startsWith('https://specs.apollo.dev/federation/'))) {
linkDirectives.push({
url: 'https://specs.apollo.dev/federation/v2.3',
import: [],
});
}
const extensions = (subgraph.extensions ||= {});
extensions.directives = schemaDirectives;
return subgraph;
}
function importFederationDirectives(subgraph, directives) {
const schemaDirectives = (0, utils_1.getDirectiveExtensions)(subgraph);
const linkDirectives = (schemaDirectives.link ||= []);
let importStatement = linkDirectives.find(linkDirectiveArgs => linkDirectiveArgs.url?.startsWith('https://specs.apollo.dev/federation/'));
if (!importStatement) {
importStatement = {
url: 'https://specs.apollo.dev/federation/v2.6',
import: [],
};
linkDirectives.push(importStatement);
}
// v2.0 is not supported so bump to v2.6
if (importStatement.url === 'https://specs.apollo.dev/federation/v2.0') {
importStatement.url = 'https://specs.apollo.dev/federation/v2.6';
}
importStatement.import = [...new Set([...(importStatement.import || []), ...directives])];
const extensions = (subgraph.extensions ||= {});
extensions.directives = schemaDirectives;
return subgraph;
}
function importMeshDirectives(subgraph, directives) {
const schemaDirectives = (0, utils_1.getDirectiveExtensions)(subgraph) || {};
const linkDirectives = (schemaDirectives.link ||= []);
let importStatement = linkDirectives.find(linkDirectiveArgs => linkDirectiveArgs.url?.startsWith('https://the-guild.dev/graphql/mesh/spec/'));
if (!importStatement) {
importStatement = {
url: 'https://the-guild.dev/graphql/mesh/spec/v1.0',
import: [],
};
linkDirectives.push(importStatement);
}
importStatement.import = [...new Set([...importStatement.import, ...directives])];
const composeDirectives = (schemaDirectives.composeDirective = schemaDirectives.composeDirective
? schemaDirectives.composeDirective.filter(Boolean)
: []);
for (const directiveName of directives) {
if (!composeDirectives.some(dir => dir.name === directiveName)) {
composeDirectives.push({
name: directiveName,
});
}
}
const extensions = (subgraph.extensions ||= {});
extensions.directives = schemaDirectives;
subgraph = importFederationDirectives(subgraph, ['@composeDirective']);
return subgraph;
}
const FEDERATION_V1_DIRECTIVES = [
'@key',
'@provides',
'@requires',
'@external',
'@inaccessible',
'@shareable',
'@extends',
'@tag',
];
function detectAndAddMeshDirectives(subgraph) {
const meshDirectives = [];
const existingAdditionalDirectives = [];
const existingDirectiveExtensionsOnSchema = (0, utils_1.getDirectiveExtensions)(subgraph);
if (existingDirectiveExtensionsOnSchema?.link) {
const linkDirectives = existingDirectiveExtensionsOnSchema.link;
for (const linkDirective of linkDirectives) {
if (linkDirective.url != null &&
!linkDirective.url.startsWith('https://the-guild.dev/graphql/mesh/spec/')) {
if (linkDirective.import) {
existingAdditionalDirectives.push(...linkDirective.import);
}
}
}
}
subgraph = (0, utils_1.mapSchema)(subgraph, {
[utils_1.MapperKind.DIRECTIVE]: directive => {
const directiveName = `@${directive.name}`;
if (!(0, graphql_1.isSpecifiedDirective)(directive) &&
!FEDERATION_V1_DIRECTIVES.includes(directiveName) &&
!directiveName.startsWith('@federation__') &&
directiveName !== '@stream' &&
directiveName !== '@link' &&
!existingAdditionalDirectives.includes(directiveName)) {
meshDirectives.push(directiveName);
if (!directive.isRepeatable && directive.args.some(arg => arg.name === 'subgraph')) {
return new graphql_1.GraphQLDirective({
...directive.toConfig(),
isRepeatable: true,
});
}
}
return directive;
},
});
if (meshDirectives.length > 0) {
subgraph = importMeshDirectives(subgraph, meshDirectives);
}
return subgraph;
}
function normalizeDirectiveExtensions(subgraph) {
return new graphql_1.GraphQLSchema({
...subgraph.toConfig(),
extensions: {
...subgraph.extensions,
directives: (0, utils_1.getDirectiveExtensions)(subgraph),
},
astNode: undefined,
extensionASTNodes: undefined,
});
}
function convertSubgraphToFederationv2(subgraph) {
const schemaDirectives = (0, utils_1.getDirectiveExtensions)(subgraph);
if (schemaDirectives?.link?.some(linkDirectiveArgs => linkDirectiveArgs.url?.startsWith('https://specs.apollo.dev/'))) {
// This is already v2, skipping
return subgraph;
}
subgraph = addFederation2DirectivesToSubgraph(subgraph);
// Add @shareable
subgraph = (0, utils_1.mapSchema)(subgraph, {
[utils_1.MapperKind.OBJECT_TYPE]: type => {
const typeDirectives = (0, utils_1.getDirectiveExtensions)(type);
typeDirectives.shareable ||= [];
if (!typeDirectives.shareable?.length) {
const typeExtensions = (type.extensions ||= {});
typeExtensions.directives ||= {};
typeExtensions.directives.shareable = [{}];
}
return type;
},
[utils_1.MapperKind.INTERFACE_TYPE]: type => {
if (type?.extensions?.directives?.shareable) {
delete type.extensions.directives.shareable;
}
return type;
},
});
subgraph = importFederationDirectives(subgraph, FEDERATION_V1_DIRECTIVES);
return subgraph;
}
;