@graphql-mesh/supergraph
Version:
120 lines (119 loc) • 5.69 kB
JavaScript
import { parse } from 'graphql';
import { process } from '@graphql-mesh/cross-helpers';
import { PredefinedProxyOptions } from '@graphql-mesh/store';
import { getInterpolatedHeadersFactory, getInterpolatedStringFactory, stringInterpolator, } from '@graphql-mesh/string-interpolation';
import { isUrl, readFile, readUrl } from '@graphql-mesh/utils';
import { buildHTTPExecutor } from '@graphql-tools/executor-http';
import { getStitchedSchemaFromSupergraphSdl } from '@graphql-tools/federation';
export default class SupergraphHandler {
constructor({ config, baseDir, store, importFn, logger, }) {
this.config = config;
this.baseDir = baseDir;
this.supergraphSdl = store.proxy('nonExecutableSchema', PredefinedProxyOptions.JsonWithoutValidation);
this.importFn = importFn;
this.logger = logger;
}
async getSupergraphSdl() {
const schemaHeadersFactory = getInterpolatedHeadersFactory(this.config.schemaHeaders);
const interpolatedSource = stringInterpolator.parse(this.config.source, {
env: process.env,
});
if (isUrl(interpolatedSource)) {
const res = await readUrl(interpolatedSource, {
headers: schemaHeadersFactory({
env: process.env,
}),
cwd: this.baseDir,
allowUnknownExtensions: true,
importFn: this.importFn,
fetch: this.fetchFn,
logger: this.logger,
});
if (typeof res === 'string') {
return parse(res, { noLocation: true });
}
return res;
}
return this.supergraphSdl.getWithSet(async () => {
const sdlOrIntrospection = await readFile(interpolatedSource, {
cwd: this.baseDir,
allowUnknownExtensions: true,
importFn: this.importFn,
fetch: this.fetchFn,
logger: this.logger,
});
if (typeof sdlOrIntrospection === 'string') {
return parse(sdlOrIntrospection, { noLocation: true });
}
return sdlOrIntrospection;
});
}
async getMeshSource({ fetchFn }) {
const subgraphConfigs = this.config.subgraphs || [];
this.fetchFn = fetchFn;
const supergraphSdl = await this.getSupergraphSdl();
const operationHeadersFactory = this.config.operationHeaders != null
? getInterpolatedHeadersFactory(this.config.operationHeaders)
: undefined;
const joingraphEnum = supergraphSdl.definitions.find(def => def.kind === 'EnumTypeDefinition' && def.name.value === 'join__Graph');
const subgraphNameIdMap = new Map();
if (joingraphEnum) {
joingraphEnum.values?.forEach(value => {
value.directives?.forEach(directive => {
if (directive.name.value === 'join__graph') {
const nameArg = directive.arguments?.find(arg => arg.name.value === 'name');
if (nameArg?.value?.kind === 'StringValue') {
subgraphNameIdMap.set(value.name.value, nameArg.value.value);
}
}
});
});
}
const schema = getStitchedSchemaFromSupergraphSdl({
supergraphSdl,
onExecutor: ({ subgraphName, endpoint: nonInterpolatedEndpoint }) => {
const subgraphRealName = subgraphNameIdMap.get(subgraphName);
const subgraphConfiguration = subgraphConfigs.find(subgraphConfig => subgraphConfig.name === subgraphRealName) || {
name: subgraphName,
};
nonInterpolatedEndpoint = subgraphConfiguration.endpoint || nonInterpolatedEndpoint;
const endpointFactory = getInterpolatedStringFactory(nonInterpolatedEndpoint);
return buildHTTPExecutor({
...subgraphConfiguration,
endpoint: nonInterpolatedEndpoint,
fetch(url, init, context, info) {
const endpoint = endpointFactory({
env: process.env,
context,
info,
});
url = url.replace(nonInterpolatedEndpoint, endpoint);
return fetchFn(url, init, context, info);
},
headers(executorRequest) {
const headers = {};
const resolverData = {
root: executorRequest.rootValue,
env: process.env,
context: executorRequest.context,
info: executorRequest.info,
args: executorRequest.variables,
};
if (subgraphConfiguration?.operationHeaders) {
const headersFactory = getInterpolatedHeadersFactory(subgraphConfiguration.operationHeaders);
Object.assign(headers, headersFactory(resolverData));
}
if (operationHeadersFactory) {
Object.assign(headers, operationHeadersFactory(resolverData));
}
return headers;
},
});
},
batch: this.config.batch == null ? true : this.config.batch,
});
return {
schema,
};
}
}