'use strict'; const _ = require('lodash'); const { rulesToQuery } = require('@casl/ability/extra'); const { VALID_REST_OPERATORS } = require('strapi-utils'); const ops = { common: VALID_REST_OPERATORS.map(op => `$${op}`), boolean: ['$or'], cleanable: ['$elemMatch'], }; const buildCaslQuery = (ability, action, model) => { const query = rulesToQuery(ability, action, model, o => o.conditions); return query && _.has(query, '$or') ? _.pick(query, '$or') : {}; }; const buildStrapiQuery = caslQuery => { const transform = _.flow([flattenDeep, cleanupUnwantedProperties]); return transform(caslQuery); }; const flattenDeep = condition => { if (_.isArray(condition)) { return _.map(condition, flattenDeep); } if (!_.isObject(condition)) { return condition; } const shouldIgnore = e => !!ops.common.includes(e); const shouldPerformTransformation = (v, k) => _.isObject(v) && !_.isArray(v) && !shouldIgnore(k); const result = {}; const set = (key, value) => (result[key] = value); const getTransformParams = (prevKey, v, k) => shouldIgnore(k) ? [`${prevKey}_${k.replace('$', '')}`, v] : [`${prevKey}.${k}`, v]; _.each(condition, (value, key) => { if (ops.boolean.includes(key)) { set(key.replace('$', '_'), _.map(value, flattenDeep)); } else if (shouldPerformTransformation(value, key)) { _.each(flattenDeep(value), (v, k) => { set(...getTransformParams(key, v, k)); }); } else { set(key, flattenDeep(value)); } }); return result; }; const cleanupUnwantedProperties = condition => { if (!_.isObject(condition)) { return condition; } if (_.isArray(condition)) { return condition.map(cleanupUnwantedProperties); } const shouldClean = e => typeof e === 'string' ? ops.cleanable.find(o => e.includes(`.${o}`)) : undefined; return _.reduce( condition, (acc, value, key) => { const keyToClean = shouldClean(key); const newKey = keyToClean ? key.split(`.${keyToClean}`).join('') : key; return { ...acc, [newKey]: _.isArray(value) ? value.map(cleanupUnwantedProperties) : value, }; }, {} ); }; module.exports = { buildCaslQuery, buildStrapiQuery, };