mongoose-diff-tracking
Version:
Manage Mongo Collection diff History and versions
568 lines (464 loc) • 17.1 kB
JavaScript
;
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';
}