UNPKG

@graphql-mesh/transform-federation

Version:
169 lines (168 loc) • 8.16 kB
import { GraphQLObjectType, GraphQLUnionType, isObjectType } from 'graphql'; import { loadFromModuleExportExpression } from '@graphql-mesh/utils'; import { entitiesField, EntityType, serviceField } from '@apollo/subgraph/dist/types.js'; import { mapSchema, MapperKind, printSchemaWithDirectives } from '@graphql-tools/utils'; import set from 'lodash.set'; import { stringInterpolator } from '@graphql-mesh/string-interpolation'; export default class FederationTransform { constructor({ apiName, baseDir, config, importFn, }) { this.noWrap = true; this.apiName = apiName; this.config = config; this.baseDir = baseDir; this.importFn = importFn; } transformSchema(schema, rawSource) { var _a, _b, _c, _d, _e, _f, _g; rawSource.merge = {}; if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.types) { const queryType = schema.getQueryType(); const queryTypeFields = queryType.getFields(); for (const type of this.config.types) { rawSource.merge[type.name] = {}; const typeObj = schema.getType(type.name); typeObj.extensions = typeObj.extensions || {}; const typeDirectivesObj = (typeObj.extensions.directives = typeObj.extensions.directives || {}); if ((_b = type.config) === null || _b === void 0 ? void 0 : _b.key) { typeDirectivesObj.key = type.config.key; } if ((_c = type.config) === null || _c === void 0 ? void 0 : _c.shareable) { typeDirectivesObj.shareable = type.config.shareable; } if ((_d = type.config) === null || _d === void 0 ? void 0 : _d.extends) { typeDirectivesObj.extends = type.config.extends; } const typeFieldObjs = typeObj.getFields(); if ((_e = type.config) === null || _e === void 0 ? void 0 : _e.fields) { for (const field of type.config.fields) { const typeField = typeFieldObjs[field.name]; if (typeField) { typeField.extensions = typeField.extensions || {}; const directivesObj = (typeField.extensions.directives = typeField.extensions.directives || {}); Object.assign(directivesObj, field.config); } rawSource.merge[type.name].fields = rawSource.merge[type.name].fields || {}; rawSource.merge[type.name].fields[field.name] = rawSource.merge[type.name].fields[field.name] || {}; if (field.config.requires) { rawSource.merge[type.name].fields[field.name].computed = true; rawSource.merge[type.name].fields[field.name].selectionSet = `{ ${field.config.requires} }`; } } } // If a field is a key field, it should be GraphQLID if ((_f = type.config) === null || _f === void 0 ? void 0 : _f.key) { let selectionSetContent = ''; for (const keyField of type.config.key) { selectionSetContent += '\n'; selectionSetContent += keyField.fields || ''; } if (selectionSetContent) { rawSource.merge[type.name].selectionSet = `{ ${selectionSetContent} }`; } } let resolveReference; if ((_g = type.config) === null || _g === void 0 ? void 0 : _g.resolveReference) { const resolveReferenceConfig = type.config.resolveReference; if (typeof resolveReferenceConfig === 'string') { const fn$ = loadFromModuleExportExpression(resolveReferenceConfig, { cwd: this.baseDir, defaultExportName: 'default', importFn: this.importFn, }); resolveReference = (...args) => fn$.then(fn => fn(...args)); } else if (typeof resolveReferenceConfig === 'function') { resolveReference = resolveReferenceConfig; } else { const queryField = queryTypeFields[resolveReferenceConfig.queryFieldName]; resolveReference = async (root, context, info) => { const args = {}; for (const argName in resolveReferenceConfig.args) { const argVal = stringInterpolator.parse(resolveReferenceConfig.args[argName], { root, args, context, info, env: process.env, }); if (argVal) { set(args, argName, argVal); } } const result = await context[this.apiName].Query[queryField.name]({ root, args, context, info, }); return { ...root, ...result, }; }; } rawSource.merge[type.name].resolve = resolveReference; } } } const entityTypes = []; for (const typeName in rawSource.merge || {}) { const type = schema.getType(typeName); if (isObjectType(type)) { entityTypes.push(type); } set(type, 'extensions.apollo.subgraph.resolveReference', rawSource.merge[typeName].resolve); } const schemaWithFederationQueryType = mapSchema(schema, { [MapperKind.QUERY]: type => { const config = type.toConfig(); return new GraphQLObjectType({ ...config, fields: { ...config.fields, _entities: entitiesField, _service: { ...serviceField, resolve: (root, args, context, info) => ({ sdl: printSchemaWithDirectives(info.schema), }), }, }, }); }, }); const schemaWithUnionType = mapSchema(schemaWithFederationQueryType, { [MapperKind.UNION_TYPE]: type => { if (type.name === EntityType.name) { return new GraphQLUnionType({ ...EntityType.toConfig(), types: entityTypes, }); } return type; }, }); schemaWithUnionType.extensions = schemaWithUnionType.extensions || {}; const directivesObj = (schemaWithUnionType.extensions.directives = schemaWithUnionType.extensions.directives || {}); directivesObj.link = { url: 'https://specs.apollo.dev/federation/v2.0', import: [ '@extends', '@external', '@inaccessible', '@key', '@override', '@provides', '@requires', '@shareable', '@tag', ], }; return schemaWithUnionType; } }