From 2dbdb70ab1de9e5cc7ef9d1c826466e7229f91e7 Mon Sep 17 00:00:00 2001 From: t4nz Date: Fri, 22 Jun 2018 16:29:48 +0200 Subject: [PATCH] async/await --- package.json | 29 +++-- src/index.ts | 284 +++++++++++++++++++++++++++--------------------- src/omitDeep.ts | 49 +++------ yarn.lock | 46 +++++--- 4 files changed, 224 insertions(+), 184 deletions(-) diff --git a/package.json b/package.json index a61d670..1158f5a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,21 @@ "name": "mongoose-jsondiffpatch", "version": "1.1.1", "description": "Mongoose collections history using jsondiffpatch", + "keywords": [ + "mongoose", + "diff", + "history", + "versioning", + "jsondiffpatch" + ], + "homepage": "https://github.com/t4nz/mongoose-jsondiffpatch", + "license": "MIT", "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/t4nz/mongoose-jsondiffpatch" + }, "author": { "name": "Gaetano Fiorello", "email": "gfiorello@gmail.com" @@ -16,21 +30,14 @@ "build": "tsc -p tsconfig.json" }, "dependencies": { + "is-plain-object": "^2.0.4", "jsondiffpatch": "^0.3.10" }, "devDependencies": { - "@types/mongoose": "^5.0.15", - "mongoose": "^5.1.4" + "@types/mongoose": "^5.0.18", + "mongoose": "^5.1.6" }, "engines": { "node": "*" - }, - "keywords": [ - "mongoose", - "diff", - "history", - "versioning", - "jsondiffpatch" - ], - "license": "MIT" + } } diff --git a/src/index.ts b/src/index.ts index 7444f18..572c479 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,14 @@ import * as jsondiffpatch from 'jsondiffpatch'; -import { Document, Model, Mongoose, Promise, Schema, Types } from 'mongoose'; +import { Document, Model, Mongoose, Schema, Types, Query } from 'mongoose'; import { HistoryDocument, HistorySchema } from './HistoryModel'; -import omit from './omitDeep'; +import omitDeep from './omitDeep'; + +export interface PluginOptions { + mongoose: Mongoose; + omit?: string | string[]; + [k: string]: any; +} var History: Model; @@ -12,69 +18,88 @@ const diffPatcher = (jsondiffpatch as any).create({ /** * - * + * Compares documents and save a json diff object. * @param {Document} current Current document * @param {Document} orig Original document - * @param {*} updated Updated document or update params - * @param {*} opts Options passed by Mongoose Schema.plugin + * @param {*} updates Update params + * @param {*} pluginOpts Options passed by Mongoose Schema.plugin * @param {*} [meta] Extra meta data * @returns */ -function saveDiffObject(current: Document, orig: Document, updated: any, opts: any, meta?: any) { +async function saveDiffObject( + current: Document, + orig: Document, + updates: any = {}, + pluginOpts: PluginOptions, + meta?: any, +) { const { __user: user, __reason: reason, __update: update } = meta || current; - - const diff = diffPatcher.diff(JSON.parse(JSON.stringify(orig)), JSON.parse(JSON.stringify(updated || {}))); - - if (opts.omit) omit(diff, opts.omit); - if (!diff || !Object.keys(diff).length) return Promise.resolve(); - - const collectionId = current._id; - const collectionName = (current as any).constructor.modelName; - - return History.findOne({ - collectionId, - collectionName, - }) - .sort('-version') - .then(last => { - if (last && update === true) { - Object.assign(last, { diff, user, reason }); - return last.save(); - } - - const history = new History({ - collectionId, - collectionName, - diff, - user, - reason, - version: last ? last.version + 1 : 0, - }); - return history.save(); + const { omit } = pluginOpts; + + try { + let diff = diffPatcher.diff(JSON.parse(JSON.stringify(orig)), JSON.parse(JSON.stringify(updates))); + if (omit) diff = omitDeep(diff, omit); + + if (!diff || !Object.keys(diff).length) return; + + const collectionId = current._id; + const collectionName = (current as any).constructor.modelName; + + const last = await History.findOne({ + collectionId, + collectionName, + }).sort('-version'); + + if (last && update === true) { + Object.assign(last, { diff, user, reason }); + return last.save(); + } + + const history = new History({ + collectionId, + collectionName, + diff, + user, + reason, + version: last ? last.version + 1 : 0, }); + return history.save(); + } catch (err) { + throw err; + } } /** * * Save differences - * @param {*} query Query object - * @param {*} opts Options passed by Mongoose Schema.plugin - * @returns + * @param {Query} query + * @param {PluginOptions} pluginOpts */ -function saveDiffs(query: any, opts: any) { - return query - .find(query._conditions) - .lean(false) - .cursor() - .eachAsync((result: any) => { - const updates = query._update['$set'] || {}; - Object.keys(query._update).forEach(k => { - if (!k.startsWith('$')) updates[k] = query._update[k]; +async function saveDiffs(query: Query, pluginOpts: PluginOptions) { + const conditions = query.getQuery(); + const updates = query.getUpdate(); + const queryOptions = (query as any).options; + + try { + await query + .find(conditions) + .lean(false) + .cursor() + .eachAsync(async document => { + const changes = updates.$set || {}; + + Object.keys(updates) + .filter(k => !k.startsWith('$')) + .forEach(k => { + changes[k] = updates[k]; + }); + + const orig = Object.assign({}, ...Object.keys(changes).map(k => ({ [k]: document[k] }))); + await saveDiffObject(document, orig, changes, pluginOpts, queryOptions); }); - - const orig = Object.assign({}, ...Object.keys(updates).map(k => ({ [k]: result[k] }))); - return saveDiffObject(result, orig, updates, opts, query.options); - }); + } catch (err) { + throw err; + } } /** @@ -86,35 +111,34 @@ function saveDiffs(query: any, opts: any) { * @param {*} [queryOpts] Query options * @returns */ -function getVersion(this: Model, id: Types.ObjectId, version: string | number, queryOpts?: any) { - return this.findById(id, null, queryOpts) - .then(latest => { - return History.find( - { - collectionName: this.modelName, - collectionId: id, - version: { - $gte: typeof version === 'string' ? parseInt(version, 10) : version, - }, - }, - { - diff: 1, - version: 1, +async function getVersion(this: Model, id: Types.ObjectId, version: string | number, queryOpts?: any) { + try { + const latest = await this.findById(id, null, queryOpts); + await History.find( + { + collectionName: this.modelName, + collectionId: id, + version: { + $gte: typeof version === 'string' ? parseInt(version, 10) : version, }, - { - sort: '-version', - }, - ) - .lean() - .cursor() - .eachAsync(history => { - diffPatcher.unpatch(latest, history.diff); - }) - .then(() => latest || {}); - }) - .catch(err => { - throw err; - }); + }, + { + diff: 1, + version: 1, + }, + { + sort: '-version', + }, + ) + .lean() + .cursor() + .eachAsync(history => { + diffPatcher.unpatch(latest, history.diff); + }); + return latest || {}; + } catch (err) { + throw err; + } } /** @@ -125,21 +149,22 @@ function getVersion(this: Model, id: Types.ObjectId, version: string | * @param {*} [queryOpts] Query options * @returns */ -function getDiffs(this: Model, id: Types.ObjectId, queryOpts?: any) { - return History.find( - { - collectionName: this.modelName, - collectionId: id, - }, - null, - queryOpts, - ) - .lean() - .exec() - .then(histories => histories) - .catch(err => { - throw err; - }); +async function getDiffs(this: Model, id: Types.ObjectId, queryOpts?: any) { + try { + const histories = await History.find( + { + collectionName: this.modelName, + collectionId: id, + }, + null, + queryOpts, + ) + .lean() + .exec(); + return histories; + } catch (err) { + throw err; + } } /** @@ -148,52 +173,63 @@ function getDiffs(this: Model, id: Types.ObjectId, queryOpts?: any) { * @param {Mongoose} [opts.mongoose] Mongoose instance to use * @param {string|string[]} [opts.omit] fields to omit from diffs (ex. ['a', 'b.c.d']) */ -export default function mongooseJsonDiff(schema: Schema, opts: any = {}) { - if (opts.mongoose === undefined) { +export default function mongooseJsonDiff(schema: Schema, pluginOpts: PluginOptions) { + if (!pluginOpts || !pluginOpts.mongoose) { throw new Error('Please, pass mongoose while requiring mongoose-jsondiffpatch'); } - const mongoose: Mongoose = opts.mongoose; + const { mongoose } = pluginOpts; History = mongoose.model('History', HistorySchema); - if (opts.omit && typeof opts.omit === 'string') { - opts.omit = [opts.omit]; - } - - schema.pre('save', function(next) { + schema.pre('save', async function(next) { if (this.isNew) return next(); - this.collection - .findOne({ + try { + const orig = await this.collection.findOne({ _id: this._id, - }) - .then(orig => saveDiffObject(this, orig, this, opts)) - .then(() => next()) - .catch(next); + }); + + await saveDiffObject(this, orig, this.toJSON(), pluginOpts); + next(); + } catch (err) { + next(err); + } }); - schema.pre('findOneAndUpdate', function(next) { - saveDiffs(this, opts) - .then(() => next()) - .catch(next); + schema.pre('findOneAndUpdate', async function(next) { + try { + await saveDiffs(this, pluginOpts); + next(); + } catch (err) { + next(err); + } }); - schema.pre('update', function(next) { - saveDiffs(this, opts) - .then(() => next()) - .catch(next); + schema.pre('update', async function(next) { + try { + await saveDiffs(this, pluginOpts); + next(); + } catch (err) { + next(err); + } }); - schema.pre('updateOne', function(next) { - saveDiffs(this, opts) - .then(() => next()) - .catch(next); + schema.pre('updateOne', async function(next) { + try { + await saveDiffs(this as Query, pluginOpts); + next(); + } catch (err) { + next(err); + } }); - schema.pre('remove', function(next) { - saveDiffObject(this, this, null, opts) - .then(() => next()) - .catch(next); + schema.pre('remove', async function(next) { + try { + await saveDiffObject(this, this, {}, pluginOpts); + next(); + } catch (err) { + next(err); + } }); // assign a function to the "statics" object of schema diff --git a/src/omitDeep.ts b/src/omitDeep.ts index 1cdd14d..be2ab72 100644 --- a/src/omitDeep.ts +++ b/src/omitDeep.ts @@ -1,40 +1,27 @@ -function isPlainObject(v: any) { - return Object.prototype.toString.call(v) === '[object Object]'; +import * as isPlainObject from 'is-plain-object'; + +interface SourceObject { + [key: string]: any; } -export default function omitDeep(value: any, keys: string[]) { - if (typeof value === 'undefined') { - return {}; - } +type Source = SourceObject | SourceObject[]; +type Paths = string | string[]; - if (Array.isArray(value)) { - for (let i = 0; i < value.length; i++) { - value[i] = omitDeep(value[i], keys); - } - return value; - } +export default function omitDeep(source: Source, paths: Paths): Source { + if (!source) return source; - if (!isPlainObject(value)) { - return value; - } + const keys = Array.isArray(paths) ? paths : [paths]; + if (!keys.length) return source; - if (typeof keys === 'string') { - keys = [keys]; + if (Array.isArray(source)) { + return source.map(s => omitDeep(s, keys)); } - if (!Array.isArray(keys)) { - return value; + if (isPlainObject(source)) { + keys.forEach(key => delete source[key]); + Object.keys(source).forEach(key => { + source[key] = omitDeep(source[key], paths); + }); } - - for (let j = 0; j < keys.length; j++) { - delete value[keys[j]]; - } - - for (let key in value) { - if (value.hasOwnProperty(key)) { - value[key] = omitDeep(value[key], keys); - } - } - - return value; + return source; } diff --git a/yarn.lock b/yarn.lock index 2a5d10e..928c6ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,9 +20,9 @@ "@types/events" "*" "@types/node" "*" -"@types/mongoose@^5.0.15": - version "5.0.15" - resolved "http://10.1.2.119:4873/@types%2fmongoose/-/mongoose-5.0.15.tgz#80da6b1f5225ddcfd75aec03a633f5b7b4d05786" +"@types/mongoose@^5.0.18": + version "5.0.18" + resolved "http://10.1.2.119:4873/@types%2fmongoose/-/mongoose-5.0.18.tgz#4cb671e870f8312df86d734f9563a5c1b9d1ec6e" dependencies: "@types/events" "*" "@types/mongodb" "*" @@ -88,6 +88,16 @@ has-flag@^3.0.0: version "3.0.0" resolved "http://10.1.2.119:4873/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" +is-plain-object@^2.0.4: + version "2.0.4" + resolved "http://10.1.2.119:4873/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +isobject@^3.0.1: + version "3.0.1" + resolved "http://10.1.2.119:4873/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + jsondiffpatch@^0.3.10: version "0.3.10" resolved "http://10.1.2.119:4873/jsondiffpatch/-/jsondiffpatch-0.3.10.tgz#79b132ea4e50b370fd9f5b150625462a05fedcd2" @@ -95,9 +105,9 @@ jsondiffpatch@^0.3.10: chalk "^2.3.0" diff-match-patch "^1.0.0" -kareem@2.1.0: - version "2.1.0" - resolved "http://10.1.2.119:4873/kareem/-/kareem-2.1.0.tgz#d63197b57311830e4ceb3f34431f22f2de826a03" +kareem@2.2.1: + version "2.2.1" + resolved "http://10.1.2.119:4873/kareem/-/kareem-2.2.1.tgz#9950809415aa3cde62ab43b4f7b919d99816e015" lodash.get@4.4.2: version "4.4.2" @@ -107,32 +117,32 @@ lodash@^4.17.10: version "4.17.10" resolved "http://10.1.2.119:4873/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" -mongodb-core@3.0.8: - version "3.0.8" - resolved "http://10.1.2.119:4873/mongodb-core/-/mongodb-core-3.0.8.tgz#8d401f4eab6056c0d874a3d5844a4844f761d4d7" +mongodb-core@3.0.9: + version "3.0.9" + resolved "http://10.1.2.119:4873/mongodb-core/-/mongodb-core-3.0.9.tgz#8327410c88811013fb3e4ac7c4c670f324349be1" dependencies: bson "~1.0.4" require_optional "^1.0.1" -mongodb@3.0.9: - version "3.0.9" - resolved "http://10.1.2.119:4873/mongodb/-/mongodb-3.0.9.tgz#742e685e45ac9c3ead02a91c9ebaae7165bb2def" +mongodb@3.0.10: + version "3.0.10" + resolved "http://10.1.2.119:4873/mongodb/-/mongodb-3.0.10.tgz#f948cb9595adcbfcad7444f6b24a040b653b23e8" dependencies: - mongodb-core "3.0.8" + mongodb-core "3.0.9" mongoose-legacy-pluralize@1.0.2: version "1.0.2" resolved "http://10.1.2.119:4873/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4" -mongoose@^5.1.4: - version "5.1.4" - resolved "http://10.1.2.119:4873/mongoose/-/mongoose-5.1.4.tgz#eda83a5d81149c8114b6cac5e8b419324f92cc4b" +mongoose@^5.1.6: + version "5.1.6" + resolved "http://10.1.2.119:4873/mongoose/-/mongoose-5.1.6.tgz#af92762c1e0317c897435594e83d6a5952873a0e" dependencies: async "2.6.1" bson "~1.0.5" - kareem "2.1.0" + kareem "2.2.1" lodash.get "4.4.2" - mongodb "3.0.9" + mongodb "3.0.10" mongoose-legacy-pluralize "1.0.2" mpath "0.4.1" mquery "3.0.0"