diff --git a/lib/model-builder.js b/lib/model-builder.js index 218dad7c..7ce0ec45 100644 --- a/lib/model-builder.js +++ b/lib/model-builder.js @@ -426,6 +426,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined; ModelClass.validatesPresenceOf(propertyName, requiredOptions); } + if (DataType === Date) ModelClass.validatesDateOf(propertyName); Object.defineProperty(ModelClass.prototype, propertyName, { get: function() { @@ -526,10 +527,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett // DataType for Date function DateType(arg) { + if (arg === null) return null; var d = new Date(arg); - if (isNaN(d.getTime())) { - throw new Error(g.f('Invalid date: %s', arg)); - } return d; } diff --git a/lib/validations.js b/lib/validations.js index 97a822ab..7c8ed722 100644 --- a/lib/validations.js +++ b/lib/validations.js @@ -243,6 +243,7 @@ Validatable.validateAsync = getConfigurator('custom', {async: true}); */ Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true}); +Validatable.validatesDateOf = getConfigurator('date'); // implementation of validators /*! @@ -383,6 +384,16 @@ function validateUniqueness(attr, conf, err, options, done) { }.bind(this)); } +/*! + * Date validator + */ +function validateDate(attr, conf, err) { + if (this[attr] === null || this[attr] === undefined) return; + + var date = new Date(this[attr]); + if (isNaN(date.getTime())) return err(); +} + var validators = { presence: validatePresence, absence: validateAbsence, @@ -393,6 +404,7 @@ var validators = { format: validateFormat, custom: validateCustom, uniqueness: validateUniqueness, + date: validateDate, }; function getConfigurator(name, opts) { @@ -631,6 +643,7 @@ var defaultMessages = { inclusion: 'is not included in the list', exclusion: 'is reserved', uniqueness: 'is not unique', + date: 'is not a valid date', }; /** @@ -841,7 +854,7 @@ function formatPropertyError(propertyName, propertyValue, errorMessage) { if (valueType === 'string') { formattedValue = JSON.stringify(truncatePropertyString(propertyValue)); } else if (propertyValue instanceof Date) { - formattedValue = propertyValue.toISOString(); + formattedValue = isNaN(propertyValue.getTime()) ? propertyValue.toString() : propertyValue.toISOString(); } else if (valueType === 'object') { // objects and arrays formattedValue = util.inspect(propertyValue, { diff --git a/test/datatype.test.js b/test/datatype.test.js index 634a16c0..0ea48d60 100644 --- a/test/datatype.test.js +++ b/test/datatype.test.js @@ -54,17 +54,6 @@ describe('datatypes', function() { done(); }); - it('throws an error when property of type Date is set to an invalid value', - function() { - var myModel = db.define('myModel', { - date: {type: Date}, - }); - - (function() { - myModel.create({date: 'invalid'}); - }).should.throw({message: 'Invalid date: invalid'}); - }); - it('should keep types when get read data from db', function(done) { var d = new Date, id; diff --git a/test/manipulation.test.js b/test/manipulation.test.js index e55a3cdf..4f12776d 100644 --- a/test/manipulation.test.js +++ b/test/manipulation.test.js @@ -687,15 +687,6 @@ describe('manipulation', function() { }); }); - it('should fail if field validation fails', function(done) { - person.updateAttributes({'name': 'John', dob: 'notadate'}, - function(err, p) { - should.exist(err); - err.message.should.equal('Invalid date: notadate'); - done(); - }); - }); - it('has an alias "patchAttributes"', function(done) { person.updateAttributes.should.equal(person.patchAttributes); done(); @@ -2085,12 +2076,9 @@ describe('manipulation', function() { p1 = new Person({name: 'John', dob: undefined}); p1.should.have.property('dob', undefined); - try { - p1 = new Person({name: 'John', dob: 'X'}); - throw new Error('new Person() should have thrown'); - } catch (e) { - e.should.be.eql(new Error('Invalid date: X')); - } + p1 = new Person({name: 'John', dob: 'X'}); + p1.should.have.property('dob'); + p1.dob.toString().should.be.eql('Invalid Date'); }); }); diff --git a/test/validations.test.js b/test/validations.test.js index d2494fcc..f551ac1e 100644 --- a/test/validations.test.js +++ b/test/validations.test.js @@ -823,4 +823,78 @@ describe('validations', function() { return err.message.replace(/^.*Details: /, ''); } }); + describe('date', function() { + it('should validate a date object', function() { + User.validatesDateOf('updatedAt'); + var u = new User({updatedAt: new Date()}); + u.isValid().should.be.true; + }); + + it('should validate a date string', function() { + User.validatesDateOf('updatedAt'); + var u = new User({updatedAt: '2000-01-01'}); + u.isValid().should.be.true; + }); + + it('should validate a null date', function() { + User.validatesDateOf('updatedAt'); + var u = new User({updatedAt: null}); + u.isValid().should.be.true; + }); + + it('should validate an undefined date', function() { + User.validatesDateOf('updatedAt'); + var u = new User({updatedAt: undefined}); + u.isValid().should.be.true; + }); + + it('should validate an invalid date string', function() { + User.validatesDateOf('updatedAt'); + var u = new User({updatedAt: 'invalid date string'}); + u.isValid().should.not.be.true; + u.errors.should.eql({ + updatedAt: ['is not a valid date'], + codes: { + updatedAt: ['date'], + }, + }); + }); + + it('should attach validation by default to all date properties', function() { + var AnotherUser = db.define('User', { + email: String, + name: String, + password: String, + state: String, + age: Number, + gender: String, + domain: String, + pendingPeriod: Number, + createdByAdmin: Boolean, + createdByScript: Boolean, + updatedAt: Date, + }); + var u = new AnotherUser({updatedAt: 'invalid date string'}); + u.isValid().should.not.be.true; + u.errors.should.eql({ + updatedAt: ['is not a valid date'], + codes: { + updatedAt: ['date'], + }, + }); + }); + + it('should overwrite default blank message with custom format message', function() { + var CUSTOM_MESSAGE = 'custom validation message'; + User.validatesDateOf('updatedAt', {message: CUSTOM_MESSAGE}); + var u = new User({updatedAt: 'invalid date string'}); + u.isValid().should.not.be.true; + u.errors.should.eql({ + updatedAt: [CUSTOM_MESSAGE], + codes: { + updatedAt: ['date'], + }, + }); + }); + }); });