Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

findOneAndUpdate error handling middleware invoked with wrong arguments #5405

Closed
davebaol opened this issue Jun 26, 2017 · 5 comments
Closed
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@davebaol
Copy link

Looks like error is passed as the 1st and the 2nd argument of findOneAndUpdate error handling middleware.
The 2nd argument should be the doc instead. Correct me if I'm wrong, because I'm new to this feature and still experimenting.

Here's a repro test inspired by this blog post:

var Schema = mongoose.Schema;

describe('Error Handling Middleware', function() {

  it('On findOneAndUpdate error and res arguments of handleE11000 should be different', function(done) {
    var schema = new Schema({
      name: {
        type: String,
        unique: true
      }
    });
    
    var handleE11000 = function(error, res, next) {
      assert.equal(error.name, 'MongoError');
      assert.equal(error.code, 11000);
      assert.equal(typeof next, 'function');
      assert.notStrictEqual(error, res);  // This makes the test fail
      next();
    };

    schema.post('findOneAndUpdate', handleE11000);

    var Person = mongoose.model('Person', schema);

    var person1 = { name: 'p1' };
    var person2 = { name: 'p2' };

    Person.create([person1, person2], function(error) {
      // Cause a duplicate key error
      Person.findOneAndUpdate({name: 'p2'}, {$set:{name: 'p1'}}, {runValidators:true}, function(error) {
        done();
      });
    });
  });
});

Software version:
Node 6.10.2
Mongodb 3.4.1
Mongoose 4.9.x , 4.10.x and 4.11.0 are all affected by this issue

@vkarpov15 vkarpov15 added this to the 4.11.2 milestone Jul 3, 2017
@vkarpov15
Copy link
Collaborator

Confirmed, here's a repro:

const assert = require('assert');
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/test');

var Schema = mongoose.Schema;

describe('Error Handling Middleware', function() {
  before(async function() {
    await mongoose.connection.dropDatabase();
  });

  it('On findOneAndUpdate error and res arguments of handleE11000 should be different', function(done) {
    var schema = new Schema({
      name: {
        type: String,
        unique: true
      }
    });
    
    var handleE11000 = function(error, res, next) {
      assert.equal(error.name, 'MongoError');
      assert.equal(error.code, 11000);
      assert.equal(typeof next, 'function');
      assert.notStrictEqual(error, res);  // This makes the test fail
      next();
    };

    schema.post('findOneAndUpdate', handleE11000);

    var Person = mongoose.model('Person', schema);

    var person1 = { name: 'p1' };
    var person2 = { name: 'p2' };

    Person.on('index', function(error) {
      assert.ifError(error);
      Person.create([person1, person2], function(error) {
        // Cause a duplicate key error
        Person.findOneAndUpdate({name: 'p2'}, {$set:{name: 'p1'}}, {runValidators:true}, function(error) {
          done();
        });
      });
    });
  });
});

@vkarpov15 vkarpov15 added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Jul 3, 2017
@davebaol
Copy link
Author

davebaol commented Jul 3, 2017

Person.on('index', callback) .... a nice trick to learn. Thanks

vkarpov15 added a commit to mongoosejs/kareem that referenced this issue Jul 6, 2017
@vkarpov15
Copy link
Collaborator

So with this fix the 2nd arg will be null, which is more correct. MongoDB doesn't give you the document back if findOneAndUpdate() fails, so there's no way for mongoose to get the document unless we execute an additional findOne() query if findOneAndUpdate() fails, which I don't think we want to do.

@davebaol
Copy link
Author

davebaol commented Jul 6, 2017

@vkarpov15
I see, sounds reasonable.
This means that the plugin mongoose-beautiful-unique-validation cannot work correctly with findOneAndUpdate.
Any idea how to retrieve the collection when the doc is not available?
See https://github.com/matteodelabre/mongoose-beautiful-unique-validation/blob/master/index.js#L61

davebaol added a commit to davebaol/mongoose-beautiful-unique-validation that referenced this issue Jul 7, 2017
See Automattic/mongoose#5405
Basically, the doc argument is not available in findOneAndUpdate post hook because MongoDB doesn't give you the document back if findOneAndUpdate() fails.
@davebaol
Copy link
Author

davebaol commented Jul 7, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

No branches or pull requests

2 participants