@theguild/federation-composition
Version:
Open Source Composition library for Apollo Federation
300 lines (299 loc) • 12.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.interfaceTypeBuilder = interfaceTypeBuilder;
const ast_js_1 = require("./ast.js");
const common_js_1 = require("./common.js");
function interfaceTypeBuilder() {
return {
visitSubgraphState(graph, state, typeName, type) {
const interfaceTypeState = getOrCreateInterfaceType(state, typeName);
type.tags.forEach(tag => interfaceTypeState.tags.add(tag));
if (type.inaccessible) {
interfaceTypeState.inaccessible = true;
}
if (type.authenticated) {
interfaceTypeState.authenticated = true;
}
if (type.policies) {
interfaceTypeState.policies.push(...type.policies);
}
if (type.scopes) {
interfaceTypeState.scopes.push(...type.scopes);
}
if (type.isDefinition) {
interfaceTypeState.hasDefinition = true;
}
if (type.isInterfaceObject) {
interfaceTypeState.hasInterfaceObject = true;
}
if (type.description && !interfaceTypeState.description) {
interfaceTypeState.description = type.description;
}
type.ast.directives.forEach(directive => {
interfaceTypeState.ast.directives.push(directive);
});
type.interfaces.forEach(interfaceName => interfaceTypeState.interfaces.add(interfaceName));
type.implementedBy.forEach(objectTypeName => interfaceTypeState.implementedBy.add(objectTypeName));
if (type.keys.length) {
interfaceTypeState.isEntity = true;
}
interfaceTypeState.byGraph.set(graph.id, {
extension: type.extension,
keys: type.keys,
interfaces: type.interfaces,
implementedBy: type.implementedBy,
isInterfaceObject: type.isInterfaceObject,
version: graph.version,
});
for (const field of type.fields.values()) {
const fieldState = getOrCreateInterfaceField(interfaceTypeState, field.name, field.type);
field.tags.forEach(tag => fieldState.tags.add(tag));
if (!field.type.endsWith('!') && fieldState.type.endsWith('!')) {
fieldState.type = field.type;
}
if (field.isLeaf) {
fieldState.isLeaf = true;
}
if (field.inaccessible) {
fieldState.inaccessible = true;
}
if (field.authenticated) {
fieldState.authenticated = true;
}
if (field.policies) {
fieldState.policies.push(...field.policies);
}
if (field.scopes) {
fieldState.scopes.push(...field.scopes);
}
if (field.deprecated && !fieldState.deprecated) {
fieldState.deprecated = field.deprecated;
}
if (field.description && !fieldState.description) {
fieldState.description = field.description;
}
field.ast.directives.forEach(directive => {
fieldState.ast.directives.push(directive);
});
const usedAsKey = type.fieldsUsedAsKeys.has(field.name);
if (usedAsKey) {
fieldState.usedAsKey = true;
}
fieldState.byGraph.set(graph.id, {
type: field.type,
override: field.override,
provides: field.provides,
requires: field.requires,
version: graph.version,
external: field.external,
shareable: field.shareable,
usedAsKey,
});
for (const arg of field.args.values()) {
const argState = getOrCreateArg(fieldState, arg.name, arg.type, arg.kind);
arg.tags.forEach(tag => argState.tags.add(tag));
if (arg.type.endsWith('!')) {
argState.type = arg.type;
}
if (arg.deprecated && !argState.deprecated) {
argState.deprecated = arg.deprecated;
}
if (arg.description && !argState.description) {
argState.description = arg.description;
}
if (typeof arg.defaultValue !== 'undefined') {
argState.defaultValue = arg.defaultValue;
}
arg.ast.directives.forEach(directive => {
argState.ast.directives.push(directive);
});
argState.kind = arg.kind;
argState.byGraph.set(graph.id, {
type: arg.type,
kind: arg.kind,
defaultValue: arg.defaultValue,
version: graph.version,
});
}
}
},
composeSupergraphNode(interfaceType, graphs) {
return (0, ast_js_1.createInterfaceTypeNode)({
name: interfaceType.name,
fields: Array.from(interfaceType.fields.values()).map(field => {
let nonEmptyJoinField = false;
const joinFields = [];
if (field.byGraph.size !== interfaceType.byGraph.size) {
for (const [graphId, meta] of field.byGraph.entries()) {
if (meta.type !== field.type ||
meta.override ||
meta.provides ||
meta.requires ||
meta.external) {
nonEmptyJoinField = true;
}
joinFields.push({
graph: graphId,
type: meta.type === field.type ? undefined : meta.type,
override: meta.override ?? undefined,
provides: meta.provides ?? undefined,
requires: meta.requires ?? undefined,
external: meta.external,
});
}
}
return {
name: field.name,
type: field.type,
inaccessible: field.inaccessible,
authenticated: field.authenticated,
policies: field.policies,
scopes: field.scopes,
tags: Array.from(field.tags),
deprecated: field.deprecated,
description: field.description,
ast: {
directives: (0, common_js_1.convertToConst)(field.ast.directives),
},
arguments: Array.from(field.args.values())
.filter(arg => {
if (arg.byGraph.size !== field.byGraph.size) {
return false;
}
return true;
})
.map(arg => {
return {
name: arg.name,
type: arg.type,
kind: arg.kind,
tags: Array.from(arg.tags),
defaultValue: arg.defaultValue,
deprecated: arg.deprecated,
description: arg.description,
ast: {
directives: (0, common_js_1.convertToConst)(arg.ast.directives),
},
};
}),
join: {
field: joinFields,
},
};
}),
tags: Array.from(interfaceType.tags),
inaccessible: interfaceType.inaccessible,
authenticated: interfaceType.authenticated,
policies: interfaceType.policies,
scopes: interfaceType.scopes,
description: interfaceType.description,
interfaces: Array.from(interfaceType.interfaces),
ast: {
directives: (0, common_js_1.convertToConst)(interfaceType.ast.directives),
},
join: {
type: Array.from(interfaceType.byGraph)
.map(([graphId, meta]) => {
if (meta.keys.length && graphs.get(graphId).version !== 'v1.0') {
return meta.keys.map(key => ({
graph: graphId,
key: key.fields,
extension: meta.extension,
isInterfaceObject: meta.isInterfaceObject,
resolvable: key.resolvable,
}));
}
return [
{
graph: graphId,
},
];
})
.flat(1),
implements: interfaceType.interfaces.size > 0
? Array.from(interfaceType.byGraph.entries())
.map(([graphId, meta]) => {
if (meta.interfaces.size) {
return Array.from(meta.interfaces).map(iface => ({
graph: graphId,
interface: iface,
}));
}
return [];
})
.flat(1)
: [],
},
});
},
};
}
function getOrCreateInterfaceType(state, typeName) {
const existing = state.get(typeName);
if (existing) {
return existing;
}
const def = {
kind: 'interface',
name: typeName,
tags: new Set(),
inaccessible: false,
authenticated: false,
policies: [],
scopes: [],
hasDefinition: false,
hasInterfaceObject: false,
isEntity: false,
byGraph: new Map(),
fields: new Map(),
interfaces: new Set(),
implementedBy: new Set(),
ast: {
directives: [],
},
};
state.set(typeName, def);
return def;
}
function getOrCreateInterfaceField(interfaceTypeState, fieldName, fieldType) {
const existing = interfaceTypeState.fields.get(fieldName);
if (existing) {
return existing;
}
const def = {
name: fieldName,
type: fieldType,
isLeaf: false,
usedAsKey: false,
tags: new Set(),
inaccessible: false,
authenticated: false,
policies: [],
scopes: [],
byGraph: new Map(),
args: new Map(),
ast: {
directives: [],
},
};
interfaceTypeState.fields.set(fieldName, def);
return def;
}
function getOrCreateArg(fieldState, argName, argType, argKind) {
const existing = fieldState.args.get(argName);
if (existing) {
return existing;
}
const def = {
name: argName,
type: argType,
kind: argKind,
tags: new Set(),
byGraph: new Map(),
ast: {
directives: [],
},
};
fieldState.args.set(argName, def);
return def;
}