UNPKG

@strapi/utils

Version:

Shared utilities for the Strapi packages

141 lines (137 loc) 5.1 kB
'use strict'; var fp = require('lodash/fp'); var contentTypes = require('../content-types.js'); var operators = require('../operators.js'); var factory = require('./factory.js'); const isObj = (value)=>fp.isObject(value); /** True if this object should be walked as a filter subtree (operators / attributes), not an opaque operand. */ const isFilterLikeObject = (value, schema)=>Object.keys(value).some((k)=>operators.isOperator(k) || Boolean(schema?.attributes?.[k])); const filters = factory().intercept(// Intercept filters arrays and apply the traversal to each one individually fp.isArray, async (visitor, options, filters, { recurse })=>{ return Promise.all(filters.map((filter, i)=>{ // In filters, only operators such as $and, $in, $notIn or $or and implicit operators like [...] // can have a value array, thus we can update the raw path but not the attribute one const newPath = options.path ? { ...options.path, raw: `${options.path.raw}[${i}]` } : options.path; return recurse(visitor, { ...options, path: newPath }, filter); })).then((res)=>res.filter((val)=>!(fp.isObject(val) && fp.isEmpty(val)))); }).intercept(// Ignore non object filters and return the value as-is (filters)=>!fp.isObject(filters), (_, __, filters)=>{ return filters; })// Parse object values .parse(isObj, ()=>({ transform: fp.cloneDeep, remove (key, data) { return fp.omit(key, data); }, set (key, value, data) { return { ...data, [key]: value }; }, keys (data) { return Object.keys(data); }, get (key, data) { return data[key]; } }))// Ignore null or undefined values .ignore(({ value })=>fp.isNil(value))// Recursion on operators (non attributes) .on(({ attribute })=>fp.isNil(attribute), async ({ key, visitor, path, value, schema, getModel, attribute }, { set, recurse })=>{ const parent = { key, path, schema, attribute }; // Operator operands that are plain objects (not arrays) are only traversed when they look like // filter subtrees (nested operators or schema attributes). Otherwise treat as opaque operands // (e.g. GraphQL DateTime / Date for $gt, $null / $notNull booleans, or { $null: { anything } }). // Without this, traversing into e.g. { $null: { anything: 'x' } } makes validate throw on "anything". // $not is excluded: its value is always a nested filter map, not an opaque scalar operand. if (operators.isOperator(key) && key !== '$not' && isObj(value) && !fp.isArray(value) && !isFilterLikeObject(value, schema)) { set(key, value); return; } set(key, await recurse(visitor, { schema, path, getModel, parent }, value)); })// Handle relation recursion .onRelation(async ({ key, attribute, visitor, path, value, schema, getModel }, { set, recurse })=>{ const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph'); if (isMorphRelation) { return; } const parent = { key, path, schema, attribute }; const targetSchemaUID = attribute.target; const targetSchema = getModel(targetSchemaUID); const newValue = await recurse(visitor, { schema: targetSchema, path, getModel, parent }, value); set(key, newValue); }).onComponent(async ({ key, attribute, visitor, path, schema, value, getModel }, { set, recurse })=>{ const parent = { key, path, schema, attribute }; const targetSchema = getModel(attribute.component); const newValue = await recurse(visitor, { schema: targetSchema, path, getModel, parent }, value); set(key, newValue); })// Handle media recursion .onMedia(async ({ key, visitor, path, schema, attribute, value, getModel }, { set, recurse })=>{ const parent = { key, path, schema, attribute }; const targetSchemaUID = 'plugin::upload.file'; const targetSchema = getModel(targetSchemaUID); const newValue = await recurse(visitor, { schema: targetSchema, path, getModel, parent }, value); set(key, newValue); })// Scalar fields: recurse into operator maps (e.g. { $contains: 'x' }) so visitors see nested keys. .onAttribute(({ attribute, value })=>Boolean(contentTypes.isScalarAttribute(attribute)) && isObj(value) && !fp.isArray(value), async ({ key, visitor, path, value, schema, getModel, attribute }, { set, recurse })=>{ const parent = { key, path, schema, attribute }; set(key, await recurse(visitor, { schema, path, getModel, parent }, value)); }); var traverseQueryFilters = fp.curry(filters.traverse); module.exports = traverseQueryFilters; //# sourceMappingURL=query-filters.js.map