UNPKG

@graphql-mesh/fusion-composition

Version:

Basic composition utility for Fusion spec

166 lines (165 loc) • 7.09 kB
"use strict"; 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; }