@theguild/federation-composition
Version:
Open Source Composition library for Apollo Federation
400 lines (399 loc) • 19.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFederationVersion = isFederationVersion;
exports.createSpecSchema = createSpecSchema;
exports.getLatestFederationVersion = getLatestFederationVersion;
exports.isFederationLink = isFederationLink;
exports.detectFederationVersion = detectFederationVersion;
const graphql_1 = require("graphql");
const printer_js_1 = require("../graphql/printer.js");
const inaccessible_js_1 = require("./inaccessible.js");
const link_js_1 = require("./link.js");
const tag_js_1 = require("./tag.js");
function isFederationVersion(version) {
return version in federationSpecFactory;
}
function createSpecSchema(version, imports) {
if (!isFederationVersion(version)) {
throw new graphql_1.GraphQLError(`Invalid version ${version} for the federation feature in directive on schema`, {
extensions: {
code: 'UNKNOWN_FEDERATION_LINK_VERSION',
},
});
}
if (version !== 'v1.0') {
const spec = federationSpecFactory[version]('', imports);
const namespacedSpec = federationSpecFactory[version]('federation__');
return {
directives: spec.directives.concat(namespacedSpec.directives),
types: spec.types.concat(namespacedSpec.types),
};
}
const spec = federationSpecFactory[version]('');
return {
directives: spec.directives.concat([tag_js_1.directive, inaccessible_js_1.directive]),
types: spec.types,
};
}
const federationSpecFactory = {
'v1.0': (prefix) => createTypeDefinitions(`
directive (
fields: _FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive (fields: _FieldSet!) on FIELD_DEFINITION
directive (fields: _FieldSet!) on FIELD_DEFINITION
directive on OBJECT | FIELD_DEFINITION
directive on OBJECT | INTERFACE
# is not supported in v1 but somehow it's supported by Apollo Composition
directive (from: String!) on FIELD_DEFINITION
scalar _FieldSet
`, prefix),
'v2.0': (prefix, imports) => createTypeDefinitions(`
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive on OBJECT | FIELD_DEFINITION
directive on FIELD_DEFINITION | OBJECT
directive on OBJECT | INTERFACE
directive (from: String!) on FIELD_DEFINITION
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
`, prefix, imports),
'v2.1': (prefix, imports) => createTypeDefinitions(`
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
`, prefix, imports),
'v2.2': (prefix, imports) => createTypeDefinitions(`
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
`, prefix, imports),
'v2.3': (prefix, imports) => createTypeDefinitions(`
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
`, prefix, imports),
'v2.4': (prefix, imports) => createTypeDefinitions(`
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
`, prefix, imports),
'v2.5': (prefix, imports) => createTypeDefinitions(`
directive on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (
scopes: [[Scope!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
scalar Scope
`, prefix, imports),
'v2.6': (prefix, imports) => createTypeDefinitions(`
directive (
policies: [[federation__Policy!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (
scopes: [[federation__Scope!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
scalar federation__Policy
scalar federation__Scope
`, prefix, imports),
'v2.7': (prefix, imports) => createTypeDefinitions(`
directive (
policies: [[federation__Policy!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (
scopes: [[federation__Scope!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
scalar FieldSet
scalar federation__Policy
scalar federation__Scope
`, prefix, imports),
'v2.8': (prefix, imports) => createTypeDefinitions(`
directive (
policies: [[federation__Policy!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (
scopes: [[federation__Scope!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive (
weight: Int!
) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR
directive (
assumedSize: Int
slicingArguments: [String!]
sizedFields: [String!]
requireOneSlicingArgument: Boolean = true
) on FIELD_DEFINITION
directive (name: String!) repeatable on INTERFACE | OBJECT | UNION
directive (field: federation__ContextFieldValue) on ARGUMENT_DEFINITION
scalar FieldSet
scalar federation__Policy
scalar federation__Scope
scalar federation__ContextFieldValue
`, prefix, imports),
'v2.9': (prefix, imports) => createTypeDefinitions(`
directive (
policies: [[federation__Policy!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (
scopes: [[federation__Scope!]!]!
) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive (name: String!) repeatable on SCHEMA
directive on OBJECT | INTERFACE
directive on OBJECT | FIELD_DEFINITION
directive (
fields: FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ENUM | ENUM_VALUE | SCALAR | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ARGUMENT_DEFINITION
directive on OBJECT
directive (from: String!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive (fields: FieldSet!) on FIELD_DEFINITION
directive repeatable on FIELD_DEFINITION | OBJECT
directive (
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive (
weight: Int!
) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR
directive (
assumedSize: Int
slicingArguments: [String!]
sizedFields: [String!]
requireOneSlicingArgument: Boolean = true
) on FIELD_DEFINITION
directive (name: String!) repeatable on INTERFACE | OBJECT | UNION
directive (field: federation__ContextFieldValue) on ARGUMENT_DEFINITION
scalar FieldSet
scalar federation__Policy
scalar federation__Scope
scalar federation__ContextFieldValue
`, prefix, imports),
};
function getLatestFederationVersion() {
return Object.keys(federationSpecFactory).sort().pop();
}
function createTypeDefinitions(doc, prefix, imports) {
const shouldFilter = !!imports;
const toInclude = new Set(imports?.map(i => i.name.replace(/^@/, '')));
const docAST = (0, graphql_1.parse)(doc, {
noLocation: true,
});
if (!imports?.length ||
toInclude.has('key') ||
toInclude.has('requires') ||
toInclude.has('provides')) {
toInclude.add('FieldSet');
toInclude.add('federation__FieldSet');
}
if (toInclude.has('requiresScopes')) {
toInclude.add('federation__Scope');
}
if (toInclude.has('policy')) {
toInclude.add('federation__Policy');
}
const directives = [];
const types = [];
for (const node of docAST.definitions) {
if (isDirectiveDefinitionNode(node)) {
directives.push(applyPrefix(node, prefix));
}
else {
types.push(applyPrefix(node, prefix));
}
}
return {
directives: directives.filter(d => !graphql_1.specifiedDirectives.some(sd => sd.name === d.name.value) &&
(!shouldFilter || toInclude.has(d.name.value))),
types: types.filter(t => toInclude.has(t.name.value)),
};
}
function isDirectiveDefinitionNode(node) {
return node.kind === graphql_1.Kind.DIRECTIVE_DEFINITION;
}
function applyPrefix(node, prefix) {
if (prefix.length === 0) {
return node;
}
node.name.value = `${prefix}${node.name.value}`;
if (isDirectiveDefinitionNode(node)) {
node.arguments?.forEach(arg => {
const nameNode = resolveNamedType(arg.type);
if (!graphql_1.specifiedScalarTypes.some(t => t.name === nameNode.value)) {
nameNode.value = `${prefix}${nameNode.value}`;
}
});
}
return node;
}
function resolveNamedType(node) {
if (node.kind === graphql_1.Kind.LIST_TYPE) {
return resolveNamedType(node.type);
}
if (node.kind === graphql_1.Kind.NON_NULL_TYPE) {
return resolveNamedType(node.type);
}
return node.name;
}
function isFederationLink(link) {
return link.identity === 'https://specs.apollo.dev/federation';
}
function detectFederationVersion(typeDefs) {
for (const definition of typeDefs.definitions) {
if (definition.kind === graphql_1.Kind.SCHEMA_EXTENSION || definition.kind === graphql_1.Kind.SCHEMA_DEFINITION) {
const links = definition.directives?.filter(directive => directive.name.value === 'link');
if (links?.length) {
const parsedLinks = links.map(l => {
const url = l.arguments?.find(a => a.name.value === 'url');
const importArg = l.arguments?.find(a => a.name.value === 'import');
if (!url) {
throw new Error('Invalid directive');
}
return (0, link_js_1.parseLink)(url.value.value, importArg ? (0, printer_js_1.print)(importArg.value) : '[]');
});
const fedLink = parsedLinks.find(l => l.identity === 'https://specs.apollo.dev/federation');
if (fedLink?.version) {
if (!isFederationVersion(fedLink.version)) {
throw new Error(`Unsupported federation version: ${fedLink.version}`);
}
return {
version: fedLink.version,
imports: fedLink.imports,
};
}
}
}
}
return { version: 'v1.0', imports: [] };
}