@restorecommerce/chassis-srv
Version:
Restore Commerce microservice chassis
443 lines • 16.3 kB
JavaScript
;
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