corbanb
corbanb

Reputation: 149

waterline 0.10.14 + express - beforeCreate not executing

I am trying to has a password on my user model but never seeing before create being called.

Using Express 4.2 and waterline 0.10.14.

User Model:

var Waterline = require('waterline'),
    uuid = require('uuid'),
    bcrypt = require('bcrypt');

var User = module.exports = Waterline.Collection.extend({

  identity: 'user',
  connection: 'default',
  autoPK: false, // prevents creation of the id
  schema: true,


  types: {
    // Custom Validation - https://github.com/balderdashy/waterline-docs/blob/master/models.md#custom-validations
  },

  attributes: {

    uuid: {
      type: 'uuidv4',
      primaryKey: true,
      defaultsTo: uuid.v4
    },

    first_name: {
      type: 'string',
      required: true
    },

    last_name: {
      type: 'string',
      required: true
    },

    email: {
      type: 'email',
      required: true,
      unique: true
    },

    username: {

      // TODO: validate no spaces, or special characters

      type: 'string',
      required: true,
      unique: true,
      index: true,
      minLength: 3,
      maxLength: 30
    },

    password: {
      // TODO:  add rules on password
      type: 'string',
      minLength: 6,
      maxLength: 50,
      required: true,
      columnName: 'encrypted_password'
    },

    // TODO: custom input with - https://developers.google.com/places/documentation/autocomplete#place_autocomplete_requests
    location: {
      type: 'string'
    },

    activated: {
        type: 'boolean',
        defaultsTo: false
    },
    activationToken: {
        type: 'string'
    },

    // Override toJSON instance method to remove values
    toJSON: function() {
      var obj = this.toObject();
      // delete obj.password;
      delete obj.email;
      delete obj.activated;
      delete obj.activationToken;

      return obj;
    },


    verifyPassword: function (password) {
      return bcrypt.compareSync(password, this.password);
    },

    changePassword: function(newPassword, next){
      this.password = newPassword;
      this.save(function(err, u) {
        return next(err,u);
      });
    },


    // Lifecycle Callbacks
    beforeCreate: function(attrs, next) { 

      console.log('beforeCreate - user');

      bcrypt.genSalt(process.env.SALT_WORK_FACTOR, function(err, salt) {
          if (err) return next(err);

          bcrypt.hash(attrs.password, salt, function(err, crypted) {
            if(err) return next(err);

            attrs.password = crypted;
            attrs.activationToken = crypto.token(new Date().getTime() + attrs.email);

            return next();
          });
        });

    },

    beforeUpdate: function (attrs, next) {
      if(attrs.newPassword){
        bcrypt.genSalt(process.env.SALT_WORK_FACTOR, function(err, salt) {
          if (err) return next(err);

          bcrypt.hash(attrs.newPassword, salt, function(err, crypted) {
            if(err) return next(err);

            delete attrs.newPassword;
            attrs.password = crypted;
            return next();
          });
        });
      }
      else {
        return next();
      }
    }


  }

});

This is how its being called on my controller.

exports.create = function (req, res, next) {
    app.models.user.create(req.body, function (err, model) {
        if(err) return res.error('users/signup', err);
        res.success('users/welcome', model);
    });
};

I never see the log on my beforeCreate called and the password is never hashed. Thoughts?

Edit: User model to reflect most recent changes. Also noticed beforeCreate should run before validations.

Upvotes: 1

Views: 448

Answers (2)

corbanb
corbanb

Reputation: 149

This was a small mistake on my end having the lifecycle callbacks wrapped up in the same object as my attributes. They have to be outside of that object.

Upvotes: 1

skud
skud

Reputation: 595

I think I know why your beforeCreate is not running. It is because password is a required field. Waterline actually runs the validate callback before running the beforeCreate callback. Since password doesn't exist until values.password is created the validation fails and beforeCreate never gets called. I'm assuming that you're just passing a newPassword parameter when you're creating a new user.

Upvotes: 1

Related Questions