@graphql-mesh/transform-filter-schema
Version:
161 lines (154 loc) • 7.68 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
const utils = require('@graphql-mesh/utils');
const wrap = require('@graphql-tools/wrap');
const minimatch = _interopDefault(require('minimatch'));
const utils$1 = require('@graphql-tools/utils');
class WrapFilter {
constructor({ config: { filters } }) {
this.transforms = [];
for (const filter of filters) {
const [typeName, fieldNameOrGlob, argsGlob] = filter.split('.');
const typeMatcher = new minimatch.Minimatch(typeName);
// TODO: deprecate this in next major release as dscussed in #1605
if (!fieldNameOrGlob) {
this.transforms.push(new wrap.FilterTypes(type => {
return 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.Minimatch(fixedFieldGlob.trim());
if (typeName === 'Type') {
this.transforms.push(new wrap.FilterTypes(type => {
return globalTypeMatcher.match(type.name);
}));
continue;
}
if (argsGlob) {
const fieldMatcher = new minimatch.Minimatch(fieldNameOrGlob);
this.transforms.push(new wrap.TransformCompositeFields((fieldTypeName, fieldName, fieldConfig) => {
if (typeMatcher.match(fieldTypeName) && fieldMatcher.match(fieldName)) {
const fieldArgs = Object.entries(fieldConfig.args).reduce((args, [argName, argConfig]) => !globalTypeMatcher.match(argName) ? args : { ...args, [argName]: argConfig }, {});
return { ...fieldConfig, args: fieldArgs };
}
return undefined;
}));
continue;
}
// If the glob is not for Types nor Args, finally we register Fields filters
this.transforms.push(new wrap.FilterRootFields((rootTypeName, rootFieldName) => {
if (typeMatcher.match(rootTypeName)) {
return globalTypeMatcher.match(rootFieldName);
}
return true;
}));
this.transforms.push(new wrap.FilterObjectFields((objectTypeName, objectFieldName) => {
if (typeMatcher.match(objectTypeName)) {
return globalTypeMatcher.match(objectFieldName);
}
return true;
}));
this.transforms.push(new wrap.FilterInputObjectFields((inputObjectTypeName, inputObjectFieldName) => {
if (typeMatcher.match(inputObjectTypeName)) {
return globalTypeMatcher.match(inputObjectFieldName);
}
return true;
}));
}
}
transformSchema(originalWrappingSchema, subschemaConfig, transformedSchema) {
return utils.applySchemaTransforms(originalWrappingSchema, subschemaConfig, transformedSchema, this.transforms);
}
transformRequest(originalRequest, delegationContext, transformationContext) {
return utils.applyRequestTransforms(originalRequest, delegationContext, transformationContext, this.transforms);
}
transformResult(originalResult, delegationContext, transformationContext) {
return utils.applyResultTransforms(originalResult, delegationContext, transformationContext, this.transforms);
}
}
class BareFilter {
constructor({ config: { filters } }) {
this.noWrap = true;
this.typeGlobs = [];
this.fieldsMap = new Map();
this.argsMap = new Map();
for (const filter of filters) {
const [typeName, fieldNameOrGlob, argsGlob] = filter.split('.');
// TODO: deprecate this in next major release as dscussed in #1605
if (!fieldNameOrGlob) {
this.typeGlobs.push(typeName);
continue;
}
const rawGlob = argsGlob || fieldNameOrGlob;
const fixedGlob = rawGlob.includes('{') && !rawGlob.includes(',') ? rawGlob.replace('{', '').replace('}', '') : rawGlob;
const polishedGlob = fixedGlob.split(', ').join(',').trim();
if (typeName === 'Type') {
this.typeGlobs.push(polishedGlob);
continue;
}
const mapName = argsGlob ? 'argsMap' : 'fieldsMap';
const mapKey = argsGlob ? `${typeName}.${fieldNameOrGlob}` : typeName;
const currentRules = this[mapName].get(mapKey) || [];
this[mapName].set(mapKey, [...currentRules, polishedGlob]);
}
}
matchInArray(rulesArray, value) {
for (const rule of rulesArray) {
const ruleMatcher = new minimatch.Minimatch(rule);
if (!ruleMatcher.match(value))
return null;
}
return undefined;
}
transformSchema(schema) {
const transformedSchema = utils$1.mapSchema(schema, {
...(this.typeGlobs.length && {
[utils$1.MapperKind.TYPE]: type => this.matchInArray(this.typeGlobs, type.toString()),
}),
...((this.fieldsMap.size || this.argsMap.size) && {
[utils$1.MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName, typeName) => {
const fieldRules = this.fieldsMap.get(typeName);
const wildcardArgRules = this.argsMap.get(`${typeName}.*`) || [];
const fieldArgRules = this.argsMap.get(`${typeName}.${fieldName}`) || [];
const argRules = wildcardArgRules.concat(fieldArgRules);
const hasFieldRules = Boolean(fieldRules && fieldRules.length);
const hasArgRules = Boolean(argRules && argRules.length);
if (hasFieldRules && this.matchInArray(fieldRules, fieldName) === null)
return null;
if (!hasArgRules)
return undefined;
const fieldArgs = Object.entries(fieldConfig.args).reduce((args, [argName, argConfig]) => this.matchInArray(argRules, argName) === null ? args : { ...args, [argName]: argConfig }, {});
return { ...fieldConfig, args: fieldArgs };
},
}),
...(this.fieldsMap.size && {
[utils$1.MapperKind.INPUT_OBJECT_FIELD]: (_, fieldName, typeName) => {
const fieldRules = this.fieldsMap.get(typeName);
const hasFieldRules = Boolean(fieldRules && fieldRules.length);
if (hasFieldRules && this.matchInArray(fieldRules, fieldName) === null)
return null;
return undefined;
},
}),
});
return transformedSchema;
}
}
const FilterTransform = (function FilterTransform(options) {
if (Array.isArray(options.config)) {
return new WrapFilter({
...options,
config: {
mode: 'wrap',
filters: options.config,
},
});
}
return options.config.mode === 'bare' ? new BareFilter(options) : new WrapFilter(options);
});
module.exports = FilterTransform;