diff --git a/lib/constants.js b/lib/constants.js index f5f5c5a19b..3a03bd502f 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -44,3 +44,30 @@ const aggregateMiddlewareFunctions = [ ]; exports.aggregateMiddlewareFunctions = aggregateMiddlewareFunctions; + +/*! + * ignore + */ + +const modelMiddlewareFunctions = [ + 'bulkWrite', + 'createCollection', + 'insertMany' +]; + +exports.modelMiddlewareFunctions = modelMiddlewareFunctions; + +/*! + * ignore + */ + +const documentMiddlewareFunctions = [ + 'validate', + 'save', + 'remove', + 'updateOne', + 'deleteOne', + 'init' +]; + +exports.documentMiddlewareFunctions = documentMiddlewareFunctions; diff --git a/lib/helpers/model/applyStaticHooks.js b/lib/helpers/model/applyStaticHooks.js index 34611c3b39..3d0e129716 100644 --- a/lib/helpers/model/applyStaticHooks.js +++ b/lib/helpers/model/applyStaticHooks.js @@ -1,12 +1,16 @@ 'use strict'; const promiseOrCallback = require('../promiseOrCallback'); -const { queryMiddlewareFunctions, aggregateMiddlewareFunctions } = require('../../constants'); +const { queryMiddlewareFunctions, aggregateMiddlewareFunctions, modelMiddlewareFunctions, documentMiddlewareFunctions } = require('../../constants'); -const middlewareFunctions = [ - ...queryMiddlewareFunctions, - ...aggregateMiddlewareFunctions -]; +const middlewareFunctions = Array.from( + new Set([ + ...queryMiddlewareFunctions, + ...aggregateMiddlewareFunctions, + ...modelMiddlewareFunctions, + ...documentMiddlewareFunctions + ]) +); module.exports = function applyStaticHooks(model, hooks, statics) { const kareemOptions = { @@ -14,8 +18,11 @@ module.exports = function applyStaticHooks(model, hooks, statics) { numCallbackParams: 1 }; + model.$__insertMany = hooks.createWrapper('insertMany', + model.$__insertMany, model, kareemOptions); + hooks = hooks.filter(hook => { - // If the custom static overwrites an existing query/aggregate middleware, don't apply + // If the custom static overwrites an existing middleware, don't apply // middleware to it by default. This avoids a potential backwards breaking // change with plugins like `mongoose-delete` that use statics to overwrite // built-in Mongoose functions. @@ -25,9 +32,6 @@ module.exports = function applyStaticHooks(model, hooks, statics) { return hook.model !== false; }); - model.$__insertMany = hooks.createWrapper('insertMany', - model.$__insertMany, model, kareemOptions); - for (const key of Object.keys(statics)) { if (hooks.hasHooks(key)) { const original = model[key]; diff --git a/test/model.test.js b/test/model.test.js index c2e3f68c1e..cca70e32fd 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -5891,6 +5891,54 @@ describe('Model', function() { assert.equal(called, 1); }); + it('custom statics that overwrite model functions dont get hooks by default', async function() { + + const schema = new Schema({ name: String }); + + schema.statics.insertMany = function(docs) { + return model.insertMany.apply(this, [docs]); + }; + + let called = 0; + schema.pre('insertMany', function(next) { + ++called; + next(); + }); + const Model = db.model('Test', schema); + + const res = await Model.insertMany([ + { name: 'foo' }, + { name: 'boo' } + ]); + + assert.ok(res[0].name); + assert.ok(res[1].name); + assert.equal(called, 1); + }); + + it('custom statics that overwrite document functions dont get hooks by default', async function() { + + const schema = new Schema({ name: String }); + + schema.statics.save = function() { + return 'foo'; + }; + + let called = 0; + schema.pre('save', function(next) { + ++called; + next(); + }); + + const Model = db.model('Test', schema); + + const doc = await Model.save(); + + assert.ok(doc); + assert.equal(doc, 'foo'); + assert.equal(called, 0); + }); + it('error handling middleware passes saved doc (gh-7832)', async function() { const schema = new Schema({ _id: Number });