UNPKG

mongoose-diff-tracking

Version:
568 lines (464 loc) 17.1 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); exports.assign = assign; exports.assignToProp = assignToProp; exports.assignWithRestoration = assignWithRestoration; exports.assignToPropWithRestoration = assignToPropWithRestoration; var _fastDeepEqual = require('fast-deep-equal'); var _fastDeepEqual2 = _interopRequireDefault(_fastDeepEqual); var _powerFilter = require('power-filter'); var _oadUtils = require('oad-utils'); var _retargetToProp = require('./retarget-to-prop.js'); var _getObjectsToBeAssigned = require('./get-objects-to-be-assigned.js'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * */ var PowerAssign = function () { function PowerAssign() { _classCallCheck(this, PowerAssign); } _createClass(PowerAssign, null, [{ key: 'assignUpdateOperation', /** * */ value: function assignUpdateOperation(obj, uOp) { var updatedObj = obj; var operatorNames = Object.keys(uOp); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = operatorNames[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var operatorName = _step.value; switch (operatorName) { case '$inc': updatedObj = this.$inc(updatedObj, uOp.$inc); break; case '$set': updatedObj = this.$set(updatedObj, uOp.$set); break; case '$min': updatedObj = this.$min(updatedObj, uOp.$min); break; case '$max': updatedObj = this.$max(updatedObj, uOp.$max); break; case '$mul': updatedObj = this.$mul(updatedObj, uOp.$mul); break; case '$addToSet': updatedObj = this.$addToSet(updatedObj, uOp.$addToSet); break; case '$pop': updatedObj = this.$pop(updatedObj, uOp.$pop); break; case '$pull': updatedObj = this.$pull(updatedObj, uOp.$pull); break; case '$push': updatedObj = this.$push(updatedObj, uOp.$push); break; case '$currentDate': updatedObj = this.$currentDate(updatedObj, uOp.$currentDate); break; case '$bit': updatedObj = this.$bit(updatedObj, uOp.$bit); break; case '$unset': updatedObj = this.$unset(updatedObj, uOp.$unset); break; case '$rename': updatedObj = this.$rename(updatedObj, uOp.$rename); break; case '$restore': // this operation must run at the end of all other opreations break; case '$setOnInsert': throw new Error('The given operator "' + operatorName + '" is not implemented yet.'); default: throw new Error('Invalid operator: "' + operatorName + '"'); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } if (uOp.$restore != null) { updatedObj = this.$restore(obj, updatedObj, uOp.$restore); } return updatedObj; } /** * */ }, { key: '$set', value: function $set(obj, setOp) { var _this = this; var updatedObj = obj; Object.keys(setOp).forEach(function (docPath) { updatedObj = _this.setValue(updatedObj, docPath, setOp[docPath]); }); return updatedObj; } /** * */ }, { key: '$inc', value: function $inc(obj, incOp) { var valuesToSet = {}; Object.keys(incOp).forEach(function (docPath) { var currentVal = (0, _oadUtils.getNestedValue)(obj, docPath); var inc = incOp[docPath]; valuesToSet[docPath] = currentVal + inc; }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$min', value: function $min(obj, minOp) { var valuesToSet = {}; Object.keys(minOp).forEach(function (docPath) { var currentVal = (0, _oadUtils.getNestedValue)(obj, docPath); var newVal = minOp[docPath]; if (newVal < currentVal) { valuesToSet[docPath] = newVal; } }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$max', value: function $max(obj, maxOp) { var valuesToSet = {}; Object.keys(maxOp).forEach(function (docPath) { var currentVal = (0, _oadUtils.getNestedValue)(obj, docPath); var newVal = maxOp[docPath]; if (newVal > currentVal) { valuesToSet[docPath] = newVal; } }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$mul', value: function $mul(obj, mulOp) { var valuesToSet = {}; Object.keys(mulOp).forEach(function (docPath) { var currentNum = (0, _oadUtils.getNestedValue)(obj, docPath); if (currentNum == null) { throw Error('operand must not be null'); } valuesToSet[docPath] = currentNum * mulOp[docPath]; }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$addToSet', value: function $addToSet(obj, addToSetOp) { var valuesToSet = {}; Object.keys(addToSetOp).forEach(function (docPath) { var arr = (0, _oadUtils.getNestedValue)(obj, docPath); if (arr == null) { arr = []; // If the field is absent, empty array is set. } if (!Array.isArray(arr)) { throw new Error('"$addToSet" operator must be applied to an array. DocumentPath: "' + docPath + '".'); } var modifier = addToSetOp[docPath]; // $FlowIssue(arr-is-Array) var newArr = modifier.$each.filter(function (element) { return !arr.some(function (arrEl) { return (0, _fastDeepEqual2.default)(arrEl, element); }); }); valuesToSet[docPath] = arr.concat(newArr); }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$pop', value: function $pop(obj, popOp) { var valuesToSet = {}; Object.keys(popOp).forEach(function (docPath) { var arr = (0, _oadUtils.getNestedValue)(obj, docPath).slice(); if (arr == null) { arr = []; // If the field is absent, empty array is set. } if (!Array.isArray(arr)) { throw new Error('"$pop" operator must be applied to an array. DocumentPath: "' + docPath + '".'); } if (popOp[docPath] === 1) { arr.pop(); } else { arr.shift(); } valuesToSet[docPath] = arr; }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$pull', value: function $pull(obj, pullOp) { var valuesToSet = {}; Object.keys(pullOp).forEach(function (docPath) { var arr = (0, _oadUtils.getNestedValue)(obj, docPath); if (arr == null) { return; // If the field is absent, no requests will be executed } if (!Array.isArray(arr)) { throw new Error('"$pull" operator must be applied to an array. DocumentPath: "' + docPath + '".'); } var condition = pullOp[docPath]; valuesToSet[docPath] = arr.filter(function (val) { return (0, _powerFilter.checkCondition)(val, condition) === false; }); }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$push', value: function $push(obj, pushOp) { var valuesToSet = {}; Object.keys(pushOp).forEach(function (docPath) { var _newArr; var arr = (0, _oadUtils.getNestedValue)(obj, docPath); if (arr == null) { arr = []; // If the field is absent, empty array is set. } if (!Array.isArray(arr)) { throw new Error('"$push" operator must be applied to an array. DocumentPath: "' + docPath + '".'); } var modifier = pushOp[docPath]; var position = modifier.$position != null ? modifier.$position : arr.length; var newArr = arr.slice(); (_newArr = newArr).splice.apply(_newArr, [position, 0].concat(_toConsumableArray(modifier.$each))); if (modifier.$sort != null) { newArr = (0, _oadUtils.sortByNotation)(newArr, modifier.$sort); } if (modifier.$slice != null) { newArr = modifier.$slice >= 0 ? newArr.slice(0, modifier.$slice) : newArr.slice(modifier.$slice); } valuesToSet[docPath] = newArr; }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$currentDate', value: function $currentDate(obj, curDateOp) { var valuesToSet = {}; Object.keys(curDateOp).forEach(function (docPath) { var typeSpecification = curDateOp[docPath]; if (typeSpecification === true) { typeSpecification = { $type: 'date' }; } var now = new Date(); valuesToSet[docPath] = typeSpecification.$type === 'date' ? now : now.getTime(); }); return this.$set(obj, valuesToSet); } /** * */ }, { key: '$bit', value: function $bit(obj, bitOp) { var valuesToSet = {}; Object.keys(bitOp).forEach(function (docPath) { var currentNum = (0, _oadUtils.getNestedValue)(obj, docPath) || 0; // If the field is absent, 0 is set. var logicalOperator = Object.keys(bitOp[docPath])[0]; // $FlowIssue(return-number) var operand = bitOp[docPath][logicalOperator]; switch (logicalOperator) { case 'and': valuesToSet[docPath] = currentNum & operand; break; case 'or': valuesToSet[docPath] = currentNum | operand; break; case 'xor': valuesToSet[docPath] = currentNum ^ operand; break; } }); return this.$set(obj, valuesToSet); } /** * Unset the value of the given DocumentPaths. * NOTICE: The objects whose property are deleted will be converted into a plain object. */ }, { key: '$unset', value: function $unset(obj, unsetOp) { var _this2 = this; return Object.keys(unsetOp).reduce(function (newObj, docPath) { var attrs = (0, _oadUtils.parseDocumentPath)(docPath); var lastAttr = attrs.pop(); var pathToLast = _oadUtils.createDocumentPath.apply(undefined, _toConsumableArray(attrs)); var lastObj = pathToLast ? (0, _oadUtils.getNestedValue)(newObj, pathToLast) : newObj; var copiedLastObj = void 0; if (Array.isArray(lastObj)) { copiedLastObj = lastObj.slice(); copiedLastObj[lastAttr] = null; } else { copiedLastObj = Object.assign({}, lastObj); delete copiedLastObj[lastAttr]; } return pathToLast ? _this2.$set(newObj, _defineProperty({}, pathToLast, copiedLastObj)) : copiedLastObj; }, obj); } /** * Unset the value of the given DocumentPaths. * NOTICE: The objects whose property are renamed will be converted into a plain object. */ }, { key: '$rename', value: function $rename(obj, renameOp) { var _this3 = this; return Object.keys(renameOp).reduce(function (newObj, docPath) { var attrs = (0, _oadUtils.parseDocumentPath)(docPath); var lastAttr = attrs.pop(); var pathToLast = _oadUtils.createDocumentPath.apply(undefined, _toConsumableArray(attrs)); var lastObj = pathToLast ? (0, _oadUtils.getNestedValue)(newObj, pathToLast) : newObj; if (Array.isArray(lastObj)) { throw Error('$rename operation cannot be applied to array field: "' + pathToLast + '".'); } if (!lastObj.hasOwnProperty(lastAttr)) { return newObj; } var copiedLastObj = Object.assign({}, lastObj); delete copiedLastObj[lastAttr]; var newAttr = renameOp[docPath]; copiedLastObj[newAttr] = lastObj[lastAttr]; return pathToLast ? _this3.$set(newObj, _defineProperty({}, pathToLast, copiedLastObj)) : copiedLastObj; }, obj); } /** * */ }, { key: '$restore', value: function $restore(originalObj, targetObj, restoreOp) { var valuesToSet = {}; Object.keys(restoreOp).forEach(function (docPath) { var currentValue = (0, _oadUtils.getNestedValue)(targetObj, docPath); if (isPrimitive(currentValue)) { valuesToSet[docPath] = currentValue; return; } var Constructor = restoreOp[docPath]; if (!Constructor) { var originalValue = (0, _oadUtils.getNestedValue)(originalObj, docPath); Constructor = originalValue.constructor; } valuesToSet[docPath] = new Constructor(currentValue); }); return this.$set(targetObj, valuesToSet); } /** * */ }, { key: 'setValue', value: function setValue(obj, docPath, value) { var revObjsToBeAssigned = (0, _getObjectsToBeAssigned.getObjectsToBeAssigned)(obj, docPath).reverse(); var revKeys = (0, _oadUtils.parseDocumentPath)(docPath).reverse(); // assert(objsToBeAssigned.length === keys.length) // $FlowIssue(return-T) return revKeys.reduce(function (newValue, key, i) { var objToBeAssigned = revObjsToBeAssigned[i]; if (typeof key === 'number' && Array.isArray(objToBeAssigned)) { var shallowCopied = objToBeAssigned.slice(); shallowCopied[key] = newValue; return shallowCopied; } return Object.assign({}, objToBeAssigned, _defineProperty({}, key, newValue)); }, value); } }]); return PowerAssign; }(); /** * @public * Assign new values to object following the given operation(s). */ exports.default = PowerAssign; function assign(obj) { for (var _len = arguments.length, uOps = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { uOps[_key - 1] = arguments[_key]; } return uOps.reduce(function (ret, uOp) { return PowerAssign.assignUpdateOperation(ret, (0, _oadUtils.normalizeUpdateOperation)(uOp)); }, obj); } /** * Assign new values to the property of the obj located at the given documentPath following the given operation. */ function assignToProp(obj, docPath, uOp) { var modifiedOps = (0, _retargetToProp.retargetToProp)(docPath, uOp); return assign(obj, modifiedOps); } /** * */ function assignWithRestoration(obj, uOp) { var updatedObj = assign(obj, uOp); var Constructor = obj.constructor; return new Constructor(updatedObj); // if Constructor is Object, it's OK! } /** * */ function assignToPropWithRestoration(obj, docPath, uOp) { var updatedObj = assignToProp(obj, docPath, uOp); var Constructor = obj.constructor; return new Constructor(updatedObj); // if Constructor is Object, it's OK! } function isPrimitive(value) { var t = typeof value === 'undefined' ? 'undefined' : _typeof(value); return value == null || t != 'object' && t != 'function'; }