@graphql-hive/gateway-testing
Version:
157 lines (153 loc) • 4.81 kB
JavaScript
import { buildSubgraphSchema } from '@apollo/subgraph';
import { createGatewayRuntime, useCustomFetch } from '@graphql-hive/gateway-runtime';
import { getUnifiedGraphGracefully } from '@graphql-mesh/fusion-composition';
import { buildHTTPExecutor } from '@graphql-tools/executor-http';
import { parse } from 'graphql';
import { DisposableSymbols, createYoga } from 'graphql-yoga';
function getEnvStr(key, opts = {}) {
const globalThat = opts.globalThis ?? globalThis;
let variable = globalThat.process?.env?.[key] || // @ts-expect-error can exist in wrangler and maybe other runtimes
globalThat.env?.[key] || // @ts-expect-error can exist in deno
globalThat.Deno?.env?.get(key) || // @ts-expect-error could be
globalThat[key];
if (variable != null) {
variable += "";
} else {
variable = void 0;
}
return variable?.trim();
}
function getEnvBool(key, opts = {}) {
return strToBool(getEnvStr(key, opts));
}
function strToBool(str) {
return ["1", "t", "true", "y", "yes", "on", "enabled"].includes(
(str || "").toLowerCase()
);
}
function isDebug() {
return getEnvBool("DEBUG");
}
function createGatewayTester(config) {
let runtime;
if ("supergraph" in config) {
runtime = createGatewayRuntime({
maskedErrors: false,
logging: isDebug(),
...config
});
} else if ("subgraphs" in config) {
let buildSubgraphs2 = function() {
subgraphsRef.ref = (typeof subgraphsConfig === "function" ? subgraphsConfig() : subgraphsConfig).reduce(
(acc, subgraph) => {
const remoteSchema = buildRemoteSchema(subgraph);
return {
...acc,
[remoteSchema.name]: remoteSchema
};
},
{}
);
return Object.values(subgraphsRef.ref);
};
const subgraphsConfig = config.subgraphs;
const subgraphsRef = {
ref: null
};
runtime = createGatewayRuntime({
maskedErrors: false,
logging: isDebug(),
...config,
supergraph: typeof config.subgraphs === "function" ? () => getUnifiedGraphGracefully(buildSubgraphs2()) : getUnifiedGraphGracefully(buildSubgraphs2()),
plugins: (ctx) => [
{
onFetch({ executionRequest, setFetchFn }) {
const subgraphName = executionRequest?.subgraphName;
if (!subgraphName) {
return;
}
if (!subgraphsRef.ref) {
throw new Error("Subgraphs are not built yet");
}
const subgraph = subgraphsRef.ref[subgraphName];
if (!subgraph) {
throw new Error(`Subgraph "${subgraphName}" not found`);
}
setFetchFn(subgraph.yoga.fetch);
}
},
...config.plugins?.(ctx) || []
]
});
} else if ("proxy" in config) {
const remoteSchema = buildRemoteSchema(config.proxy);
runtime = createGatewayRuntime({
maskedErrors: false,
logging: isDebug(),
...config,
proxy: { endpoint: remoteSchema.url, headers: config.proxy.headers },
plugins: (ctx) => [
useCustomFetch((url, options, context, info) => {
return remoteSchema.yoga.fetch(
// @ts-expect-error TODO: url can be a string, not only an instance of URL
url,
options,
context,
info
);
}),
...config.plugins?.(ctx) || []
]
});
} else {
throw new Error("Unsupported gateway tester configuration");
}
const runtimeExecute = buildHTTPExecutor({
endpoint: "http://gateway/graphql",
fetch: runtime.fetch,
headers: (execReq) => execReq?.rootValue.headers
});
return {
runtime,
// @ts-expect-error native and whatwg-node fetch has conflicts
fetch: runtime.fetch,
async execute(args) {
return runtimeExecute({
document: parse(args.query),
variables: args.variables,
operationName: args.operationName,
extensions: args.extensions,
rootValue: { headers: args.headers }
});
},
[DisposableSymbols.asyncDispose]() {
return runtime[DisposableSymbols.asyncDispose]();
},
async dispose() {
await runtime.dispose();
}
};
}
function buildRemoteSchema(config) {
const schema = "typeDefs" in config.schema ? buildSubgraphSchema([
{
...config.schema,
typeDefs: parse(config.schema.typeDefs)
}
]) : config.schema;
const yoga = typeof config.yoga === "function" ? config.yoga?.(schema) : createYoga({
maskedErrors: false,
logging: isDebug(),
...config.yoga,
schema
});
const host = config.host || config.name;
const url = `http://${host}${yoga.graphqlEndpoint}`;
return {
name: config.name,
url,
schema,
yoga
};
}
export { createGatewayTester };