@graphql-mesh/fusion-runtime
Version:
Runtime for GraphQL Mesh Fusion Supergraph
340 lines (339 loc) • 15.2 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useFusiongraph = exports.getExecutorForFusiongraph = exports.getTransportExecutor = exports.createTransportGetter = exports.defaultTransportsOption = exports.getMemoizedExecutionPlanForOperation = exports.getSubgraphTransportMapFromFusiongraph = void 0;
const graphql_1 = require("graphql");
const fusion_execution_1 = require("@graphql-mesh/fusion-execution");
// eslint-disable-next-line import/no-extraneous-dependencies
const runtime_1 = require("@graphql-mesh/runtime");
// eslint-disable-next-line import/no-extraneous-dependencies
const utils_1 = require("@graphql-mesh/utils");
const stitch_1 = require("@graphql-tools/stitch");
const utils_2 = require("@graphql-tools/utils");
function getTransportDirectives(fusiongraph) {
const transportDirectives = (0, utils_2.getDirective)(fusiongraph, fusiongraph, 'transport');
if (transportDirectives?.length) {
return transportDirectives;
}
const astNode = fusiongraph.astNode;
if (astNode?.directives?.length) {
return astNode.directives
.filter(directive => directive.name.value === 'transport')
.map(transportDirective => Object.fromEntries(transportDirective.arguments?.map(argument => [
argument.name.value,
(0, graphql_1.valueFromASTUntyped)(argument.value),
])));
}
return [];
}
function getSubgraphTransportMapFromFusiongraph(fusiongraph) {
const subgraphTransportEntryMap = {};
const transportDirectives = getTransportDirectives(fusiongraph);
for (const { kind, subgraph, location, headers, ...options } of transportDirectives) {
subgraphTransportEntryMap[subgraph] = {
kind,
location,
headers,
options,
subgraph,
};
}
return subgraphTransportEntryMap;
}
exports.getSubgraphTransportMapFromFusiongraph = getSubgraphTransportMapFromFusiongraph;
exports.getMemoizedExecutionPlanForOperation = (0, utils_2.memoize2of4)(function getMemoizedExecutionPlanForOperation(fusiongraph, document, operationName, _random) {
return (0, fusion_execution_1.createExecutablePlanForOperation)({
fusiongraph,
document,
operationName,
});
});
function defaultTransportsOption(transportKind) {
return Promise.resolve(`${`@graphql-mesh/transport-${transportKind}`}`).then(s => __importStar(require(s))).catch(err => {
console.error(err);
throw new Error(`No transport found for ${transportKind}. Please install @graphql-mesh/transport-${transportKind}`);
});
}
exports.defaultTransportsOption = defaultTransportsOption;
function createTransportGetter(transports) {
if (typeof transports === 'function') {
return transports;
}
return function getTransport(transportKind) {
const transport = transports[transportKind];
if (!transport) {
throw new Error(`No transport found for ${transportKind}`);
}
return transport;
};
}
exports.createTransportGetter = createTransportGetter;
function getTransportExecutor(transportGetter, transportContext) {
transportContext.logger?.info(`Loading transport ${transportContext.transportEntry?.kind}`);
const transport$ = transportGetter(transportContext.transportEntry?.kind);
if ((0, utils_2.isPromise)(transport$)) {
return transport$.then(transport => transport.getSubgraphExecutor(transportContext));
}
return transport$.getSubgraphExecutor(transportContext);
}
exports.getTransportExecutor = getTransportExecutor;
function getExecutorForFusiongraph({ fusiongraph, transports = defaultTransportsOption, plugins, ...transportBaseContext }) {
const onSubgraphExecuteHooks = [];
if (plugins) {
for (const plugin of plugins) {
if (plugin.onSubgraphExecute) {
onSubgraphExecuteHooks.push(plugin.onSubgraphExecute);
}
}
}
const transportEntryMap = getSubgraphTransportMapFromFusiongraph(fusiongraph);
const subgraphExecutorMap = {};
const transportGetter = createTransportGetter(transports);
function onSubgraphExecute(subgraphName, document, variables, context) {
let executor = subgraphExecutorMap[subgraphName];
if (executor == null) {
transportBaseContext?.logger?.info(`Initializing executor for subgraph ${subgraphName}`);
const transportEntry = transportEntryMap[subgraphName];
// eslint-disable-next-line no-inner-declarations
function wrapExecutorWithHooks(currentExecutor) {
if (onSubgraphExecuteHooks.length) {
return function executorWithHooks(subgraphExecReq) {
const onSubgraphExecuteDoneHooks = [];
const onSubgraphExecuteHooksRes$ = (0, utils_1.iterateAsync)(onSubgraphExecuteHooks, onSubgraphExecuteHook => onSubgraphExecuteHook({
fusiongraph,
subgraphName,
transportKind: transportEntry?.kind,
transportLocation: transportEntry?.location,
transportHeaders: transportEntry?.headers,
transportOptions: transportEntry?.options,
executionRequest: subgraphExecReq,
executor: currentExecutor,
setExecutor(newExecutor) {
currentExecutor = newExecutor;
},
}), onSubgraphExecuteDoneHooks);
function handleOnSubgraphExecuteHooksResult() {
if (onSubgraphExecuteDoneHooks.length) {
// eslint-disable-next-line no-inner-declarations
function handleExecutorResWithHooks(currentResult) {
const onSubgraphExecuteDoneHooksRes$ = (0, utils_1.iterateAsync)(onSubgraphExecuteDoneHooks, onSubgraphExecuteDoneHook => onSubgraphExecuteDoneHook({
result: currentResult,
setResult(newResult) {
currentResult = newResult;
},
}));
if ((0, utils_2.isPromise)(onSubgraphExecuteDoneHooksRes$)) {
return onSubgraphExecuteDoneHooksRes$.then(() => currentResult);
}
return currentResult;
}
const executorRes$ = currentExecutor(subgraphExecReq);
if ((0, utils_2.isPromise)(executorRes$)) {
return executorRes$.then(handleExecutorResWithHooks);
}
if ((0, utils_2.isAsyncIterable)(executorRes$)) {
return (0, utils_2.mapAsyncIterator)(executorRes$, handleExecutorResWithHooks);
}
return handleExecutorResWithHooks(executorRes$);
}
return currentExecutor(subgraphExecReq);
}
if ((0, utils_2.isPromise)(onSubgraphExecuteHooksRes$)) {
return onSubgraphExecuteHooksRes$.then(handleOnSubgraphExecuteHooksResult);
}
return handleOnSubgraphExecuteHooksResult();
};
}
return currentExecutor;
}
executor = function lazyExecutor(subgraphExecReq) {
function getSubgraph() {
return (0, fusion_execution_1.extractSubgraphFromFusiongraph)(subgraphName, fusiongraph);
}
const executor$ = getTransportExecutor(transportGetter, transportBaseContext
? {
...transportBaseContext,
subgraphName,
getSubgraph,
transportEntry,
}
: { getSubgraph, transportEntry, subgraphName });
if ((0, utils_2.isPromise)(executor$)) {
return executor$.then(executor_ => {
executor = wrapExecutorWithHooks(executor_);
subgraphExecutorMap[subgraphName] = executor;
return executor(subgraphExecReq);
});
}
executor = wrapExecutorWithHooks(executor$);
subgraphExecutorMap[subgraphName] = executor;
return executor(subgraphExecReq);
};
}
return executor({ document, variables, context });
}
function fusiongraphExecutor(execReq) {
if (execReq.operationName === 'IntrospectionQuery') {
return {
data: (0, graphql_1.introspectionFromSchema)(fusiongraph),
};
}
const executablePlan = (0, exports.getMemoizedExecutionPlanForOperation)(fusiongraph, execReq.document, execReq.operationName);
return (0, fusion_execution_1.executeOperationPlan)({
executablePlan,
onExecute: onSubgraphExecute,
variables: execReq.variables,
context: execReq.context,
});
}
return {
fusiongraphExecutor,
transportEntryMap,
onSubgraphExecute,
};
}
exports.getExecutorForFusiongraph = getExecutorForFusiongraph;
function ensureSchema(source) {
if ((0, graphql_1.isSchema)(source)) {
return source;
}
if (typeof source === 'string') {
return (0, graphql_1.buildSchema)(source, { noLocation: true, assumeValidSDL: true, assumeValid: true });
}
return (0, graphql_1.buildASTSchema)(source, { assumeValidSDL: true, assumeValid: true });
}
function getExecuteFnFromExecutor(executor) {
return function executeFnFromExecutor({ document, variableValues, contextValue, rootValue, operationName, }) {
return executor({
document,
variables: variableValues,
context: contextValue,
operationName,
rootValue,
});
};
}
function useFusiongraph({ getFusiongraph, transports, additionalResolvers, polling, transportBaseContext, }) {
let fusiongraph;
let lastLoadedFusiongraph;
let executeFn;
let executor;
let yoga;
// TODO: We need to figure this out in a better way
let inContextSDK;
function handleLoadedFusiongraph(loadedFusiongraph) {
// If the fusiongraph is the same, we don't need to do anything
if (lastLoadedFusiongraph != null && lastLoadedFusiongraph === loadedFusiongraph) {
return;
}
lastLoadedFusiongraph = loadedFusiongraph;
fusiongraph = ensureSchema(loadedFusiongraph);
const { fusiongraphExecutor, onSubgraphExecute, transportEntryMap } = getExecutorForFusiongraph({
fusiongraph,
transports,
plugins: yoga.getEnveloped._plugins,
...transportBaseContext,
});
executor = fusiongraphExecutor;
if (additionalResolvers != null) {
fusiongraph = (0, stitch_1.stitchSchemas)({
subschemas: [
{
schema: fusiongraph,
executor,
},
],
resolvers: additionalResolvers,
});
const subgraphsForInContextSdk = [];
for (const subgraphName in transportEntryMap) {
subgraphsForInContextSdk.push({
name: subgraphName,
schema: (0, fusion_execution_1.extractSubgraphFromFusiongraph)(subgraphName, fusiongraph),
executor(execReq) {
return onSubgraphExecute(subgraphName, execReq.document, execReq.variables, execReq.context);
},
});
}
inContextSDK = (0, runtime_1.getInContextSDK)(fusiongraph, subgraphsForInContextSdk, transportBaseContext.logger, []);
}
else {
executeFn = getExecuteFnFromExecutor(executor);
}
}
function getAndSetFusiongraph() {
const fusiongraph$ = getFusiongraph(transportBaseContext);
if ((0, utils_2.isPromise)(fusiongraph$)) {
return fusiongraph$.then(handleLoadedFusiongraph);
}
else {
return handleLoadedFusiongraph(fusiongraph$);
}
}
if (polling) {
setInterval(getAndSetFusiongraph, polling);
}
let initialFusiongraph$;
let initiated = false;
return {
onYogaInit(payload) {
yoga = payload.yoga;
},
onRequestParse() {
return {
onRequestParseDone() {
if (!initiated) {
initialFusiongraph$ = getAndSetFusiongraph();
}
initiated = true;
return initialFusiongraph$;
},
};
},
onEnveloped({ setSchema }) {
setSchema(fusiongraph);
},
onContextBuilding({ extendContext }) {
if (inContextSDK) {
extendContext(inContextSDK);
}
extendContext(transportBaseContext);
},
onExecute({ setExecuteFn }) {
if (executeFn) {
setExecuteFn(executeFn);
}
},
onSubscribe({ setSubscribeFn }) {
if (executeFn) {
setSubscribeFn(executeFn);
}
},
invalidateUnifiedGraph() {
return getAndSetFusiongraph();
},
};
}
exports.useFusiongraph = useFusiongraph;
;