@graphql-tools/stitch
Version:
A set of utils for faster development of GraphQL tools
110 lines (109 loc) • 5.34 kB
JavaScript
import { GraphQLSchema, specifiedDirectives, extendSchema } from 'graphql';
import { addResolversToSchema, assertResolversPresent, extendResolversFromInterfaces } from '@graphql-tools/schema';
import { isSubschemaConfig, Subschema, defaultMergedResolver } from '@graphql-tools/delegate';
import { buildTypeCandidates, buildTypes } from './typeCandidates.js';
import { createStitchingInfo, completeStitchingInfo, addStitchingInfo } from './stitchingInfo.js';
import { isolateComputedFieldsTransformer, splitMergedTypeEntryPointsTransformer, } from './subschemaConfigTransforms/index.js';
import { applyExtensions, mergeExtensions, mergeResolvers } from '@graphql-tools/merge';
export function stitchSchemas({ subschemas = [], types = [], typeDefs = [], onTypeConflict, mergeDirectives, mergeTypes = true, typeMergingOptions, subschemaConfigTransforms = [], resolvers = {}, inheritResolversFromInterfaces = false, resolverValidationOptions = {}, updateResolversInPlace = true, schemaExtensions, ...rest }) {
const transformedSubschemas = [];
const subschemaMap = new Map();
const originalSubschemaMap = new Map();
for (const subschema of subschemas) {
for (const transformedSubschemaConfig of applySubschemaConfigTransforms(subschemaConfigTransforms, subschema, subschemaMap, originalSubschemaMap)) {
transformedSubschemas.push(transformedSubschemaConfig);
}
}
const directiveMap = Object.create(null);
for (const directive of specifiedDirectives) {
directiveMap[directive.name] = directive;
}
const schemaDefs = Object.create(null);
const [typeCandidates, rootTypeNameMap, extensions] = buildTypeCandidates({
subschemas: transformedSubschemas,
originalSubschemaMap,
types,
typeDefs: typeDefs || [],
parseOptions: rest,
directiveMap,
schemaDefs,
mergeDirectives,
});
let stitchingInfo = createStitchingInfo(subschemaMap, typeCandidates, mergeTypes);
const { typeMap: newTypeMap, directives: newDirectives } = buildTypes({
typeCandidates,
directives: Object.values(directiveMap),
stitchingInfo,
rootTypeNames: Object.values(rootTypeNameMap),
onTypeConflict,
mergeTypes,
typeMergingOptions,
});
let schema = new GraphQLSchema({
query: newTypeMap[rootTypeNameMap.query],
mutation: newTypeMap[rootTypeNameMap.mutation],
subscription: newTypeMap[rootTypeNameMap.subscription],
types: Object.values(newTypeMap),
directives: newDirectives,
astNode: schemaDefs.schemaDef,
extensionASTNodes: schemaDefs.schemaExtensions,
extensions: null,
});
for (const extension of extensions) {
schema = extendSchema(schema, extension, {
commentDescriptions: true,
});
}
// We allow passing in an array of resolver maps, in which case we merge them
const resolverMap = mergeResolvers(resolvers);
const finalResolvers = inheritResolversFromInterfaces
? extendResolversFromInterfaces(schema, resolverMap)
: resolverMap;
stitchingInfo = completeStitchingInfo(stitchingInfo, finalResolvers, schema);
schema = addResolversToSchema({
schema,
defaultFieldResolver: defaultMergedResolver,
resolvers: finalResolvers,
resolverValidationOptions,
inheritResolversFromInterfaces: false,
updateResolversInPlace,
});
const resolverValidationOptionsEntries = Object.entries(resolverValidationOptions);
if (resolverValidationOptionsEntries.length > 0 && resolverValidationOptionsEntries.some(([, o]) => o !== 'ignore')) {
assertResolversPresent(schema, resolverValidationOptions);
}
addStitchingInfo(schema, stitchingInfo);
if (schemaExtensions) {
if (Array.isArray(schemaExtensions)) {
schemaExtensions = mergeExtensions(schemaExtensions);
}
applyExtensions(schema, schemaExtensions);
}
return schema;
}
const subschemaConfigTransformerPresets = [
isolateComputedFieldsTransformer,
splitMergedTypeEntryPointsTransformer,
];
function applySubschemaConfigTransforms(subschemaConfigTransforms, subschemaOrSubschemaConfig, subschemaMap, originalSubschemaMap) {
let subschemaConfig;
if (isSubschemaConfig(subschemaOrSubschemaConfig)) {
subschemaConfig = subschemaOrSubschemaConfig;
}
else if (subschemaOrSubschemaConfig instanceof GraphQLSchema) {
subschemaConfig = { schema: subschemaOrSubschemaConfig };
}
else {
throw new TypeError('Received invalid input.');
}
const transformedSubschemaConfigs = subschemaConfigTransforms
.concat(subschemaConfigTransformerPresets)
.reduce((transformedSubschemaConfigs, subschemaConfigTransform) => transformedSubschemaConfigs.flatMap(ssConfig => subschemaConfigTransform(ssConfig)), [subschemaConfig]);
const transformedSubschemas = transformedSubschemaConfigs.map(ssConfig => new Subschema(ssConfig));
const baseSubschema = transformedSubschemas[0];
subschemaMap.set(subschemaOrSubschemaConfig, baseSubschema);
for (const subschema of transformedSubschemas) {
originalSubschemaMap.set(subschema, subschemaOrSubschemaConfig);
}
return transformedSubschemas;
}