chrobject
Version:
Stores chronicles of plain objects as diffs and snapshots
239 lines (238 loc) • 10.6 kB
JavaScript
/**
* Creator: Christian Hotz
* Company: hydra newmedia GmbH
* Date: 11.06.16
*
* Copyright hydra newmedia GmbH
*/
;
/**
* Imports
*/
var _ = require('lodash');
var hash = require('object-hash');
var Snapshot_1 = require('../utils/Snapshot');
var Diff_1 = require('../utils/Diff');
var DeepDiff_1 = require('../utils/DeepDiff');
var EntryAppService = (function () {
function EntryAppService(entity, storage, options) {
this.entity = entity;
this.storage = storage;
this.options = { ignoreProperties: [], ignoreSubProperties: [] };
if (options) {
this.options.ignoreProperties = options.ignoreProperties || [];
this.options.ignoreSubProperties = options.ignoreSubProperties || [];
}
}
EntryAppService.prototype.getDiffs = function (condition, callback) {
if (!condition) {
condition = {};
}
this.storage.findDiffsByCondition(condition, this.entity, callback);
};
EntryAppService.prototype.getSnapshotById = function (id, callback) {
this.storage.findSnapshotById(id, this.entity, callback);
};
EntryAppService.prototype.saveSnapshotAndDiff = function (obj, creator, timestamp, callback) {
var _this = this;
var id = _.get(obj, this.entity.idPath);
this.storage.findLatestSnapshotBefore(id, timestamp, this.entity, function (err, oldSnap) {
if (err) {
return callback(err);
}
var newSnap = new Snapshot_1.Snapshot(obj, _this.entity, creator, timestamp);
var diff;
if (oldSnap) {
diff = _this.diff(oldSnap, newSnap);
}
else {
var blankSnap = new Snapshot_1.Snapshot({}, _this.entity, creator, timestamp);
diff = _this.diff(blankSnap.setObjId(newSnap.objId), newSnap);
}
if (!diff.isEmpty()) {
_this.storage.insertSnapshot(newSnap, function (err, newSnap) {
if (err) {
return callback(err);
}
diff.linkToId(newSnap.id);
_this.storage.insertDiff(diff, function (err, diff) {
if (callback) {
callback(err, { snapshot: newSnap, diff: diff });
}
});
});
}
else {
callback(null, { snapshot: null, diff: null });
}
});
};
EntryAppService.prototype.saveSnapshot = function (obj, creator, timestamp, callback) {
var _this = this;
var id = _.get(obj, this.entity.idPath);
this.storage.findLatestSnapshotBefore(id, timestamp, this.entity, function (err, oldSnap) {
if (err) {
return callback(err);
}
var newSnap = new Snapshot_1.Snapshot(obj, _this.entity, creator, timestamp);
var diff;
if (oldSnap) {
diff = _this.diff(oldSnap, newSnap);
}
else {
var blankSnap = new Snapshot_1.Snapshot({}, _this.entity, creator, timestamp);
diff = _this.diff(blankSnap.setObjId(newSnap.objId), newSnap);
}
if (!diff.isEmpty()) {
_this.storage.insertSnapshot(newSnap, callback);
}
else {
callback(null, null);
}
});
};
EntryAppService.prototype.saveDiff = function (obj, creator, timestamp, callback) {
var _this = this;
var id = _.get(obj, this.entity.idPath);
this.storage.findLatestSnapshotBefore(id, timestamp, this.entity, function (err, oldSnap) {
if (err) {
return callback(err);
}
var newSnap = new Snapshot_1.Snapshot(obj, _this.entity, creator, timestamp, oldSnap ? oldSnap.id : undefined);
var diff;
if (oldSnap) {
diff = _this.diff(oldSnap, newSnap);
}
else {
var blankSnap = new Snapshot_1.Snapshot({}, _this.entity, creator, timestamp);
diff = _this.diff(blankSnap.setObjId(newSnap.objId), newSnap);
}
if (!diff.isEmpty()) {
_this.storage.upsertSnapshot(newSnap, function (err, newSnap) {
if (err) {
return callback(err);
}
else {
_this.storage.insertDiff(diff, callback ? callback : undefined);
}
});
}
else {
callback(null, null);
}
});
};
EntryAppService.prototype.diff = function (snapOne, snapTwo) {
// Check if snapshots are of same object
if (snapOne.objId !== snapTwo.objId) {
throw new Error('Diffed snapshots are not of same object');
}
// Check if snapshots are of same entity
if (!snapOne.entity.equals(snapTwo.entity)) {
throw new Error('Diffed snapshots are not of same entity');
}
// swap snapshots if snapTwo was taken before snapOne
if (snapOne.timestamp > snapTwo.timestamp) {
var tmp = snapOne;
snapOne = snapTwo;
snapTwo = tmp;
}
// diff between the two snapshots
var diffObj = this.deepDiff(snapOne.obj, snapTwo.obj);
return new Diff_1.Diff(diffObj, snapTwo.objId, snapTwo.entity, snapTwo.creator, snapTwo.timestamp);
};
EntryAppService.prototype.deepDiff = function (one, two, path) {
var _this = this;
if (path === void 0) { path = ''; }
var result = [];
var unsetIgnoredSubProperties = function (obj) {
if (_.isPlainObject(obj)) {
for (var _i = 0, _a = _this.options.ignoreSubProperties; _i < _a.length; _i++) {
var prop = _a[_i];
_.unset(obj, prop);
}
}
};
_.keys(one).forEach(function (key) {
var concatPath = path ? path + '.' + key : key;
if (!_.includes(_this.options.ignoreProperties, concatPath) && !_.includes(_this.options.ignoreSubProperties, concatPath)) {
unsetIgnoredSubProperties(one[key]);
unsetIgnoredSubProperties(two[key]);
var getDeletedProperties_1 = function (obj, propPath) {
if (propPath === void 0) { propPath = null; }
if (_.isPlainObject(obj)) {
for (var _i = 0, _a = _.keys(obj); _i < _a.length; _i++) {
var objKey = _a[_i];
unsetIgnoredSubProperties(obj[objKey]);
getDeletedProperties_1(obj[objKey], propPath ? propPath + '.' + objKey : objKey);
}
}
else if (_.isBoolean(obj) || _.isDate(obj) || _.isNumber(obj)
|| _.isNull(obj) || _.isRegExp(obj) || _.isString(obj) || _.isArray(obj)) {
result.push(new DeepDiff_1.DeepDiff('deleted', propPath, obj, null));
}
};
if (_.isPlainObject(one[key])) {
if (!_.has(two, key)) {
getDeletedProperties_1(one[key], concatPath);
}
else {
result = _.concat(result, _this.deepDiff(one[key], two[key], path ? path + '.' + key : key));
}
}
else if (_.isBoolean(one[key]) || _.isDate(one[key]) || _.isNumber(one[key])
|| _.isNull(one[key]) || _.isRegExp(one[key]) || _.isString(one[key])) {
if (!_.has(two, key)) {
result.push(new DeepDiff_1.DeepDiff('deleted', concatPath, one[key], null));
}
else if (_.isDate(one[key]) || _.isDate(two[key])) {
if (new Date(one[key]).valueOf() !== new Date(two[key]).valueOf()) {
result.push(new DeepDiff_1.DeepDiff('edited', concatPath, new Date(one[key]), new Date(two[key])));
}
}
else if (hash(one[key]) !== hash(two[key])) {
result.push(new DeepDiff_1.DeepDiff('edited', concatPath, one[key], two[key]));
}
}
else if (_.isArray(one[key]) && _.isArray(two[key]) && !_.isEqual(one[key], two[key])) {
for (var i = 0; i < one[key].length; i++) {
unsetIgnoredSubProperties(one[key][i]);
}
for (var i = 0; i < two[key].length; i++) {
unsetIgnoredSubProperties(two[key][i]);
}
if (hash(one[key]) !== hash(two[key])) {
result.push(new DeepDiff_1.DeepDiff('array', concatPath, one[key], two[key]));
}
}
else if (!_.has(two, key)) {
getDeletedProperties_1(one[key], concatPath);
}
}
});
var getCreatedProperties = function (obj, path) {
if (path === void 0) { path = null; }
for (var _i = 0, _a = _.keys(path ? _.get(obj, path) : obj); _i < _a.length; _i++) {
var key = _a[_i];
var concatPath = path ? path + '.' + key : key;
var val = _.get(two, concatPath);
unsetIgnoredSubProperties(val);
if (!_.includes(_this.options.ignoreProperties, concatPath)) {
if (_.isBoolean(val) || _.isDate(val) || _.isNumber(val)
|| _.isNull(val) || _.isRegExp(val) || _.isString(val) || _.isArray(val)) {
if (!_.has(one, concatPath)) {
result.push(new DeepDiff_1.DeepDiff('created', concatPath, null, val));
}
}
else if (_.isPlainObject(val)) {
getCreatedProperties(obj, concatPath);
}
}
}
};
getCreatedProperties(two, path);
return result;
};
return EntryAppService;
}());
exports.EntryAppService = EntryAppService;