From 01c1d87b18d292b39dcc53a245c91d462b9110e6 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Jun 2022 16:21:18 -0400 Subject: [PATCH 1/3] feat: add `skipMiddlewareFunction()` and `overwriteMiddlewareResult()` for skipping and modifying middleware results Fix #11426 Fix #8393 --- lib/index.js | 38 +++++++++++++++++++++++++++++++++++ package.json | 2 +- test/query.middleware.test.js | 35 +++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 6dc347545eb..fafef8b040e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,6 +8,7 @@ require('./driver').set(require('./drivers/node-mongodb-native')); const Document = require('./document'); const EventEmitter = require('events').EventEmitter; +const Kareem = require('kareem'); const Schema = require('./schema'); const SchemaType = require('./schematype'); const SchemaTypes = require('./schema/index'); @@ -1185,6 +1186,43 @@ Mongoose.prototype._promiseOrCallback = function(callback, fn, ee) { return promiseOrCallback(callback, fn, ee, this.Promise); }; +/** + * Use this function in `pre()` middleware to skip calling the wrapped function. + * + * ####Example: + * + * schema.pre('save', function() { + * // Will skip executing `save()`, but will execute post hooks as if + * // `save()` had executed with the result `{ matchedCount: 0 }` + * return mongoose.skipMiddlewareFunction({ matchedCount: 0 }); + * }); + * + * @method skipMiddlewareFunction + * @param {any} result + * @api public + */ + +Mongoose.prototype.skipMiddlewareFunction = Kareem.skipWrappedFunction; + +/** + * Use this function in `post()` middleware to replace the result + * + * ####Example: + * + * schema.post('find', function(res) { + * // Normally you have to modify `res` in place. But with + * // `overwriteMiddlewarResult()`, you can make `find()` return a + * // completely different value. + * return mongoose.overwriteMiddlewareResult(res.filter(doc => !doc.isDeleted)); + * }); + * + * @method overwriteMiddlewareResult + * @param {any} result + * @api public + */ + +Mongoose.prototype.overwriteMiddlewareResult = Kareem.overwriteResult; + /*! * The exports object is an instance of Mongoose. * diff --git a/package.json b/package.json index 4798278f6e6..3e7b6950c1e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "license": "MIT", "dependencies": { "bson": "^4.6.2", - "kareem": "2.3.5", + "kareem": "2.4.0", "mongodb": "4.7.0", "mpath": "0.9.0", "mquery": "4.0.3", diff --git a/test/query.middleware.test.js b/test/query.middleware.test.js index 881d77297a2..4d15f594eb8 100644 --- a/test/query.middleware.test.js +++ b/test/query.middleware.test.js @@ -636,7 +636,6 @@ describe('query middleware', function() { }); const Model = db.model('Test', schema); - await Model.find(); assert.equal(called, 1); @@ -655,4 +654,38 @@ describe('query middleware', function() { await Model.aggregate([{ $match: { name: 'test' } }]); assert.equal(called, 3); }); + + it('allows skipping the wrapped function with `skipMiddlewareFunction()` (gh-11426)', async function() { + const schema = Schema({ name: String }); + + schema.pre('updateOne', function(next) { + next(mongoose.skipMiddlewareFunction({ answer: 42 })); + }); + const Test = db.model('Test', schema); + + const { _id } = await Test.create({ name: 'test' }); + const res = await Test.updateOne({ _id }, { name: 'changed' }); + assert.equal(res.answer, 42); + assert.strictEqual(res.modifiedCount, undefined); + + const doc = await Test.findById(_id); + assert.equal(doc.name, 'test'); + }); + + it('allows overwriting result with `overwriteMiddlewareResult()` (gh-11426)', async function() { + const schema = Schema({ name: String }); + + schema.post('updateOne', function() { + return mongoose.overwriteMiddlewareResult({ answer: 42 }); + }); + const Test = db.model('Test', schema); + + const { _id } = await Test.create({ name: 'test' }); + const res = await Test.updateOne({ _id }, { name: 'changed' }); + assert.equal(res.answer, 42); + assert.strictEqual(res.modifiedCount, undefined); + + const doc = await Test.findById(_id); + assert.equal(doc.name, 'changed'); + }); }); From 969fd1d42c52e8cad0ad3afd47eb42c323fc1ee1 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 15 Jun 2022 12:03:41 -0400 Subject: [PATCH 2/3] test: improve tests for overwriteMiddlewareResult() and skipMiddlewareFunction() --- test/query.middleware.test.js | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/test/query.middleware.test.js b/test/query.middleware.test.js index 4d15f594eb8..6e7e4c6ee54 100644 --- a/test/query.middleware.test.js +++ b/test/query.middleware.test.js @@ -657,19 +657,22 @@ describe('query middleware', function() { it('allows skipping the wrapped function with `skipMiddlewareFunction()` (gh-11426)', async function() { const schema = Schema({ name: String }); + const now = Date.now(); - schema.pre('updateOne', function(next) { - next(mongoose.skipMiddlewareFunction({ answer: 42 })); + schema.pre('find', function(next) { + next(mongoose.skipMiddlewareFunction([{ name: 'from cache' }])); + }); + schema.post('find', function(res) { + res.forEach(doc => { + doc.loadedAt = now; + }); }); const Test = db.model('Test', schema); - const { _id } = await Test.create({ name: 'test' }); - const res = await Test.updateOne({ _id }, { name: 'changed' }); - assert.equal(res.answer, 42); - assert.strictEqual(res.modifiedCount, undefined); - - const doc = await Test.findById(_id); - assert.equal(doc.name, 'test'); + const res = await Test.find(); + assert.equal(res.length, 1); + assert.strictEqual(res[0].name, 'from cache'); + assert.strictEqual(res[0].loadedAt, now); }); it('allows overwriting result with `overwriteMiddlewareResult()` (gh-11426)', async function() { @@ -678,12 +681,17 @@ describe('query middleware', function() { schema.post('updateOne', function() { return mongoose.overwriteMiddlewareResult({ answer: 42 }); }); + schema.post('updateOne', function(res) { + assert.strictEqual(res.answer, 42); + res.secondMiddlewareRan = true; + }); const Test = db.model('Test', schema); const { _id } = await Test.create({ name: 'test' }); const res = await Test.updateOne({ _id }, { name: 'changed' }); assert.equal(res.answer, 42); assert.strictEqual(res.modifiedCount, undefined); + assert.strictEqual(res.secondMiddlewareRan, true); const doc = await Test.findById(_id); assert.equal(doc.name, 'changed'); From ebc0b88125a6dbac7b01650d93a5d0264471600d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Jun 2022 16:31:58 -0400 Subject: [PATCH 3/3] fix: upgrade kareem to fix #6611 test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e7b6950c1e..3fd5eca1bff 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "license": "MIT", "dependencies": { "bson": "^4.6.2", - "kareem": "2.4.0", + "kareem": "2.4.1", "mongodb": "4.7.0", "mpath": "0.9.0", "mquery": "4.0.3",