@graphql-mesh/fusion-runtime
Version:
Runtime for GraphQL Mesh Fusion Supergraph
159 lines (158 loc) • 7.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnifiedGraphManager = void 0;
exports.ensureSchema = ensureSchema;
const graphql_1 = require("graphql");
const runtime_1 = require("@graphql-mesh/runtime");
const utils_1 = require("@graphql-mesh/utils");
const utils_2 = require("@graphql-tools/utils");
const disposablestack_1 = require("@whatwg-node/disposablestack");
const supergraph_js_1 = require("./federation/supergraph.js");
const utils_js_1 = require("./utils.js");
function ensureSchema(source) {
if ((0, graphql_1.isSchema)(source)) {
return source;
}
if (typeof source === 'string') {
return (0, graphql_1.buildSchema)(source, { assumeValid: true, assumeValidSDL: true });
}
if ((0, utils_2.isDocumentNode)(source)) {
return (0, graphql_1.buildASTSchema)(source, { assumeValid: true, assumeValidSDL: true });
}
return source;
}
class UnifiedGraphManager {
constructor(opts) {
this.opts = opts;
this.disposableStack = new disposablestack_1.AsyncDisposableStack();
this.batch = opts.batch ?? true;
this.handleUnifiedGraph = opts.handleUnifiedGraph || supergraph_js_1.handleFederationSupergraph;
this.onSubgraphExecuteHooks = opts?.onSubgraphExecuteHooks || [];
this.disposableStack.defer(() => {
this.unifiedGraph = undefined;
this.lastLoadedUnifiedGraph = undefined;
this.inContextSDK = undefined;
this.initialUnifiedGraph$ = undefined;
this.pausePolling();
return this._transportExecutorStack?.disposeAsync();
});
}
pausePolling() {
if (this.currentTimeout) {
clearTimeout(this.currentTimeout);
this.currentTimeout = undefined;
}
}
continuePolling() {
if (this.opts.pollingInterval) {
this.currentTimeout = setTimeout(() => {
this.currentTimeout = undefined;
return this.getAndSetUnifiedGraph();
}, this.opts.pollingInterval);
}
}
ensureUnifiedGraph() {
if (!this.initialUnifiedGraph$ && !this.unifiedGraph) {
this.initialUnifiedGraph$ = this.getAndSetUnifiedGraph();
}
return this.initialUnifiedGraph$;
}
getAndSetUnifiedGraph() {
this.pausePolling();
try {
return (0, utils_1.mapMaybePromise)(this.opts.getUnifiedGraph(this.opts.transportContext), (loadedUnifiedGraph) => {
if (loadedUnifiedGraph != null &&
this.lastLoadedUnifiedGraph != null &&
(0, utils_js_1.compareSchemas)(loadedUnifiedGraph, this.lastLoadedUnifiedGraph)) {
this.opts.transportContext?.logger?.debug('Unified Graph has not changed, skipping...');
this.continuePolling();
return;
}
if (this.lastLoadedUnifiedGraph != null) {
this.opts.transportContext?.logger?.debug('Unified Graph changed, updating...');
}
let cleanupJob$;
if (this._transportExecutorStack) {
cleanupJob$ = this._transportExecutorStack.disposeAsync();
}
return (0, utils_1.mapMaybePromise)(cleanupJob$, () => {
this._transportExecutorStack = new disposablestack_1.AsyncDisposableStack();
this.lastLoadedUnifiedGraph ||= loadedUnifiedGraph;
this.lastLoadedUnifiedGraph = loadedUnifiedGraph;
this.unifiedGraph = ensureSchema(loadedUnifiedGraph);
const { unifiedGraph: newUnifiedGraph, transportEntryMap, subschemas, additionalResolvers, } = this.handleUnifiedGraph({
unifiedGraph: this.unifiedGraph,
additionalTypeDefs: this.opts.additionalTypeDefs,
additionalResolvers: this.opts.additionalResolvers,
onSubgraphExecute(subgraphName, execReq) {
return onSubgraphExecute(subgraphName, execReq);
},
transportEntryAdditions: this.opts.transportEntryAdditions,
batch: this.batch,
});
this.unifiedGraph = newUnifiedGraph;
const onSubgraphExecute = (0, utils_js_1.getOnSubgraphExecute)({
onSubgraphExecuteHooks: this.onSubgraphExecuteHooks,
transports: this.opts.transports,
transportContext: this.opts.transportContext,
transportEntryMap,
getSubgraphSchema(subgraphName) {
const subgraph = subschemas.find(s => (0, utils_js_1.compareSubgraphNames)(s.name, subgraphName));
if (!subgraph) {
throw new Error(`Subgraph ${subgraphName} not found`);
}
return subgraph.schema;
},
transportExecutorStack: this._transportExecutorStack,
});
if (this.opts.additionalResolvers || additionalResolvers.length) {
this.inContextSDK = (0, runtime_1.getInContextSDK)(this.unifiedGraph,
// @ts-expect-error Legacy Mesh RawSource is not compatible with new Mesh
subschemas, this.opts.transportContext?.logger, this.opts.onDelegateHooks || []);
}
this.continuePolling();
this._transportEntryMap = transportEntryMap;
this.opts.onSchemaChange?.(this.unifiedGraph);
return true;
});
}, err => {
this.opts.transportContext?.logger?.error('Failed to load Supergraph', err);
this.continuePolling();
if (!this.unifiedGraph) {
throw err;
}
return true;
});
}
catch (e) {
this.opts.transportContext?.logger?.error('Failed to load Supergraph', e);
this.continuePolling();
if (!this.unifiedGraph) {
throw e;
}
return true;
}
}
getUnifiedGraph() {
return (0, utils_1.mapMaybePromise)(this.ensureUnifiedGraph(), () => this.unifiedGraph);
}
getContext(base = {}) {
return (0, utils_1.mapMaybePromise)(this.ensureUnifiedGraph(), () => {
if (this.inContextSDK) {
Object.assign(base, this.inContextSDK);
}
Object.assign(base, this.opts.transportContext);
return base;
});
}
getTransportEntryMap() {
return (0, utils_1.mapMaybePromise)(this.ensureUnifiedGraph(), () => this._transportEntryMap);
}
invalidateUnifiedGraph() {
return this.getAndSetUnifiedGraph();
}
[disposablestack_1.DisposableSymbols.asyncDispose]() {
return this.disposableStack.disposeAsync();
}
}
exports.UnifiedGraphManager = UnifiedGraphManager;