@graphql-mesh/fusion-composition
Version:
Basic composition utility for Fusion spec
116 lines (115 loc) • 4.91 kB
JavaScript
import { DirectiveLocation, GraphQLDirective, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType, GraphQLSchema, GraphQLString, } from 'graphql';
import { addInaccessibleDirective } from './filter-schema.js';
const OPERATION_TYPE_SUFFIX_MAP = {
query: 'Query',
mutation: 'Mutation',
subscription: 'Subscription',
};
const DEFAULT_APPLY_TO = {
query: true,
mutation: true,
subscription: true,
};
export function createEncapsulateTransform(opts = {}) {
return function encapsulateTransform(schema, subgraphConfig) {
const groupName = opts.name || subgraphConfig.name;
const applyToMap = {
...DEFAULT_APPLY_TO,
...(opts.applyTo || {}),
};
const newRootTypes = {};
for (const opTypeString in applyToMap) {
const operationType = opTypeString;
const originalType = schema.getRootType(operationType);
if (originalType && applyToMap[operationType]) {
const originalTypeConfig = originalType.toConfig();
const wrappedTypeName = `${groupName}${OPERATION_TYPE_SUFFIX_MAP[operationType]}`;
const originalFieldMapWithHidden = {};
const wrappedFieldMap = {};
for (const fieldName in originalTypeConfig.fields) {
const originalFieldConfig = originalTypeConfig.fields[fieldName];
wrappedFieldMap[fieldName] = {
...originalFieldConfig,
extensions: {
directives: {
resolveTo: [
{
sourceName: subgraphConfig.name,
sourceTypeName: originalType.name,
sourceFieldName: fieldName,
},
],
},
},
};
const newOriginalFieldConfig = {
...originalFieldConfig,
astNode: undefined,
};
addInaccessibleDirective(newOriginalFieldConfig);
originalFieldMapWithHidden[fieldName] = newOriginalFieldConfig;
}
const wrappedType = new GraphQLObjectType({
name: wrappedTypeName,
fields: wrappedFieldMap,
});
newRootTypes[operationType] = new GraphQLObjectType({
...originalTypeConfig,
fields: {
...originalFieldMapWithHidden,
[groupName]: {
type: new GraphQLNonNull(wrappedType),
extensions: {
directives: {
resolveTo: [
{
sourceName: subgraphConfig.name,
sourceTypeName: originalType.name,
sourceFieldName: '__typename',
},
],
},
},
},
},
});
}
else {
newRootTypes[operationType] = originalType;
}
}
const schemaConfig = schema.toConfig();
const newDirectives = [...schemaConfig.directives];
if (!newDirectives.some(directive => directive.name === 'resolveTo')) {
newDirectives.push(resolveToDirective);
}
return new GraphQLSchema({
...schemaConfig,
types: undefined,
directives: newDirectives,
...newRootTypes,
});
};
}
export const resolveToSourceArgsScalar = new GraphQLScalarType({
name: 'ResolveToSourceArgs',
});
export const resolveToDirective = new GraphQLDirective({
name: 'resolveTo',
locations: [DirectiveLocation.FIELD_DEFINITION],
args: {
additionalArgs: { type: resolveToSourceArgsScalar },
filterBy: { type: GraphQLString },
keyField: { type: GraphQLString },
keysArg: { type: GraphQLString },
pubsubTopic: { type: GraphQLString },
requiredSelectionSet: { type: GraphQLString },
result: { type: GraphQLString },
resultType: { type: GraphQLString },
sourceArgs: { type: resolveToSourceArgsScalar },
sourceFieldName: { type: GraphQLString },
sourceName: { type: GraphQLString },
sourceSelectionSet: { type: GraphQLString },
sourceTypeName: { type: GraphQLString },
},
});