diff --git a/lib/dao.js b/lib/dao.js index 85741acc..3191bc19 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -59,7 +59,8 @@ DataAccessObject._forDB = function (data) { } var res = {}; Object.keys(data).forEach(function (propName) { - if (this.whatTypeName(propName) === 'JSON' || data[propName] instanceof Array) { + var type = this.whatTypeName(propName); + if (type === 'JSON' || type === 'Any' || type === 'Object' || data[propName] instanceof Array) { res[propName] = JSON.stringify(data[propName]); } else { res[propName] = data[propName]; diff --git a/lib/datasource.js b/lib/datasource.js index 6c3f3d12..56b1fdf6 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -319,6 +319,16 @@ DataSource.prototype.createModel = DataSource.prototype.define = function define properties = properties || {}; settings = settings || {}; + if(this.isRelational()) { + // Set the strict mode to be true for relational DBs by default + if(settings.strict === undefined || settings.strict === null) { + settings.strict = true; + } + if(settings.strict === false) { + settings.strict = 'throw'; + } + } + var NewClass = ModelBuilder.prototype.define.call(this, className, properties, settings); // add data access objects diff --git a/lib/model-builder.js b/lib/model-builder.js index 58ecedae..bf6995c6 100644 --- a/lib/model-builder.js +++ b/lib/model-builder.js @@ -94,6 +94,11 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett properties = properties || {}; settings = settings || {}; + // Set the strict mode to be false by default + if(settings.strict === undefined || settings.strict === null) { + settings.strict = false; + } + this.buildSchema(className, properties); // every class can receive hash of data as optional param diff --git a/lib/model.js b/lib/model.js index 77cd1100..77f13e1c 100644 --- a/lib/model.js +++ b/lib/model.js @@ -163,8 +163,13 @@ ModelBaseClass.defineProperty = function (prop, params) { ModelBaseClass.whatTypeName = function (propName) { var prop = this.properties[propName]; - if (!prop || !prop.type) { - throw new Error('Undefined type for ' + this.modelName + ':' + propName); + if(!prop) { + // The property is not part of the definition + return null; + } + if (!prop.type) { + throw new Error('Type not defined for property ' + this.modelName + '.' + propName); + // return null; } return prop.type.name; }; diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index f1dcfb6a..2c617a03 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -320,12 +320,12 @@ function seed(done) { var beatles = [ { name: 'John Lennon', - mail: 'john@b3atl3s.co.uk', + email: 'john@b3atl3s.co.uk', role: 'lead', order: 2 }, { name: 'Paul McCartney', - mail: 'paul@b3atl3s.co.uk', + email: 'paul@b3atl3s.co.uk', role: 'lead', order: 1 }, diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index 45abf3fb..9292fa06 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -11,7 +11,6 @@ describe('ModelBuilder define model', function () { it('should be able to define plain models', function (done) { var modelBuilder = new ModelBuilder(); - // simplier way to describe model var User = modelBuilder.define('User', { name: String, bio: ModelBuilder.Text, @@ -29,7 +28,6 @@ describe('ModelBuilder define model', function () { modelBuilder.definitions.should.be.a('object').and.have.property('User'); var user = new User({name: 'Joe', age: 20}); - // console.log(user); User.modelName.should.equal('User'); user.should.be.a('object').and.have.property('name', 'Joe'); @@ -42,15 +40,12 @@ describe('ModelBuilder define model', function () { it('should not take unknown properties in strict mode', function (done) { var modelBuilder = new ModelBuilder(); - // simplier way to describe model var User = modelBuilder.define('User', {name: String, bio: String}, {strict: true}); var user = new User({name: 'Joe', age: 20}); - // console.log(user); User.modelName.should.equal('User'); user.should.be.a('object'); - // console.log(user); assert(user.name === 'Joe'); assert(user.age === undefined); assert(user.toObject().age === undefined); @@ -59,14 +54,41 @@ describe('ModelBuilder define model', function () { done(null, User); }); + it('should throw when unknown properties are used if strict=throw', function (done) { + var modelBuilder = new ModelBuilder(); + + var User = modelBuilder.define('User', {name: String, bio: String}, {strict: 'throw'}); + + try { + var user = new User({name: 'Joe', age: 20}); + assert(false, 'The code should have thrown an error'); + } catch(e) { + assert(true, 'The code is expected to throw an error'); + } + done(null, User); + }); + it('should be able to define open models', function (done) { var modelBuilder = new ModelBuilder(); - // simplier way to describe model var User = modelBuilder.define('User', {}, {strict: false}); var user = new User({name: 'Joe', age: 20}); - // console.log(user); + + User.modelName.should.equal('User'); + user.should.be.a('object').and.have.property('name', 'Joe'); + user.should.have.property('name', 'Joe'); + user.should.have.property('age', 20); + user.should.not.have.property('bio'); + done(null, User); + }); + + it('should use false as the default value for strict', function (done) { + var modelBuilder = new ModelBuilder(); + + var User = modelBuilder.define('User', {}); + + var user = new User({name: 'Joe', age: 20}); User.modelName.should.equal('User'); user.should.be.a('object').and.have.property('name', 'Joe'); @@ -115,7 +137,6 @@ describe('ModelBuilder define model', function () { emails: [{label: 'work', email: 'xyz@sample.com'}], friends: ['Mary', 'John'] }); - // console.log(user); User.modelName.should.equal('User'); user.should.be.a('object').and.have.property('name', 'Joe'); @@ -149,13 +170,13 @@ describe('DataSource define model', function () { title: { type: String, length: 255 }, content: { type: DataSource.Text }, date: { type: Date, default: function () { - return new Date; + return new Date(); } }, timestamp: { type: Number, default: Date.now }, published: { type: Boolean, default: false, index: true } }); -// simplier way to describe model +// simpler way to describe model var User = ds.define('User', { name: String, bio: DataSource.Text, @@ -164,15 +185,17 @@ describe('DataSource define model', function () { age: Number }); - var Group = ds.define('Group', {name: String}); + var Group = ds.define('Group', {group: String}); + User.mixin(Group); // define any custom method User.prototype.getNameAndAge = function () { return this.name + ', ' + this.age; }; - var user = new User({name: 'Joe'}); - // console.log(user); + var user = new User({name: 'Joe', group: 'G1'}); + assert.equal(user.name, 'Joe'); + assert.equal(user.group, 'G1'); // setup relationships User.hasMany(Post, {as: 'posts', foreignKey: 'userId'}); @@ -183,7 +206,6 @@ describe('DataSource define model', function () { var user2 = new User({name: 'Smith'}); user2.save(function (err) { - // console.log(user2); var post = user2.posts.build({title: 'Hello world'}); post.save(function (err, data) { // console.log(err ? err : data); @@ -199,9 +221,7 @@ describe('DataSource define model', function () { console.log(err); return; } - // console.log(data); var post = data.posts.build({title: 'My Post'}); - // console.log(post); }); User.create({name: 'Ray'}, function (err, data) { @@ -244,6 +264,104 @@ describe('DataSource define model', function () { }); }); + + it('should not take unknown properties in strict mode', function (done) { + var ds = new DataSource('memory'); + + var User = ds.define('User', {name: String, bio: String}, {strict: true}); + + User.create({name: 'Joe', age: 20}, function (err, user) { + + User.modelName.should.equal('User'); + user.should.be.a('object'); + assert(user.name === 'Joe'); + assert(user.age === undefined); + assert(user.toObject().age === undefined); + assert(user.toObject(true).age === undefined); + assert(user.bio === undefined); + done(null, User); + }); + }); + + it('should throw when unknown properties are used if strict=throw', function (done) { + var ds = new DataSource('memory'); + + var User = ds.define('User', {name: String, bio: String}, {strict: 'throw'}); + + try { + var user = new User({name: 'Joe', age: 20}); + assert(false, 'The code should have thrown an error'); + } catch(e) { + assert(true, 'The code is expected to throw an error'); + } + done(null, User); + }); + + it('should be able to define open models', function (done) { + var ds = new DataSource('memory'); + + var User = ds.define('User', {}, {strict: false}); + + User.create({name: 'Joe', age: 20}, function (err, user) { + + User.modelName.should.equal('User'); + user.should.be.a('object').and.have.property('name', 'Joe'); + user.should.have.property('name', 'Joe'); + user.should.have.property('age', 20); + user.should.not.have.property('bio'); + done(null, User); + }); + }); + + it('should use false as the default value for strict', function (done) { + var ds = new DataSource('memory'); + + var User = ds.define('User', {}); + + User.create({name: 'Joe', age: 20}, function (err, user) { + + User.modelName.should.equal('User'); + user.should.be.a('object').and.have.property('name', 'Joe'); + user.should.have.property('name', 'Joe'); + user.should.have.property('age', 20); + user.should.not.have.property('bio'); + done(null, User); + }); + }); + + it('should use true as the default value for strict for relational DBs', function (done) { + var ds = new DataSource('memory'); + ds.connector.relational = true; // HACK + + var User = ds.define('User', {name: String, bio: String}, {strict: true}); + + var user = new User({name: 'Joe', age: 20}); + + User.modelName.should.equal('User'); + user.should.be.a('object'); + assert(user.name === 'Joe'); + assert(user.age === undefined); + assert(user.toObject().age === undefined); + assert(user.toObject(true).age === undefined); + assert(user.bio === undefined); + done(null, User); + }); + + it('should throw when unknown properties are used if strict=false for relational DBs', function (done) { + var ds = new DataSource('memory'); + ds.connector.relational = true; // HACK + + var User = ds.define('User', {name: String, bio: String}, {strict: 'throw'}); + + try { + var user = new User({name: 'Joe', age: 20}); + assert(false, 'The code should have thrown an error'); + } catch(e) { + assert(true, 'The code is expected to throw an error'); + } + done(null, User); + }); + }); @@ -286,7 +404,7 @@ describe('Load models from json', function () { models.should.have.property('Customer'); for (var s in models) { var m = models[s]; - // console.log(m.modelName, new m()); + assert(new m()); } }); });