UNPKG

@pothos/plugin-federation

Version:

A Pothos plugin for implementing apollo federation subGraphs

203 lines (179 loc) 5.85 kB
import './global-types'; import './schema-builder'; import SchemaBuilder, { BasePlugin, type PothosEnumValueConfig, type PothosInputFieldConfig, type PothosOutputFieldConfig, type PothosTypeConfig, type SchemaTypes, type TypeParam, } from '@pothos/core'; import { defaultFieldResolver } from 'graphql'; import { providesMap } from './external-ref'; import { addUsedDirectives, entityMapping, keyDirective, mergeDirectives } from './util'; export { hasResolvableKey } from './schema-builder'; export * from './types'; const pluginName = 'federation'; export default pluginName; export class PothosFederationPlugin<Types extends SchemaTypes> extends BasePlugin<Types> { override onTypeConfig(typeConfig: PothosTypeConfig) { const entityConfig = entityMapping.get(this.builder)?.get(typeConfig.name); const apollo = entityConfig ? { ...(typeConfig.extensions?.apollo as {}), subgraph: { ...(typeConfig.extensions?.apollo as { subgraph: {} })?.subgraph, resolveReference: entityConfig.resolveReference, }, } : typeConfig.extensions?.apollo; const typeDirectives = [ ...(entityConfig ? keyDirective(entityConfig.key) : []), ...(entityConfig?.interfaceObject ? [{ name: 'interfaceObject', args: {} }] : []), ...getCommonDirectives(typeConfig), ]; addUsedDirectives( this.builder, typeDirectives.map((d) => d.name), ); return { ...typeConfig, extensions: { ...typeConfig.extensions, apollo, directives: mergeDirectives(typeConfig.extensions?.directives as [], typeDirectives), }, }; } override onOutputFieldConfig( fieldConfig: PothosOutputFieldConfig<Types>, ): PothosOutputFieldConfig<Types> | null { const options = fieldConfig.pothosOptions as PothosSchemaTypes.FieldOptionsByKind< Types, unknown, TypeParam<Types>, false, {}, {}, {} >['ExtendedEntity']; const ref = Array.isArray(options.type) ? options.type[0] : options.type; const resolve = ( fieldConfig.kind === 'ExternalEntity' ? (defaultFieldResolver as never) : fieldConfig.resolve )!; const fieldDirectives = [ options.requires ? { name: 'requires', args: { fields: options.requires.selection } } : null, fieldConfig.kind === 'ExternalEntity' ? { name: 'external' } : null, providesMap.has(ref) ? { name: 'provides', args: { fields: providesMap.get(ref) } } : null, fieldConfig.pothosOptions.override ? { name: 'override', args: fieldConfig.pothosOptions.override } : null, ...getCommonDirectives(fieldConfig), ].filter(Boolean) as { name: string }[]; addUsedDirectives( this.builder, fieldDirectives.map((d) => d.name), ); const directives = mergeDirectives(fieldConfig.extensions?.directives as [], fieldDirectives); return { ...fieldConfig, resolve, extensions: { ...fieldConfig.extensions, directives, }, }; } override onInputFieldConfig(fieldConfig: PothosInputFieldConfig<Types>) { return addCommonDirectives(this.builder, fieldConfig); } override onEnumValueConfig(valueConfig: PothosEnumValueConfig<Types>) { return addCommonDirectives(this.builder, valueConfig); } } function getCommonDirectives< T extends { extensions?: Record<string, unknown> | null; pothosOptions: { tag?: string[] | string; inaccessible?: boolean; shareable?: boolean; authenticated?: boolean; requiresScopes?: unknown[][]; policy?: unknown[][]; cost?: number; listSize?: { assumedSize?: number; slicingArguments?: string[]; sizedFields?: string[]; requireOneSlicingArgument?: boolean; }; }; }, >(config: T) { const tags = typeof config.pothosOptions.tag === 'string' ? [config.pothosOptions.tag] : (config.pothosOptions.tag ?? []); const tagDirectives = tags.map((tag) => ({ name: 'tag', args: { name: tag }, })); const requiresScopes = config.pothosOptions.requiresScopes ? { name: 'requiresScopes', args: { scopes: config.pothosOptions.requiresScopes }, } : null; const policy = config.pothosOptions.policy ? { name: 'policy', args: { policies: config.pothosOptions.policy } } : null; const cost = config.pothosOptions.cost !== undefined ? { name: 'cost', args: { weight: config.pothosOptions.cost } } : null; const listSize = config.pothosOptions.listSize ? { name: 'listSize', args: Object.fromEntries( Object.entries(config.pothosOptions.listSize).filter(([, v]) => v !== undefined), ), } : null; return [ config.pothosOptions.inaccessible ? { name: 'inaccessible' } : null, config.pothosOptions.shareable ? { name: 'shareable' } : null, config.pothosOptions.authenticated ? { name: 'authenticated' } : null, requiresScopes, policy, cost, listSize, ...tagDirectives, ].filter(Boolean) as { name: string }[]; } function addCommonDirectives< Types extends SchemaTypes, T extends { extensions?: Record<string, unknown> | null; pothosOptions: { tag?: string[] | string; inaccessible?: boolean; }; }, >(builder: PothosSchemaTypes.SchemaBuilder<Types>, config: T): T { const commonDirectives = getCommonDirectives(config); addUsedDirectives( builder, commonDirectives.map((d) => d.name), ); const directives = mergeDirectives(config.extensions?.directives as [], commonDirectives); return { ...config, extensions: { ...config.extensions, directives, }, }; } SchemaBuilder.registerPlugin(pluginName, PothosFederationPlugin);