@graphql-mesh/merger-federation
Version:
121 lines (117 loc) • 5.07 kB
JavaScript
const graphql = require('graphql');
const wrap = require('@graphql-tools/wrap');
const gateway = require('@apollo/gateway');
const schema = require('@graphql-tools/schema');
const utils = require('@graphql-mesh/utils');
const utils$1 = require('@graphql-tools/utils');
const store = require('@graphql-mesh/store');
const crossHelpers = require('@graphql-mesh/cross-helpers');
class FederationMerger {
constructor(options) {
this.name = 'federation';
this.logger = options.logger;
this.cache = options.cache;
this.pubsub = options.pubsub;
this.store = options.store;
}
async getUnifiedSchema({ rawSources, typeDefs, resolvers }) {
this.logger.debug(`Creating localServiceList for gateway`);
const rawSourceMap = new Map();
const localServiceList = [];
const sourceMap = new Map();
await Promise.all(rawSources.map(async (rawSource) => {
const transformedSchema = wrap.wrapSchema(rawSource);
rawSourceMap.set(rawSource.name, rawSource);
sourceMap.set(rawSource, transformedSchema);
const sdl = await this.store
.proxy(`${rawSource.name}_sdl`, store.PredefinedProxyOptions.StringWithoutValidation)
.getWithSet(async () => {
var _a;
this.logger.debug(`Fetching Apollo Federated Service SDL for ${rawSource.name}`);
const sdlQueryResult = await graphql.execute({
schema: transformedSchema,
document: graphql.parse(gateway.SERVICE_DEFINITION_QUERY),
});
if ((_a = sdlQueryResult.errors) === null || _a === void 0 ? void 0 : _a.length) {
throw new utils$1.AggregateError(sdlQueryResult.errors, `Failed on fetching Federated SDL for ${rawSource.name}`);
}
return sdlQueryResult.data._service.sdl;
});
localServiceList.push({
name: rawSource.name,
typeDefs: graphql.parse(sdl),
});
}));
this.logger.debug(`Creating ApolloGateway`);
const gateway$1 = new gateway.ApolloGateway({
localServiceList,
buildService: ({ name }) => {
this.logger.debug(`Building federation service: ${name}`);
const rawSource = rawSourceMap.get(name);
const transformedSchema = sourceMap.get(rawSource);
return new gateway.LocalGraphQLDataSource(transformedSchema);
},
logger: this.logger,
debug: !!crossHelpers.process.env.DEBUG,
serviceHealthCheck: true,
});
this.logger.debug(`Loading gateway`);
const { schema: schema$1, executor: gatewayExecutor } = await gateway$1.load();
const schemaHash = utils$1.printSchemaWithDirectives(schema$1);
let remoteSchema = schema$1;
this.logger.debug(`Wrapping gateway executor in a unified schema`);
const executor = ({ document, info, variables, context, operationName }) => {
const documentStr = utils.printWithCache(document);
const { operation } = info;
// const operationName = operation.name?.value;
return gatewayExecutor({
document,
request: {
query: documentStr,
operationName,
variables,
},
operationName,
cache: this.cache,
context,
queryHash: documentStr,
logger: this.logger,
metrics: {},
source: documentStr,
operation,
schema: schema$1,
schemaHash,
overallCachePolicy: undefined,
});
};
const id = this.pubsub.subscribe('destroy', async () => {
this.pubsub.unsubscribe(id);
await gateway$1.stop();
});
this.logger.debug(`Applying additionalTypeDefs`);
typeDefs === null || typeDefs === void 0 ? void 0 : typeDefs.forEach(typeDef => {
remoteSchema = graphql.extendSchema(remoteSchema, typeDef);
});
if (resolvers) {
this.logger.debug(`Applying additionalResolvers`);
for (const resolversObj of utils$1.asArray(resolvers)) {
remoteSchema = schema.addResolversToSchema({
schema: remoteSchema,
resolvers: resolversObj,
updateResolversInPlace: true,
});
}
}
this.logger.debug(`Attaching sourceMap to the unified schema`);
remoteSchema.extensions = remoteSchema.extensions || {};
Object.defineProperty(remoteSchema.extensions, 'sourceMap', {
get: () => sourceMap,
});
return {
schema: remoteSchema,
executor,
};
}
}
module.exports = FederationMerger;
;