UNPKG

@graphql-mesh/transform-federation

Version:
172 lines (167 loc) • 8.22 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } const graphql = require('graphql'); const utils = require('@graphql-mesh/utils'); const types_js = require('@apollo/subgraph/dist/types.js'); const utils$1 = require('@graphql-tools/utils'); const set = _interopDefault(require('lodash.set')); const stringInterpolation = require('@graphql-mesh/string-interpolation'); 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$ = utils.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 = stringInterpolation.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 (graphql.isObjectType(type)) { entityTypes.push(type); } set(type, 'extensions.apollo.subgraph.resolveReference', rawSource.merge[typeName].resolve); } const schemaWithFederationQueryType = utils$1.mapSchema(schema, { [utils$1.MapperKind.QUERY]: type => { const config = type.toConfig(); return new graphql.GraphQLObjectType({ ...config, fields: { ...config.fields, _entities: types_js.entitiesField, _service: { ...types_js.serviceField, resolve: (root, args, context, info) => ({ sdl: utils$1.printSchemaWithDirectives(info.schema) }), }, }, }); }, }); const schemaWithUnionType = utils$1.mapSchema(schemaWithFederationQueryType, { [utils$1.MapperKind.UNION_TYPE]: type => { if (type.name === types_js.EntityType.name) { return new graphql.GraphQLUnionType({ ...types_js.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; } } module.exports = FederationTransform;