enhancv/mongoose-originals

View on GitHub
src/index.js

Summary

Maintainability
A
2 hrs
Test Coverage
const { isEqual, pick } = require("lodash/fp");

function eachMongooseOriginalsPath(item, fn, name) {
    if (item && item.schema) {
        if (item.schema.mongooseOriginals) {
            fn(item);
        }
        item.schema.eachPath((name, type) => {
            if (item[name] instanceof Array) {
                item[name].forEach(item => eachMongooseOriginalsPath(item, fn, name));
            } else {
                eachMongooseOriginalsPath(item[name], fn, name);
            }
        });
    }
}

function mongooseOriginals(schema, userOptions) {
    var options = Object.assign({ methods: true }, userOptions);

    if (!options.fields) {
        throw new Error("No fields specified for mongoose originals on schema");
    }

    schema.method("isChanged", function isChanged() {
        return (
            !this._original ||
            !isEqual(
                this._original,
                pick(options.fields, this.toObject({ depopulate: true, getters: false, transform: false }))
            )
        );
    });

    schema.method("setSnapshotOriginal", function setSnapshotOriginal() {
        eachMongooseOriginalsPath(this, item => {
            item.snapshotOriginal = item._original;
        });
        return this;
    });

    schema.method("clearSnapshotOriginal", function clearSnapshotOriginal() {
        eachMongooseOriginalsPath(this, item => {
            delete item.snapshotOriginal;
        });
        return this;
    });

    function saveOriginalNamed() {
        this._original = {};
        const newValues = this.toObject({ depopulate: true, getters: false, transform: false });

        options.fields.forEach(name => {
            this._original[name] = newValues[name];
        });
    }

    schema.method("initOriginals", function initOriginals() {
        if (this._original === undefined) {
            saveOriginalNamed.bind(this)();
        }
    });

    schema.mongooseOriginals = true;
    schema.post("init", saveOriginalNamed);
    schema.post("save", saveOriginalNamed);

    if (options.methods) {
        schema.methods.collectionAdded = function collectionAdded(name) {
            var _this = this;

            return this[name].filter(function(item) {
                return !_this._original[name].find(function(_originalItem) {
                    return item._id.equals(_originalItem._id);
                });
            });
        };

        schema.methods.collectionRemoved = function collectionRemoved(name) {
            var _this = this;

            return this._original[name].filter(function(_originalItem) {
                return !_this[name].find(function(item) {
                    return item._id.equals(_originalItem._id);
                });
            });
        };

        schema.methods.collectionUpdated = function collectionUpdated(name) {
            var _this = this;

            return this[name].filter(function(item) {
                return _this._original[name].find(function(_originalItem) {
                    return item._id.equals(_originalItem._id);
                });
            });
        };
    }
}

module.exports = mongooseOriginals;