@graphql-mesh/fusion-runtime
Version:
Runtime for GraphQL Mesh Fusion Supergraph
138 lines (137 loc) • 5.38 kB
JavaScript
import { buildASTSchema, buildSchema, isSchema, } from 'graphql';
import { useReadinessCheck } from 'graphql-yoga';
import { getInContextSDK } from '@graphql-mesh/runtime';
import { mapMaybePromise, resolveAdditionalResolversWithoutImport } from '@graphql-mesh/utils';
import { stitchSchemas } from '@graphql-tools/stitch';
import { isDocumentNode, isPromise } from '@graphql-tools/utils';
import { extractSubgraphsFromFusiongraph } from './getSubschemasFromFusiongraph.js';
import { defaultTransportsOption, getOnSubgraphExecute, } from './utils.js';
function ensureSchema(source) {
if (isSchema(source)) {
return source;
}
if (typeof source === 'string') {
return buildSchema(source, { assumeValid: true, assumeValidSDL: true });
}
if (isDocumentNode(source)) {
return buildASTSchema(source, { assumeValid: true, assumeValidSDL: true });
}
return source;
}
export function useFusiongraph(opts) {
let fusiongraph;
let lastLoadedFusiongraph;
let yoga;
// TODO: We need to figure this out in a better way
let inContextSDK;
function handleLoadedFusiongraph(loadedFusiongraph) {
if (loadedFusiongraph != null && lastLoadedFusiongraph === loadedFusiongraph) {
return;
}
lastLoadedFusiongraph = loadedFusiongraph;
fusiongraph = ensureSchema(loadedFusiongraph);
const { transportEntryMap, subschemaMap, additionalTypeDefs, additionalResolversFromTypeDefs } = extractSubgraphsFromFusiongraph(fusiongraph);
const subgraphMap = new Map();
const subschemas = [];
const onSubgraphExecute = getOnSubgraphExecute({
fusiongraph,
plugins: yoga.getEnveloped._plugins,
transports: opts.transports || defaultTransportsOption,
transportBaseContext: opts.transportBaseContext,
transportEntryMap,
subgraphMap,
});
for (const [subschemaName, subschemaConfig] of subschemaMap) {
subgraphMap.set(subschemaName, subschemaConfig.schema);
subschemas.push({
...subschemaConfig,
name: subschemaName,
executor(execReq) {
return onSubgraphExecute(subschemaName, execReq);
},
});
}
fusiongraph = stitchSchemas({
subschemas,
assumeValid: true,
assumeValidSDL: true,
typeDefs: [opts.additionalTypedefs, ...additionalTypeDefs],
resolvers: [
opts.additionalResolvers,
additionalResolversFromTypeDefs.map(additionalResolver => resolveAdditionalResolversWithoutImport(additionalResolver)),
],
});
if (opts.additionalResolvers || additionalResolversFromTypeDefs.length) {
const onDelegateHooks = [];
for (const plugin of yoga.getEnveloped._plugins) {
if (plugin.onDelegate) {
onDelegateHooks.push(plugin.onDelegate);
}
}
inContextSDK = getInContextSDK(fusiongraph, subschemas, opts.transportBaseContext?.logger, onDelegateHooks);
}
}
function getAndSetFusiongraph() {
const supergraph$ = opts.getFusiongraph(opts.transportBaseContext);
return mapMaybePromise(supergraph$, handleLoadedFusiongraph);
}
if (opts.polling) {
setInterval(getAndSetFusiongraph, opts.polling);
}
let initialFusiongraph$;
let initiated = false;
function ensureFusiongraph() {
if (!initiated) {
initialFusiongraph$ = getAndSetFusiongraph();
}
initiated = true;
return initialFusiongraph$;
}
return {
onYogaInit(payload) {
yoga = payload.yoga;
},
onPluginInit({ addPlugin }) {
if (opts.readinessCheckEndpoint) {
addPlugin(
// TODO: fix useReadinessCheck typings to inherit the context
useReadinessCheck({
endpoint: opts.readinessCheckEndpoint,
check() {
const initialFusiongraph$ = ensureFusiongraph();
if (isPromise(initialFusiongraph$)) {
return initialFusiongraph$.then(() => !!fusiongraph);
}
return !!fusiongraph;
},
}));
}
},
onRequestParse() {
return {
onRequestParseDone() {
return ensureFusiongraph();
},
};
},
onEnveloped({ setSchema }) {
setSchema(fusiongraph);
},
onContextBuilding({ extendContext }) {
const initialFusiongraph$ = ensureFusiongraph();
function handleInitiatedFusiongraph() {
if (inContextSDK) {
extendContext(inContextSDK);
}
extendContext(opts.transportBaseContext);
}
if (isPromise(initialFusiongraph$)) {
return initialFusiongraph$.then(handleInitiatedFusiongraph);
}
handleInitiatedFusiongraph();
},
invalidateUnifiedGraph() {
return getAndSetFusiongraph();
},
};
}