grll
grll

Reputation: 1087

invalidate a validation from a nested schema virtual mongoose

I have my parent schema defined like this:

User.js:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PasswordSchema = require('./Password');

var UserSchema = new Schema({
    name: { type: String, required: true },
    password: PasswordSchema
});
mongoose.model('User', UserSchema);

My children schema defined like this:

Password.js:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crypto = require('crypto');

var PasswordSchema = new Schema ({
    _id: false,
    hashedPassword: { type: String, required: true },
    salt: { type: String, default: '' }
});

var passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,24}$/;

PasswordSchema.virtual('password')
    .set(function (password) {
        if (passwordRegex.test(password))
        {
             this.invalidate('password', 'Invalid password format');
        }
     });

mongoose.model('Password', PasswordSchema);
module.exports = PasswordSchema;

Now I used these Models schema in my controller like this:

user.js:

require('../models/User');

var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Password = mongoose.model('Password');

router.post('/register', function (req, res, next) {
    var user = new User(req.body);
    var password = new Password({ password: 'abcd1234' });
    console.log(password.$__.validationError.errors['hashedPassword']); // Here it works I got the validation error
    user.password = password;
    user.password.$__.validationError = password.$__.validationError; // WORKAROUND
    console.log(user.password.$__.validationError.errors['hashedPassword']); // Here it doesn't work no validation error anymore ... 
    user.save(function (err) {
        if (err)
            console.log(":(");
        else
            console.log(":)");
    });
});

module.exports = router;

Question:
So my problem now is that no matter what password I send to my children virtual it doesn't invalidate the process. How could I invalidate the mongoose save action from a children virtual ? Is there an other better option ?

Question Updated:
In user.js why the variable password has the validation error and when I assign it to user.password I don't have the validation error anymore ? How can I correct it ?

** Update 2:**
I have found a workaround see user.js: I just assign the required property to generate validation error. But it looks really not clean is there another way?

Upvotes: 0

Views: 1381

Answers (1)

zangw
zangw

Reputation: 48476

Here is one good example https://gist.github.com/swaj/1350041, refactor it as below

PasswordSchema.virtual('password')
    .get(function(){
        return this._password;
    })
    .set(function (password) {
        this._password = password;
        // invoke crypto to hash and encrypt password, then assign it to hashedPassword
        this.hashedPassword = password; // this is just for test
     });

PasswordSchema.path('hashedPassword').validate(function(v) {
    if (v) {
        if (passwordRegex.test(v)) {
            this.invalidate('password', 'Invalid password format');
        }
    }
    if (!v) {
        this.validate('password', 'password required');
    }
}, null);

Test codes

var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfASF123444'});
user.save(function (err) {
    if (err)
        console.log(err);
    else
        console.log("save user successfully");
});

Validation error is

{ [ValidationError: User validation failed]
  message: 'User validation failed',
  name: 'ValidationError',
  errors:
   { password:
      { [ValidatorError: Invalid password format]
        properties: [Object],
        message: 'Invalid password format',
        name: 'ValidatorError',
        kind: 'user defined',
        path: 'password',
        value: undefined } } }

Per invalidate source code

Document.prototype.invalidate = function (path, err, val) {
  if (!this.$__.validationError) {
    this.$__.validationError = new ValidationError(this);
  }
  // ...

We know invalidate function belong to Document.

 password.$__.validationError.errors['hashedPassword']

You define the validation for PasswordSchema, not in UserSchema. so user.password.$__.validationError.errors['hashedPassword'] is not valid.


Test your code with

var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfwe32113'});
user.save(function (err) {
    if (err)
        console.log(err);
    else
        console.log("save user successfully");
});

Validation will be triggered, however, with this code

`user.password = new Password({hashedPassword: 'asdfwe32113'});`

This validation is NOT triggered.

Because for virtual field, only the correct virtual name field is updated then the .set function could be called.

Also please add those codes to virtual('password'), to make sure the hashedPassword could be set correctly.

    if (passwordRegex.test(password)) {
         this.invalidate('password', 'Invalid password format');
    }else {
        this.hashedPassword = password;
    }   

For the second question, require('../models/User'); must be invoked before mongoose.model() to make sure the User.js is parsed firstly, and the User could be added into mongoose.model in User.js. So in user.js could find this User model from mongoose. JavaScript is an interpreted programming language, so we should tell the JS engine the file parsed order in this way.

Upvotes: 1

Related Questions