@theguild/federation-composition
Version:
Open Source Composition library for Apollo Federation
109 lines (108 loc) • 4.39 kB
JavaScript
import { Kind, specifiedDirectives as specifiedDirectivesArray, visit, } from 'graphql';
export const federationScalars = new Set([
'_FieldSet',
'link__Import',
'join__FieldSet',
'join__DirectiveArguments',
'policy__Policy',
'requiresScopes__Scope',
]);
export const federationEnums = new Set(['core__Purpose', 'join__Graph', 'link__Purpose']);
export const federationDirectives = new Set([
'link',
'core',
'tag',
'join__graph',
'join__type',
'join__owner',
'join__implements',
'join__unionMember',
'join__directive',
'join__enumValue',
'join__field',
'inaccessible',
'authenticated',
'policy',
'requiresScopes',
]);
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);
function removeFederationOrSpecifiedDirectives(node) {
if (federationDirectives.has(node.name.value) ||
additionalFederationDirectives?.has(node.name.value) ||
(node.kind === Kind.DIRECTIVE_DEFINITION && specifiedDirectives.has(node.name.value))) {
return null;
}
}
function hasInaccessibleDirective(node) {
return node.directives?.some(d => d.name.value === inaccessibleDirectiveName);
}
function removeInaccessibleNode(node) {
if (hasInaccessibleDirective(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 (federationScalars.has(node.name.value) || hasInaccessibleDirective(node)) {
return null;
}
},
[Kind.ENUM_TYPE_DEFINITION](node) {
if (federationEnums.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]: removeInaccessibleNode,
[Kind.INPUT_VALUE_DEFINITION]: removeInaccessibleNode,
});
}