UNPKG

@restorecommerce/chassis-srv

Version:
443 lines 16.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.encodeMessage = exports.buildReturn = exports.buildSorter = exports.buildLimiter = exports.buildFilter = exports.buildField = exports.buildComparison = exports.autoCastValue = exports.autoCastKey = exports.sanitizeInputFields = exports.sanitizeOutputFields = exports.idToKey = exports.query = void 0; const _ = __importStar(require("lodash")); const long_1 = __importDefault(require("long")); /** * Ensure that the collection exists and process the query * @param {Object} db arangodb connection * @param {string} collectionName collection name * @param {string} query query string * @param {Object} args list of arguments, optional * @return {Promise} arangojs query result */ const query = async (db, collectionName, query, args) => { const collection = db.collection(collectionName); const collectionExists = await collection.exists(); try { if (!collectionExists) { await collection.create(); } } catch (err) { if (err.message && err.message.indexOf('duplicate name') == -1) { throw err; } } return await db.query(query, args); }; exports.query = query; /** * Convert id to arangodb friendly key. * @param {string} id document identification * @return {any} arangodb friendly key */ const idToKey = (id) => { return id.replace(/\//g, '_'); }; exports.idToKey = idToKey; /** * Ensure that the _key exists. * @param {Object} document Document template. * @return {any} Clone of the document with the _key field set. */ const ensureKey = (document) => { const doc = _.clone(document); if (_.has(doc, '_key')) { return doc; } const id = doc.id; if (id) { _.set(doc, '_key', (0, exports.idToKey)(id)); } return doc; }; const ensureDatatypes = (document) => { const doc = _.clone(document); const keys = _.keys(doc); for (const key of keys) { if (long_1.default.isLong(doc[key])) { doc[key] = doc[key].toNumber(); } } return doc; }; /** * Remove arangodb specific fields. * @param {Object} document A document returned from arangodb. * @return {Object} A clone of the document without arangodb specific fields. */ const sanitizeOutputFields = (document) => { const doc = _.clone(document); _.unset(doc, '_id'); _.unset(doc, '_key'); _.unset(doc, '_rev'); return doc; }; exports.sanitizeOutputFields = sanitizeOutputFields; const sanitizeInputFields = (document) => { const doc = ensureDatatypes(document); return ensureKey(doc); }; exports.sanitizeInputFields = sanitizeInputFields; /** * Auto-casting reference value by using native function of arangoDB * * @param {string} key * @param {object} value - raw value optional * @return {object} interpreted value */ const autoCastKey = (key, value) => { if (_.isDate(value)) { // Date return `DATE_TIMESTAMP(node.${key})`; } return 'node.' + key; }; exports.autoCastKey = autoCastKey; /** * Auto-casting raw data * * @param {object} value - raw value * @returns {any} interpreted value */ const autoCastValue = (value) => { if (_.isArray(value)) { return value.map(value => value.toString()); } if (_.isString(value)) { // String return value; } if (_.isBoolean(value)) { // Boolean return Boolean(value); } if (_.isNumber(value)) { return _.toNumber(value); } if (long_1.default.isLong(value)) { return value.toNumber(); } if (_.isDate(value)) { // Date return new Date(value); } return value; }; exports.autoCastValue = autoCastValue; /** * Links children of filter together via a comparision operator. * @param {any} filter * @param {string} op comparision operator * @param {number} index to keep track of bind variables * @param {any} bindVarsMap mapping of keys to values for bind variables * @return {any} query template string and bind variables */ const buildComparison = (filter, op, index, bindVarsMap) => { const ele = _.map(filter, (e) => { if (!_.isArray(e)) { e = [e]; } e = (0, exports.buildFilter)(e, index, bindVarsMap); index += 1; return e.q; }); let q = '( '; for (let i = 0; i < ele.length; i += 1) { if (i == ele.length - 1) { q = `${q} ${ele[i]} )`; } else { q = `${q} ${ele[i]} ${op} `; } } return { q, bindVarsMap }; }; exports.buildComparison = buildComparison; /** * Creates a filter key, value. * When the value is a string, boolean, number or date a equal comparision is created. * Otherwise if the key corresponds to a known operator, the operator is constructed. * @param {string} key * @param {string|boolean|number|date|object} value * @param {number} index to keep track of bind variables * @param {any} bindVarsMap mapping of keys to values for bind variables * @return {String} query template string */ const buildField = (key, value, index, bindVarsMap) => { const bindValueVar = `@value${index}`; const bindValueVarWithOutPrefix = `value${index}`; if (_.isString(value) || _.isBoolean(value) || _.isNumber(value || _.isDate(value))) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value); return (0, exports.autoCastKey)(key, value) + ' == ' + bindValueVar; } if (!_.isNil(value.$eq)) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$eq); return (0, exports.autoCastKey)(key, value) + ' == ' + bindValueVar; } if (value.$gt) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$gt); return (0, exports.autoCastKey)(key, value) + ' > ' + bindValueVar; } if (value.$gte) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$gte); return (0, exports.autoCastKey)(key, value) + ' >= ' + bindValueVar; } if (value.$lt) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$lt); return (0, exports.autoCastKey)(key, value) + ' < ' + bindValueVar; } if (value.$lte) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$lte); return (0, exports.autoCastKey)(key, value) + ' <= ' + bindValueVar; } if (!_.isNil(value.$ne)) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$ne); return (0, exports.autoCastKey)(key, value) + ' != ' + bindValueVar; } if (value.$inVal) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$inVal); return bindValueVar + ' IN ' + (0, exports.autoCastKey)(key, value); } if (value.$in) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$in); if (_.isString(value.$in)) { // if it is a field which should be an array // (useful for querying within a document list-like attributen return bindValueVar + ' IN ' + (0, exports.autoCastKey)(key); } // assuming it is a list of provided values return (0, exports.autoCastKey)(key, value) + ' IN ' + bindValueVar; } if (value.$nin) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$nin); return (0, exports.autoCastKey)(key, value) + ' NOT IN ' + bindValueVar; } if (value.$iLike) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(value.$iLike); // @param 'true' is for case insensitive return ' LIKE (' + (0, exports.autoCastKey)(key, value) + ',' + bindValueVar + ', true)'; } if (!_.isNil(value.$not)) { const temp = (0, exports.buildField)(key, value.$not, index, bindVarsMap); return `!(${temp})`; } if (_.has(value, '$isEmpty')) { bindVarsMap[bindValueVarWithOutPrefix] = (0, exports.autoCastValue)(''); // will always search for an empty string return (0, exports.autoCastKey)(key, '') + ' == ' + bindValueVar; } if (!_.isNil(value.$startswith)) { const bindValueVar1 = `@value${index + 1}`; const bindValueVarWithOutPrefix1 = `value${index + 1}`; const k = (0, exports.autoCastKey)(key); const v = (0, exports.autoCastValue)(value.$startswith); bindVarsMap[bindValueVarWithOutPrefix] = v; bindVarsMap[bindValueVarWithOutPrefix1] = v; return `LEFT(${k}, LENGTH(${bindValueVar})) == ${bindValueVar1}`; } if (!_.isNil(value.$endswith)) { const bindValueVar1 = `@value${index + 1}`; const bindValueVarWithOutPrefix1 = `value${index + 1}`; const k = (0, exports.autoCastKey)(key); const v = (0, exports.autoCastValue)(value.$endswith); bindVarsMap[bindValueVarWithOutPrefix] = v; bindVarsMap[bindValueVarWithOutPrefix1] = v; return `RIGHT(${k}, LENGTH(${bindValueVar})) == ${bindValueVar1}`; } throw new Error(`unsupported operator ${_.keys(value)} in ${key}`); }; exports.buildField = buildField; /** * Build ArangoDB query based on filter. * @param {Object} filter key, value tree object. * @param {number} index to keep track of bind variables * @param {any} bindVarsMap mapping of keys to values for bind variables * @return {any} query template string and bind variables */ const buildFilter = (filter, index, bindVarsMap) => { if (!index) { index = 0; } if (!bindVarsMap) { bindVarsMap = {}; } if (filter.length > 0) { let q = ''; let multipleFilters = false; for (const eachFilter of filter) { _.forEach(eachFilter, (value, key) => { switch (key) { case '$or': if (!multipleFilters) { if (_.isEmpty(value)) { q = true; } else { q = (0, exports.buildComparison)(value, '||', index, bindVarsMap).q; } multipleFilters = true; // since there is a possiblility for recursive call from buildComparision to buildFilter again. index += 1; } else { q = q + '&& ' + (0, exports.buildComparison)(value, '||', index, bindVarsMap).q; index += 1; } break; case '$and': if (!multipleFilters) { if (_.isEmpty(value)) { q = false; } else { q = (0, exports.buildComparison)(value, '&&', index, bindVarsMap).q; } multipleFilters = true; index += 1; } else { q = q + '&& ' + (0, exports.buildComparison)(value, '&&', index, bindVarsMap).q; index += 1; } break; default: if (_.startsWith(key, '$')) { throw new Error(`unsupported query operator ${key}`); } if (!multipleFilters) { q = (0, exports.buildField)(key, value, index, bindVarsMap); multipleFilters = true; index += 1; } else { q = q + ' && ' + (0, exports.buildField)(key, value, index, bindVarsMap); index += 1; } break; } }); } return { q, bindVarsMap }; } }; exports.buildFilter = buildFilter; /** * Build count and offset filters. * @param {Object} options query options * @return {String} template query string */ const buildLimiter = (options) => { // LIMIT count // LIMIT offset, count if (!_.isNil(options.limit)) { if (!_.isNil(options.offset)) { return `LIMIT @offset, @limit`; } return `LIMIT @limit`; } return ''; }; exports.buildLimiter = buildLimiter; /** * Build sort filter. * @param {Object} options query options * @param {number} index to keep track of bind variables * @param {any} bindVarsMap Object containing bind key to values * @return {any} template query string and bind variables Object */ const buildSorter = (options, index, bindVarsMap) => { if (_.isNil(options.sort) || _.isEmpty(options.sort)) { return ''; } if (!index) { index = 0; } if (!bindVarsMap) { bindVarsMap = {}; } const sort = _.mapKeys(options.sort, (value, key) => { return (0, exports.autoCastKey)(key); }); let sortKeysOrder = ''; let i = 1; const objLength = Object.keys(sort).length; for (const key in sort) { if (objLength == i) { // Do not append ',' for the last element sortKeysOrder = `${sortKeysOrder} ${key} ${sort[key]} `; } else { sortKeysOrder = `${sortKeysOrder} ${key} ${sort[key]},`; } i += 1; } return 'SORT ' + sortKeysOrder; }; exports.buildSorter = buildSorter; const buildReturn = (options) => { let excludeIndex = 0; let includeIndex = 0; const bindVarsMap = {}; let q = ''; if (_.isNil(options.fields) || _.isEmpty(options.fields)) { return { q, bindVarsMap }; } const keep = []; const exclude = []; _.forEach(options.fields, (value, key) => { switch (value) { case 0: bindVarsMap[`exclude${excludeIndex}`] = key; exclude.push(`@exclude${excludeIndex}`); excludeIndex += 1; break; case 1: default: bindVarsMap[`include${includeIndex}`] = key; keep.push(`@include${includeIndex}`); includeIndex += 1; } }); if (keep.length > 0) { const include = _.join(_.map(keep, (e) => { return e; })); q = `RETURN KEEP( node, ${include} )`; return { q, bindVarsMap }; } if (exclude.length > 0) { const unset = _.join(_.map(exclude, (e) => { return e; })); q = `RETURN UNSET( node, ${unset} )`; return { q, bindVarsMap }; } q = 'RETURN result'; return { q, bindVarsMap }; }; exports.buildReturn = buildReturn; const encodeMessage = (object) => { return Buffer.from(JSON.stringify(object)); }; exports.encodeMessage = encodeMessage; //# sourceMappingURL=common.js.map