UNPKG

@theguild/federation-composition

Version:
130 lines (129 loc) 6.19 kB
import { Kind, specifiedDirectives as specifiedDirectivesArray, visit, } from 'graphql'; import { extractLinkImplementations } from '../utils/link/index.js'; import { extraFederationDirectiveNames, extraFederationTypeNames, getSupergraphSpecNodes, } from './supergraph-spec.js'; const specifiedDirectives = new Set(specifiedDirectivesArray.map(d => d.name)); function getAdditionalDirectivesToStrip(documentNode) { const schemaDefinitionNode = documentNode.definitions.find((node) => node.kind === Kind.SCHEMA_DEFINITION); if (!schemaDefinitionNode?.directives?.length) { return null; } const additionalDirectivesToStrip = new Set(); for (const directive of schemaDefinitionNode.directives) { if (directive.name.value !== 'link') { continue; } const asArg = directive.arguments?.find(arg => arg.name.value === 'as'); if (asArg?.value.kind === Kind.STRING) { additionalDirectivesToStrip.add(asArg.value.value); } } return additionalDirectivesToStrip; } const federationInaccessibleDirectiveUrlPrefix = 'https://specs.apollo.dev/inaccessible'; function getInaccessibleDirectiveName(documentNode) { const schemaDefinitionNode = documentNode.definitions.find((node) => node.kind === Kind.SCHEMA_DEFINITION); if (schemaDefinitionNode?.directives?.length) { for (const directive of schemaDefinitionNode.directives) { if (directive.name.value !== 'link') { continue; } const urlArg = directive.arguments?.find(arg => arg.name.value === 'url'); const asArg = directive.arguments?.find(arg => arg.name.value === 'as'); if (urlArg?.value.kind === Kind.STRING && urlArg.value.value.startsWith(federationInaccessibleDirectiveUrlPrefix)) { if (asArg?.value.kind === Kind.STRING) { return asArg.value.value; } break; } } } return 'inaccessible'; } export function transformSupergraphToPublicSchema(documentNode) { const additionalFederationDirectives = getAdditionalDirectivesToStrip(documentNode); const inaccessibleDirectiveName = getInaccessibleDirectiveName(documentNode); const specLinks = extractLinkImplementations(documentNode).links.filter(link => link.identity.includes('//specs.apollo.dev/')); const specPrefixes = specLinks.map(l => l.identity.substring(l.identity.lastIndexOf('/') + 1) + '__'); const supergraphSpecNodeNames = getSupergraphSpecNodes(); const supergraphDirectives = new Set(); const supergraphEnums = new Set(); const supergraphInputObjects = new Set(); const supergraphScalars = new Set(); for (const { kind, name } of supergraphSpecNodeNames) { if (kind === Kind.ENUM_TYPE_DEFINITION) { supergraphEnums.add(name); } else if (kind === Kind.INPUT_OBJECT_TYPE_DEFINITION) { supergraphInputObjects.add(name); } else if (kind === Kind.SCALAR_TYPE_DEFINITION) { supergraphScalars.add(name); } else if (kind === Kind.DIRECTIVE_DEFINITION) { supergraphDirectives.add(name); } } const importedPieces = specLinks.map(l => l.imports.map(i => i.as ?? i.name)).flat(); const definitionsToRemove = new Set(importedPieces.map(name => name.replace('@', ''))); const directivesToRemove = new Set(importedPieces.filter(name => name.startsWith('@')).map(name => name.substring(1))); function removeFederationOrSpecifiedDirectives(node) { if (belongsToLinkedSpec(node) || supergraphDirectives.has(node.name.value) || extraFederationDirectiveNames.has(node.name.value) || additionalFederationDirectives?.has(node.name.value) || directivesToRemove.has(node.name.value) || (node.kind === Kind.DIRECTIVE_DEFINITION && specifiedDirectives.has(node.name.value))) { return null; } } function belongsToLinkedSpec(node) { return specPrefixes.some(prefix => node.name.value.startsWith(prefix)); } function hasInaccessibleDirective(node) { return node.directives?.some(d => d.name.value === inaccessibleDirectiveName); } function removeInaccessibleNode(node) { if (hasInaccessibleDirective(node) || belongsToLinkedSpec(node)) { return null; } } return visit(documentNode, { [Kind.DIRECTIVE_DEFINITION]: removeFederationOrSpecifiedDirectives, [Kind.DIRECTIVE]: removeFederationOrSpecifiedDirectives, [Kind.SCHEMA_EXTENSION]: () => null, [Kind.SCHEMA_DEFINITION]: () => null, [Kind.SCALAR_TYPE_DEFINITION](node) { if (belongsToLinkedSpec(node) || supergraphScalars.has(node.name.value) || extraFederationTypeNames.has(node.name.value) || definitionsToRemove.has(node.name.value) || hasInaccessibleDirective(node)) { return null; } }, [Kind.ENUM_TYPE_DEFINITION](node) { if (belongsToLinkedSpec(node) || supergraphEnums.has(node.name.value) || extraFederationTypeNames.has(node.name.value) || definitionsToRemove.has(node.name.value) || hasInaccessibleDirective(node)) { return null; } }, [Kind.ENUM_VALUE_DEFINITION]: removeInaccessibleNode, [Kind.OBJECT_TYPE_DEFINITION]: removeInaccessibleNode, [Kind.FIELD_DEFINITION]: removeInaccessibleNode, [Kind.INTERFACE_TYPE_DEFINITION]: removeInaccessibleNode, [Kind.UNION_TYPE_DEFINITION]: removeInaccessibleNode, [Kind.INPUT_OBJECT_TYPE_DEFINITION](node) { if (belongsToLinkedSpec(node) || supergraphInputObjects.has(node.name.value) || definitionsToRemove.has(node.name.value) || hasInaccessibleDirective(node)) { return null; } }, [Kind.INPUT_VALUE_DEFINITION]: removeInaccessibleNode, }); }