@graphql-mesh/fusion-composition
Version:
Basic composition utility for Fusion spec
203 lines (202 loc) • 8.98 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.addInaccessibleDirective = addInaccessibleDirective;
exports.createFilterTransform = createFilterTransform;
const graphql_1 = require("graphql");
const minimatch_1 = require("minimatch");
const utils_1 = require("@graphql-tools/utils");
function addInaccessibleDirective(directableObj) {
const directives = (0, utils_1.getDirectiveExtensions)(directableObj);
directives.inaccessible = [{}];
const extensions = (directableObj.extensions ||= {});
extensions.directives = directives;
directableObj.astNode = undefined;
}
function compareAndAddInaccessibleDirective(originalSchema, filteredSchema) {
return (0, utils_1.mapSchema)(originalSchema, {
[utils_1.MapperKind.TYPE]: type => {
const typeInFiltered = filteredSchema.getType(type.name);
if (!typeInFiltered) {
addInaccessibleDirective(type);
}
else if ('getFields' in type) {
if (!('getFields' in typeInFiltered)) {
addInaccessibleDirective(type);
}
else {
const numOfFieldsInFiltered = Object.keys(typeInFiltered.getFields()).length;
if (numOfFieldsInFiltered === 0) {
addInaccessibleDirective(type);
}
}
}
return type;
},
[utils_1.MapperKind.FIELD]: (fieldConfig, fieldName, typeName) => {
const typeInFiltered = filteredSchema.getType(typeName);
if (!typeInFiltered || !('getFields' in typeInFiltered)) {
addInaccessibleDirective(fieldConfig);
}
else {
const filteredFields = typeInFiltered.getFields();
const fieldInFiltered = filteredFields[fieldName];
if (!fieldInFiltered) {
addInaccessibleDirective(fieldConfig);
}
else {
if ('args' in fieldConfig && !('args' in fieldInFiltered)) {
for (const argName in fieldConfig.args) {
const argConfig = fieldConfig.args[argName];
addInaccessibleDirective(argConfig);
}
}
else if ('args' in fieldConfig && 'args' in fieldInFiltered) {
const filteredArgs = fieldInFiltered.args;
for (const argName in fieldConfig.args) {
if (!filteredArgs.some(arg => arg.name === argName)) {
const argConfig = fieldConfig.args[argName];
addInaccessibleDirective(argConfig);
if ((0, graphql_1.isNonNullType)(argConfig.type)) {
argConfig.type = argConfig.type.ofType;
}
}
}
}
}
}
return fieldConfig;
},
});
}
function createFilterTransform({
// Declarative Filters
filters, filterDeprecatedTypes, filterDeprecatedFields,
// Programmatic Filters
...programmaticFilters }) {
const typeFilters = programmaticFilters.typeFilter
? [programmaticFilters.typeFilter]
: [];
const fieldFilters = programmaticFilters.fieldFilter
? [programmaticFilters.fieldFilter]
: [];
const argumentFilters = programmaticFilters.argumentFilter ? [programmaticFilters.argumentFilter] : [];
if (filters?.length) {
for (const filter of filters) {
const [typeName, fieldNameOrGlob, argsGlob] = filter.split('.');
const typeMatcher = new minimatch_1.Minimatch(typeName);
// TODO: deprecate this in next major release as dscussed in #1605
if (!fieldNameOrGlob) {
typeFilters.push(type => typeMatcher.match(type.name));
continue;
}
let fixedFieldGlob = argsGlob || fieldNameOrGlob;
if (fixedFieldGlob.includes('{') && !fixedFieldGlob.includes(',')) {
fixedFieldGlob = fieldNameOrGlob.replace('{', '').replace('}', '');
}
fixedFieldGlob = fixedFieldGlob.split(', ').join(',');
const globalTypeMatcher = new minimatch_1.Minimatch(fixedFieldGlob.trim());
if (typeName === 'Type') {
typeFilters.push(type => globalTypeMatcher.match(type.name));
continue;
}
if (argsGlob) {
const fieldMatcher = new minimatch_1.Minimatch(fieldNameOrGlob);
argumentFilters.push((typeName, fieldName, argName) => {
if (typeMatcher.match(typeName) && fieldMatcher.match(fieldName)) {
return globalTypeMatcher.match(argName);
}
});
continue;
}
// If the glob is not for Types nor Args, finally we register Fields filters
fieldFilters.push((typeName, fieldName) => {
if (typeMatcher.match(typeName)) {
return globalTypeMatcher.match(fieldName);
}
return true;
});
}
}
if (filterDeprecatedFields) {
fieldFilters.push((_, __, fieldConfig) => !fieldConfig.deprecationReason);
}
if (filterDeprecatedTypes) {
typeFilters.push(type => !(0, utils_1.getDirectiveExtensions)(type)?.deprecated?.length);
}
const schemaMapper = {};
if (programmaticFilters.rootFieldFilter) {
schemaMapper[utils_1.MapperKind.ROOT_FIELD] = (fieldConfig, fieldName, typeName, schema) => {
const filterResult = programmaticFilters.rootFieldFilter(typeName, fieldName);
if (filterResult != null && !filterResult) {
return null;
}
};
}
if (typeFilters.length) {
schemaMapper[utils_1.MapperKind.TYPE] = type => {
for (const filter of typeFilters) {
const filterResult = filter(type);
if (filterResult != null && !filterResult) {
return null;
}
}
return type;
};
}
if (argumentFilters.length || fieldFilters.length) {
schemaMapper[utils_1.MapperKind.FIELD] = (fieldConfig, fieldName, typeName, schema) => {
for (const filter of fieldFilters) {
const filterResult = filter(typeName, fieldName, fieldConfig);
if (filterResult != null && !filterResult) {
return null;
}
}
if (argumentFilters.length && 'args' in fieldConfig && fieldConfig.args) {
const newArgs = {};
for (const argName in fieldConfig.args) {
const argConfig = fieldConfig.args[argName];
let filtered = false;
for (const argFilter of argumentFilters) {
const argFilterResult = argFilter(typeName, fieldName, argName);
if (argFilterResult != null && !argFilterResult) {
filtered = true;
break;
}
}
if (!filtered) {
newArgs[argName] = argConfig;
}
}
fieldConfig.args = newArgs;
}
return fieldConfig;
};
}
if (programmaticFilters.objectFieldFilter) {
schemaMapper[utils_1.MapperKind.OBJECT_FIELD] = (fieldConfig, fieldName, typeName, schema) => {
const filterResult = programmaticFilters.objectFieldFilter(typeName, fieldName);
if (filterResult != null && !filterResult) {
return null;
}
};
}
if (programmaticFilters.interfaceFieldFilter) {
schemaMapper[utils_1.MapperKind.INTERFACE_FIELD] = (fieldConfig, fieldName, typeName, schema) => {
const filterResult = programmaticFilters.interfaceFieldFilter(typeName, fieldName);
if (filterResult != null && !filterResult) {
return null;
}
};
}
if (programmaticFilters.inputObjectFieldFilter) {
schemaMapper[utils_1.MapperKind.INPUT_OBJECT_FIELD] = (fieldConfig, fieldName, typeName, schema) => {
const filterResult = programmaticFilters.inputObjectFieldFilter(typeName, fieldName);
if (filterResult != null && !filterResult) {
return null;
}
};
}
return function filterTransform(schema) {
return compareAndAddInaccessibleDirective(schema, (0, utils_1.mapSchema)(schema, schemaMapper));
};
}
;