diff --git a/lib/dao.js b/lib/dao.js index cf7dc7ea9..75bb0b094 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -1109,8 +1109,15 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) let obj; if (data) { - obj = new Model(data, {fields: query.fields, applySetters: false, - persisted: true}); + const ctorOpts = { + fields: query.fields, + applySetters: false, + persisted: true, + }; + if (Model.settings.applyDefaultsOnReads === false) { + ctorOpts.applyDefaultValues = false; + } + obj = new Model(data, ctorOpts); } if (created) { @@ -1632,6 +1639,9 @@ DataAccessObject.find = function find(query, options, cb) { applySetters: false, persisted: true, }; + if (Model.settings.applyDefaultsOnReads === false) { + ctorOpts.applyDefaultValues = false; + } let obj; try { obj = new Model(data, ctorOpts); diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index 57fd3b4e9..b30f0125b 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -917,6 +917,71 @@ describe('basic-querying', function() { }); }); }); + + it('applies default values by default', () => { + // Backwards compatibility, see + // https://github.com/strongloop/loopback-datasource-juggler/issues/1692 + + // Initially, all Products were always active, no property was needed + const Product = db.define('Product', {name: String}); + let created; + return db.automigrate('Product') + .then(() => Product.create({name: 'Pen'})) + .then(result => { + created = result; + + // Later on, we decide to introduce `active` property + Product.defineProperty('active', { + type: Boolean, + default: false, + }); + + // And query existing data + return Product.findOne(); + }) + .then(found => { + found.toObject().should.eql({ + id: created.id, + name: 'Pen', + // Backwards-compatibility + // When Pen does not have "active" flag set, we change it to default + active: false, + }); + }); + }); + + it('preserves empty values from the database when "applyDefaultsOnReads" is false', () => { + // https://github.com/strongloop/loopback-datasource-juggler/issues/1692 + + // Initially, all Products were always active, no property was needed + const Product = db.define( + 'Product', + {name: String}, + {applyDefaultsOnReads: false} + ); + let created; + return db.automigrate('Product') + .then(() => Product.create({name: 'Pen'})) + .then(result => { + created = result; + + // Later on, we decide to introduce `active` property + Product.defineProperty('active', { + type: Boolean, + default: false, + }); + + // And query existing data + return Product.findOne(); + }) + .then(found => { + found.toObject().should.eql({ + id: created.id, + name: 'Pen', + active: undefined, + }); + }); + }); }); describe('count', function() { diff --git a/test/manipulation.test.js b/test/manipulation.test.js index 9c939c0d6..e7d4fae1d 100644 --- a/test/manipulation.test.js +++ b/test/manipulation.test.js @@ -1619,6 +1619,72 @@ describe('manipulation', function() { }) .catch(done); }); + + it('applies default values on returned data', () => { + // Backwards compatibility, see + // https://github.com/strongloop/loopback-datasource-juggler/issues/1692 + + // Initially, all Products were always active, no property was needed + const Product = db.define('Product', {name: String}); + let created; + return db.automigrate('Product') + .then(() => Product.create({name: 'Pen'})) + .then(result => { + created = result; + + // Later on, we decide to introduce `active` property + Product.defineProperty('active', { + type: Boolean, + default: false, + }); + + // and findOrCreate an existing record + return Product.findOrCreate({id: created.id}, {name: 'updated'}); + }) + .spread(found => { + found.toObject().should.eql({ + id: created.id, + name: 'Pen', + // Backwards-compatibility + // When Pen does not have "active" flag set, we change it to default + active: false, + }); + }); + }); + + it('preserves empty values from the database when "applyDefaultsOnReads" is false', () => { + // https://github.com/strongloop/loopback-datasource-juggler/issues/1692 + + // Initially, all Products were always active, no property was needed + const Product = db.define( + 'Product', + {name: String}, + {applyDefaultsOnReads: false} + ); + let created; + + return db.automigrate('Product') + .then(() => Product.create({name: 'Pen'})) + .then(result => { + created = result; + + // Later on, we decide to introduce `active` property + Product.defineProperty('active', { + type: Boolean, + default: false, + }); + + // And findOrCreate an existing record + return Product.findOrCreate({id: created.id}, {name: 'updated'}); + }) + .spread(found => { + found.toObject().should.eql({ + id: created.id, + name: 'Pen', + active: undefined, + }); + }); + }); }); describe('destroy', function() {