UNPKG

@graphql-mesh/transform-filter-schema

Version:
161 lines (154 loc) 7.68 kB
'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;