@graphql-mesh/fusion-composition
Version:
Basic composition utility for Fusion spec
183 lines (182 loc) • 6.91 kB
JavaScript
import { extendSchema, isSpecifiedScalarType, parse, parseType, typeFromAST, } from 'graphql';
import { getRootTypes, MapperKind, mapSchema } from '@graphql-tools/utils';
import { getDirectiveExtensions } from './getDirectiveExtensions.js';
export function createRenameTypeTransform(renameFn, kind = MapperKind.TYPE) {
return function renameTypeTransform(schema, subgraphConfig) {
return mapSchema(schema, {
[kind]: (type) => isSpecifiedScalarType(type)
? type
: new (Object.getPrototypeOf(type).constructor)({
...type.toConfig(),
name: renameFn(type, subgraphConfig) || type.name,
}),
});
};
}
export function createRenameFieldTransform(renameFn, kind = MapperKind.FIELD) {
return function renameFieldTransform(schema, subgraphConfig) {
return mapSchema(schema, {
[kind]: (field, fieldName, typeName) => [renameFn(field, fieldName, typeName, subgraphConfig) || fieldName, field],
});
};
}
export function createPrefixTransform({ value, ignore = [], includeRootOperations = false, includeTypes = true, } = {}) {
return function prefixTransform(schema, subgraphConfig) {
value = value || subgraphConfig.name;
const transforms = [];
const rootTypeNames = [...getRootTypes(schema)].map(type => type.name);
if (includeTypes) {
transforms.push(createRenameTypeTransform(type => {
if (ignore.includes(type.name)) {
return type.name;
}
return `${value}${type.name}`;
}));
}
if (includeRootOperations) {
transforms.push(createRenameFieldTransform((_field, fieldName, typeName) => {
if (rootTypeNames.includes(typeName) && !ignore.includes(typeName)) {
return `${value}${fieldName}`;
}
return fieldName;
}, MapperKind.ROOT_FIELD));
}
for (const transform of transforms) {
schema = transform(schema, subgraphConfig);
}
return schema;
};
}
function addHiddenDirective(directableObj) {
const directives = getDirectiveExtensions(directableObj);
directives.hidden = [{}];
const extensions = (directableObj.extensions ||= {});
extensions.directives = directives;
}
export function createFilterTransform(config) {
const schemaMapper = {};
if (config.rootFieldFilter) {
schemaMapper[MapperKind.ROOT_FIELD] = (fieldConfig, fieldName, typeName) => {
if (!config.rootFieldFilter(typeName, fieldName)) {
addHiddenDirective(fieldConfig);
}
return [fieldName, fieldConfig];
};
}
if (config.typeFilter) {
schemaMapper[MapperKind.TYPE] = type => {
if (!config.typeFilter(type)) {
addHiddenDirective(type);
}
return type;
};
}
if (config.fieldFilter) {
schemaMapper[MapperKind.FIELD] = (fieldConfig, fieldName, typeName) => {
if (!config.fieldFilter(typeName, fieldName)) {
addHiddenDirective(fieldConfig);
}
return fieldConfig;
};
}
if (config.objectFieldFilter) {
schemaMapper[MapperKind.OBJECT_FIELD] = (fieldConfig, fieldName, typeName) => {
if (!config.objectFieldFilter(typeName, fieldName)) {
addHiddenDirective(fieldConfig);
}
return [fieldName, fieldConfig];
};
}
if (config.interfaceFieldFilter) {
schemaMapper[MapperKind.INTERFACE_FIELD] = (fieldConfig, fieldName, typeName) => {
if (!config.interfaceFieldFilter(typeName, fieldName)) {
addHiddenDirective(fieldConfig);
}
return [fieldName, fieldConfig];
};
}
if (config.inputObjectFieldFilter) {
schemaMapper[MapperKind.INPUT_OBJECT_FIELD] = (fieldConfig, fieldName, typeName) => {
if (!config.inputObjectFieldFilter(typeName, fieldName)) {
addHiddenDirective(fieldConfig);
}
return fieldConfig;
};
}
// TODO
/*
if (config.argumentFilter) {
schemaMapper[MapperKind.ARGUMENT] = (argConfig, fieldName, typeName) => {
if (config.argumentFilter(typeName, fieldName, argConfig)) {
return argConfig;
}
return null;
};
}
*/
return function filterTransform(schema) {
return mapSchema(schema, schemaMapper);
};
}
export * from 'change-case';
export function createNamingConventionTransform(config) {
const schemaMapper = {};
if (config.typeNames) {
schemaMapper[MapperKind.TYPE] = type => {
if (isSpecifiedScalarType(type)) {
return type;
}
return new (Object.getPrototypeOf(type).constructor)({
...type.toConfig(),
name: config.typeNames(type.name),
});
};
}
if (config.fieldNames) {
schemaMapper[MapperKind.FIELD] = (fieldConfig, fieldName) => [
config.fieldNames(fieldName),
fieldConfig,
];
}
if (config.enumValues) {
schemaMapper[MapperKind.ENUM_VALUE] = (valueConfig, _typeName, _schema, externalValue) => [
config.enumValues(externalValue),
valueConfig,
];
}
if (config.fieldArgumentNames) {
schemaMapper[MapperKind.ARGUMENT] = (argumentConfig, argumentName) => ({
...argumentConfig,
name: config.fieldArgumentNames(argumentName),
});
}
return function namingConventionTransform(schema) {
return mapSchema(schema, schemaMapper);
};
}
export function createTypeReplaceTransform(replacerFn) {
return function typeReplaceTransform(schema) {
return mapSchema(schema, {
[MapperKind.FIELD]: (fieldConfig, fieldName, typeName) => {
const newTypeName = replacerFn(typeName, fieldName, fieldConfig.type);
if (newTypeName) {
const newType = typeFromAST(schema, parseType(newTypeName));
if (!newType) {
throw new Error(`No type found for ${newTypeName}`);
}
return [fieldName, { ...fieldConfig, type: newType }];
}
return undefined;
},
});
};
}
export function createExtendTransform(typeDefs) {
const typeDefsDoc = typeof typeDefs === 'string' ? parse(typeDefs, { noLocation: true }) : typeDefs;
return function extendTransform(schema) {
return extendSchema(schema, typeDefsDoc, {
assumeValid: true,
assumeValidSDL: true,
});
};
}