UNPKG

arrest

Version:

OpenAPI v3 compliant REST framework for Node.js, with support for MongoDB and JSON-Schema

183 lines 6.8 kB
import { rulesToQuery } from '@casl/ability/extra'; import { ObjectId } from 'mongodb'; function haveKeysInCommon(a, b) { return !!Object.keys(a).find((k) => (typeof b[k] !== 'undefined' ? k : undefined)); } export function addConstraint(query, constraint) { if (!query) { query = {}; } if (typeof constraint === 'object' && constraint !== null) { if (Array.isArray(query)) { if (Array.isArray(constraint)) { query = query.concat(constraint); } else if (query.length && query[query.length - 1].$match) { query[query.length - 1].$match = addConstraint(query[query.length - 1].$match, constraint); } else if (Object.keys(constraint).length > 0) { query.push({ $match: constraint }); } } else if (Array.isArray(constraint)) { query = [{ $match: query }].concat(constraint); } else { if (query.$or && constraint.$or) { query = { $and: [query, constraint] }; } else if (query.$and && Object.keys(constraint).length > 0) { const lastCond = query.$and[query.$and.length - 1]; if (lastCond && !(lastCond.$or && constraint.$or) && !(lastCond.$and && constraint.$and) && !haveKeysInCommon(lastCond, constraint)) { Object.assign(lastCond, constraint); } else { query.$and.push(constraint); } } else { if (haveKeysInCommon(query, constraint)) { query = { $and: [query, constraint] }; } else { Object.assign(query, constraint); } } } } return query; } export function escapeMongoKey(key) { return key.replace(/%/g, '%25').replace(/\$/g, '%24').replace(/\./g, '%2E'); } export const unescapeMongoKey = decodeURIComponent; function translateProperties(val, f) { if (typeof val !== 'object' || val === null || val instanceof Date || ObjectId.isValid(val)) { return val; } else if (Array.isArray(val)) { return val.map((k) => translateProperties(k, f)); } else { const out = {}; for (let k in val) { out[f(k)] = translateProperties(val[k], f); } return out; } } export function escapeMongoObject(val) { return translateProperties(val, escapeMongoKey); } export function unescapeMongoObject(val) { return translateProperties(val, unescapeMongoKey); } export function patchToMongo(patch, escape = false) { const out = {}; for (let p of patch) { const path = (p.path || '') .split('/') .map((i) => (escape ? escapeMongoKey(i) : i)) .slice(1); if (!path.length) { throw new Error('path cannot be empty'); } switch (p.op) { case 'add': if (!out.doc) out.doc = {}; if (path.length && path[path.length - 1] == '-') { if (path.length === 1) { throw new Error("cannot use '-' index at root of path"); } path.pop(); if (!out.doc.$push) out.doc.$push = {}; out.doc.$push[path.join('.')] = p.value; } else { if (!out.doc.$set) out.doc.$set = {}; out.doc.$set[path.join('.')] = p.value; } break; case 'replace': if (path.length && path[path.length - 1] == '-') { throw new Error("cannot use '-' index in path of replace"); } else { if (!out.doc) out.doc = {}; if (!out.doc.$set) out.doc.$set = {}; out.doc.$set[path.join('.')] = p.value; if (!out.query) out.query = {}; out.query[path.join('.')] = { $exists: true }; } break; case 'move': const from = (p.from || '').split('/').slice(1); if (!from.length) { throw new Error('from path cannot be empty'); } if (from.length && from[from.length - 1] == '-') { throw new Error("cannot use '-' index in from path of move"); } else if (path.length && path[path.length - 1] == '-') { throw new Error("cannot use '-' index in path of move"); } else { if (!out.doc) out.doc = {}; if (!out.doc.$rename) out.doc.$rename = {}; out.doc.$rename[from.join('.')] = path.join('.'); } break; case 'remove': if (path.length && path[path.length - 1] == '-') { throw new Error("cannot use '-' index in path of remove"); } else { if (!out.doc) out.doc = {}; if (!out.doc.$unset) out.doc.$unset = {}; out.doc.$unset[path.join('.')] = 1; } break; case 'copy': throw new Error('copy not supported'); case 'test': if (path.length && path[path.length - 1] == '-') { throw new Error("cannot use '-' index in path of test"); } else { if (!out.query) out.query = {}; out.query[path.join('.')] = p.value; } break; } } return out; } // The following two functions come from @casl/mongoose and were // copied (and adapted) here to avoid importing mongoose function convertToMongoQuery(rule) { const conditions = rule.conditions; return rule.inverted ? { $nor: [conditions] } : conditions; } export function toMongoQuery(ability, subjectType, action) { // TODO: typescript doesn't like the type of action, so we work around it const f = rulesToQuery; const out = f(ability, action, subjectType, convertToMongoQuery); if (Object.keys(out).length === 1 && out.$or.length === 1) { return out.$or[0]; } else { return out; } } //# sourceMappingURL=util.js.map