UNPKG

@theguild/federation-composition

Version:

Open Source Composition library for Apollo Federation

223 lines (222 loc) 9.23 kB
import { createInterfaceTypeNode } from './ast.js'; import { convertToConst } from './common.js'; export 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.isDefinition) { interfaceTypeState.hasDefinition = 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)); interfaceTypeState.byGraph.set(graph.id, { extension: type.extension, keys: type.keys, interfaces: type.interfaces, implementedBy: type.implementedBy, }); 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.inaccessible) { fieldState.inaccessible = true; } 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); }); fieldState.byGraph.set(graph.id, { type: field.type, override: field.override, provides: field.provides, requires: field.requires, }); for (const arg of field.args.values()) { const argState = getOrCreateArg(fieldState, arg.name, arg.type); 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 === 'string') { argState.defaultValue = arg.defaultValue; } arg.ast.directives.forEach(directive => { argState.ast.directives.push(directive); }); argState.byGraph.set(graph.id, { type: arg.type, defaultValue: arg.defaultValue, }); } } }, composeSupergraphNode(interfaceType, graphs) { return createInterfaceTypeNode({ name: interfaceType.name, fields: Array.from(interfaceType.fields.values()).map(field => { return { name: field.name, type: field.type, inaccessible: field.inaccessible, tags: Array.from(field.tags), deprecated: field.deprecated, description: field.description, ast: { directives: 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, tags: Array.from(arg.tags), defaultValue: arg.defaultValue, deprecated: arg.deprecated, description: arg.description, ast: { directives: convertToConst(arg.ast.directives), }, }; }), join: { field: field.byGraph.size === interfaceType.byGraph.size ? [] : Array.from(field.byGraph.entries()).map(([graphName, meta]) => ({ graph: graphName.toUpperCase(), type: meta.type === field.type ? undefined : meta.type, override: meta.override ?? undefined, provides: meta.provides ?? undefined, requires: meta.requires ?? undefined, })), }, }; }), tags: Array.from(interfaceType.tags), inaccessible: interfaceType.inaccessible, description: interfaceType.description, interfaces: Array.from(interfaceType.interfaces), ast: { directives: 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, })); } 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 = { name: typeName, tags: new Set(), inaccessible: false, hasDefinition: 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, tags: new Set(), inaccessible: false, byGraph: new Map(), args: new Map(), ast: { directives: [], }, }; interfaceTypeState.fields.set(fieldName, def); return def; } function getOrCreateArg(fieldState, argName, argType) { const existing = fieldState.args.get(argName); if (existing) { return existing; } const def = { name: argName, type: argType, tags: new Set(), byGraph: new Map(), ast: { directives: [], }, }; fieldState.args.set(argName, def); return def; }