Skip to content

Instantly share code, notes, and snippets.

@mikermcneil
Created January 11, 2014 02:09
Show Gist options
  • Save mikermcneil/8366092 to your computer and use it in GitHub Desktop.
Save mikermcneil/8366092 to your computer and use it in GitHub Desktop.
hack to determine whether an error is a validation error from waterline (for Sails/Waterline 0.9.x)
var _ = require('lodash');
/**
* `isValidationError`
*
* Is this a waterline validation error?
*/
function isWaterlineValidationError (err) {
if (_.isPlainObject(err)) {
var keys = Object.keys(err);
if (keys.length) {
var failedValidation = err[keys[0]];
if (_.isArray(failedValidation) && failedValidation.length &&
_.isPlainObject(failedValidation[0]) && failedValidation[0]['rule']
) {
return true;
}
}
}
if ( _.isString(err) && err.match(/duplicate key value violates unique constraint/g) ) {
return true;
}
if ( _.isString(err) && err.match(/^Bad request/ig)) {
return true;
}
return false;
}
@mikermcneil
Copy link
Author

Also

/**
 * Takes a Sails Model object (e.g. User) and a ValidationError object and translates it into a friendly
 * object for sending via JSON to client-side frameworks.
 *
 * To use add a new object on your model describing what validation errors should be translated:
 *
 * module.exports = {
 *   attributes: {
 *     name: {
 *       type: 'string',
 *       required: true
 *     }
 *   },
 * 
 *   validation_messages: {
 *     name: {
 *       required: 'you have to specify a name or else'
 *     }
 *   }  
 * };
 *
 * Then in your controller you could write something like this:
 *
 * var validator = require('sails-validator-tool');
 *
 * Mymodel.create(options).done(function(error, mymodel) {
 *   if(error) {
 *     if(error.ValidationError) {
 *       error_object = validator(Mymodel, error.Validation);
 *       res.send({result: false, errors: error_object});
 *     }
 *   }
 * });
 *
 * @param model {Object} An instance of a Sails.JS model object.
 * @param validationErrors {Object} A standard Sails.JS validation object.
 *
 * @returns {Object} An object with friendly validation error conversions.
 */ 
module.exports = function(model, validationError) {
  var validation_response = {};
  var messages = model.validation_messages;
  validation_fields = Object.keys(messages);

  validation_fields.forEach(function(validation_field) {

    if(validationError[validation_field]) {
      var processField = validationError[validation_field];
      //console.log(processField);
      processField.forEach(function(rule) {
        if(messages[validation_field][rule.rule]) {
          if(!(validation_response[validation_field] instanceof Array)) {
            validation_response[validation_field] = new Array();
          }

          var newMessage={};
          newMessage[rule.rule] = messages[validation_field][rule.rule];
          validation_response[validation_field].push(newMessage);
        }
      });

    }
  });

  return validation_response;
};

@afonsomota
Copy link

Thanks. This code on the last comment is great.

It makes it easier to translate error messages with i18n also.

@mikermcneil
Copy link
Author

@afonsomota np! It's actually not mine, so I can't take the credit. Someone shared it in IRC-- wish I could remember who... :\

@johnnncodes
Copy link

I think its _sfb. Anyway, I'm also using that solution to provide custom validation messages, but the problem is if you didn't provide a custom validation message to some fields, only the fields where you provided a custom validation message will have error messages :/

Anyone have other solutions or improvements on the current solution above?

I hope custom validation messages will really be supported really soon - balderdashy/sails#1173

@danegigi
Copy link

danegigi commented Mar 7, 2014

Here's my version of the code above to generate generic error message if no custom message defined.

/**
 * Takes a Sails Model object (e.g. User) and a ValidationError object and translates it into a friendly
 * object for sending via JSON to client-side frameworks.
 *
 * To use add a new object on your model describing what validation errors should be translated:
 *
 * module.exports = {
 *   attributes: {
 *     name: {
 *       type: 'string',
 *       required: true
 *     }
 *   },
 *
 *   validation_messages: {
 *     name: {
 *       required: 'you have to specify a name or else'
 *     }
 *   }
 * };
 *
 * Then in your controller you could write something like this:
 *
 * var validator = require('sails-validator-tool');
 *
 * Mymodel.create(options).done(function(error, mymodel) {
 *   if(error) {
 *       error_object = validator(Mymodel, error);
 *       res.send({result: false, errors: error_object});
 *   }
 * });
 *
 * @param model {Object} An instance of a Sails.JS model object.
 * @param validationErrors {Object} A standard Sails.JS validation object.
 *
 * @returns {Object} An object error messages in array.
 * NOTE:
 *   ARRANGEMENT OF ERROR MESSAGES WILL FOLLOW HOW YOU DEFINE YOUR FIELD
 */

module.exports = function(model, err){
  if(!err.ValidationError) return

  var validationError = err.ValidationError;
  var custom_messages = model.validation_messages;
  var error_fields = Object.keys(validationError);
  var error_messages = {};

  error_fields.forEach(function(field){
    error_messages[field] = new Array();

    field_messages = custom_messages[field]
    validationError[field].forEach(function(err){
      if(!field_messages) error_messages[field].push(field + " should be " + err.rule);
      else{
        error_message = field_messages[err.rule]? field_messages[err.rule] : (field + " should be " + err.rule);
        error_messages[field].push(error_message)
      }

    });
  });

  return error_messages;
}

@mikermcneil
Copy link
Author

@danegigi @basco-johnkevin @afonsomota Hey y'all- check out the latest version of Sails/Waterline on master branch on github. Waterline errors have been normalized into a more predictable format. Still needs some work/testing, but the situation is much better now.

@dezull
Copy link

dezull commented Nov 26, 2014

@mikermcneil Is okay to just check e.code ? as on master

  if (e.code && e.code === 'E_VALIDATION') {
    // var validationErrors = e.invalidAttributes;
  }

@lykmapipo
Copy link

For sails v0.11.X you may use this sails-hook-validation

@mrTimofey
Copy link

More compact solution

function(err, model) {
    // no errors
    if (!err) return true;
    // error is not a validation error
    if (!err.ValidationError) return false;
    // { field: { rule: 'Message', otherRule: 'Message' ... } ... }
    var messages = model.validationMessages,
        errors = err.ValidationError,
        output = {};

    Object.keys(errors).forEach(function(field) {
        output[field] = [];
        errors[field].forEach(function(error) {
            output[field].push(messages[field] && messages[field][error.rule] || error.rule);
        })
    });

    return output;
}

@mikermcneil
Copy link
Author

Note that in Sails v1 this is changing a bit. For details, see the docs on sailsjs.com (or balderdashy/sails#3459 (comment) for a bit more background)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment