From 93e8df07f99a1ce5bfa9835b626a885ae3df1ae8 Mon Sep 17 00:00:00 2001 From: Bryan Clark Date: Wed, 22 Jul 2015 11:32:14 -0700 Subject: [PATCH] sets validateUpsert to false with tests for upsert This fixes #10 by adding in the tests from @0ff which then required that we cannot yet support the upsert validation. Upsert is causing validation to happen before our observer can run which makes the required timestamps to fail the presence validation. --- es6/time-stamp.js | 6 ++++++ package.json | 2 +- test/test-index.js | 24 ++++++++++++++++-------- test/test.js | 24 ++++++++++++++++-------- time-stamp.js | 6 ++++++ time-stamp.js.map | 2 +- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/es6/time-stamp.js b/es6/time-stamp.js index 840c0a8..e260391 100644 --- a/es6/time-stamp.js +++ b/es6/time-stamp.js @@ -9,6 +9,12 @@ export default (Model, options = {}) => { debug('options', options); + debug('Model.settings.validateUpsert', Model.settings.validateUpsert); + if (Model.settings.validateUpsert && options.required) { + console.warn('TimeStamp mixin requires validateUpsert be false. See @clarkbw/loopback-ds-timestamp-mixin#10'); + } + Model.settings.validateUpsert = false; + Model.defineProperty(options.createdAt, {type: Date, required: options.required, defaultFn: 'now'}); Model.defineProperty(options.updatedAt, {type: Date, required: options.required}); diff --git a/package.json b/package.json index d1cdf15..6cf48be 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "debug": "2.x" }, "peerDependencies": { - "loopback-datasource-juggler": ">=2.23.0" + "loopback-datasource-juggler": ">=2.33.2" }, "devDependencies": { "babel-core": "latest", diff --git a/test/test-index.js b/test/test-index.js index 464b6d0..60b6dd6 100644 --- a/test/test-index.js +++ b/test/test-index.js @@ -35,12 +35,10 @@ test('loopback datasource timestamps', function(tap) { }); t.test('should not change on save', function(tt) { - var createdAt; Book.destroyAll(function() { Book.create({name:'book 1', type:'fiction'}, function(err, book) { tt.error(err); tt.ok(book.createdAt); - createdAt = book.createdAt; book.name = 'book inf'; book.save(function(err, b) { tt.equal(book.createdAt, b.createdAt); @@ -51,7 +49,6 @@ test('loopback datasource timestamps', function(tap) { }); t.test('should not change on update', function(tt) { - var createdAt; Book.destroyAll(function() { Book.create({name:'book 1', type:'fiction'}, function(err, book) { tt.error(err); @@ -65,6 +62,20 @@ test('loopback datasource timestamps', function(tap) { }); }); + t.test('should not change on upsert', function(tt) { + Book.destroyAll(function() { + Book.create({name:'book 1', type:'fiction'}, function(err, book) { + tt.error(err); + tt.ok(book.createdAt); + Book.upsert({id: book.id, name:'book inf'}, function(err, b) { + tt.error(err); + tt.equal(book.createdAt.getTime(), b.createdAt.getTime()); + tt.end(); + }); + }); + }); + }); + t.test('should not change with bulk updates', function(tt) { var createdAt; Book.destroyAll(function() { @@ -224,22 +235,19 @@ test('loopback datasource timestamps', function(tap) { ); t.test('should skip changing updatedAt when option passed', function(tt) { - var updated, book; Book.destroyAll(function() { Book.create({name:'book 1', type:'fiction'}, function(err, book1) { tt.error(err); tt.ok(book1.updatedAt); - updated = book1.updatedAt; - book = book1.toObject(); - book.name = 'book 2'; + var book = {id: book1.id, name:'book 2'}; Book.updateOrCreate(book, {skipUpdatedAt: true}, function(err, book2) { tt.error(err); tt.ok(book2.updatedAt); - tt.equal(updated.getTime(), book2.updatedAt.getTime()); + tt.equal(book1.updatedAt.getTime(), book2.updatedAt.getTime()); tt.end(); }); diff --git a/test/test.js b/test/test.js index 2bf9164..5add036 100644 --- a/test/test.js +++ b/test/test.js @@ -23,12 +23,10 @@ test('loopback datasource timestamps', function(tap) { }); t.test('should not change on save', function(tt) { - var createdAt; Widget.destroyAll(function() { Widget.create({name:'book 1', type:'fiction'}, function(err, book) { tt.error(err); tt.ok(book.createdAt); - createdAt = book.createdAt; book.name = 'book inf'; book.save(function(err, b) { tt.equal(book.createdAt, b.createdAt); @@ -39,7 +37,6 @@ test('loopback datasource timestamps', function(tap) { }); t.test('should not change on update', function(tt) { - var createdAt; Widget.destroyAll(function() { Widget.create({name:'book 1', type:'fiction'}, function(err, book) { tt.error(err); @@ -53,6 +50,20 @@ test('loopback datasource timestamps', function(tap) { }); }); + t.test('should not change on upsert', function(tt) { + Widget.destroyAll(function() { + Widget.create({name:'book 1', type:'fiction'}, function(err, book) { + tt.error(err); + tt.ok(book.createdAt); + Widget.upsert({id: book.id, name:'book inf'}, function(err, b) { + tt.error(err); + tt.equal(book.createdAt.getTime(), b.createdAt.getTime()); + tt.end(); + }); + }); + }); + }); + t.test('should not change with bulk updates', function(tt) { var createdAt; Widget.destroyAll(function() { @@ -205,22 +216,19 @@ test('loopback datasource timestamps', function(tap) { tap.test('operation hook options', function(t) { t.test('should skip changing updatedAt when option passed', function(tt) { - var updated, book; Widget.destroyAll(function() { Widget.create({name:'book 1', type:'fiction'}, function(err, book1) { tt.error(err); tt.ok(book1.updatedAt); - updated = book1.updatedAt; - book = book1.toObject(); - book.name = 'book 2'; + var book = {id: book1.id, name:'book 2'}; Widget.updateOrCreate(book, {skipUpdatedAt: true}, function(err, book2) { tt.error(err); tt.ok(book2.updatedAt); - tt.equal(updated.getTime(), book2.updatedAt.getTime()); + tt.equal(book1.updatedAt.getTime(), book2.updatedAt.getTime()); tt.end(); }); diff --git a/time-stamp.js b/time-stamp.js index f9a2459..12edc88 100644 --- a/time-stamp.js +++ b/time-stamp.js @@ -23,6 +23,12 @@ exports['default'] = function (Model) { debug('options', options); + debug('Model.settings.validateUpsert', Model.settings.validateUpsert); + if (Model.settings.validateUpsert && options.required) { + console.warn('TimeStamp mixin requires validateUpsert be false. See @clarkbw/loopback-ds-timestamp-mixin#10'); + } + Model.settings.validateUpsert = false; + Model.defineProperty(options.createdAt, { type: Date, required: options.required, defaultFn: 'now' }); Model.defineProperty(options.updatedAt, { type: Date, required: options.required }); diff --git a/time-stamp.js.map b/time-stamp.js.map index f44aeec..8ec7d4e 100644 --- a/time-stamp.js.map +++ b/time-stamp.js.map @@ -1 +1 @@ -{"version":3,"sources":["time-stamp.js"],"names":[],"mappings":";;;;;;;;;;sBAAmB,SAAS;;;;AAC5B,IAAM,KAAK,GAAG,yBAAQ,CAAC;;qBAER,UAAC,KAAK,EAAmB;MAAjB,OAAO,yDAAG,EAAE;;AAEjC,OAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;;AAEvD,SAAO,GAAG,SAAc,EAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAC,EAAE,OAAO,CAAC,CAAC;;AAEnG,OAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;;AAE1B,OAAK,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,EAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAC,CAAC,CAAC;AACpG,OAAK,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,EAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAC,CAAC,CAAC;;AAElF,OAAK,CAAC,OAAO,CAAC,aAAa,EAAE,UAAC,GAAG,EAAE,IAAI,EAAK;AAC1C,SAAK,CAAC,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AAClC,QAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE;AAAE,aAAO,IAAI,EAAE,CAAC;KAAE;AAChE,QAAI,GAAG,CAAC,QAAQ,EAAE;AAChB,WAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxF,SAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;KAC9C,MAAM;AACL,WAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AAClG,SAAG,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;KAC1C;AACD,QAAI,EAAE,CAAC;GACR,CAAC,CAAC;CAEJ","file":"time-stamp.js","sourcesContent":["import _debug from './debug';\nconst debug = _debug();\n\nexport default (Model, options = {}) => {\n\n debug('TimeStamp mixin for Model %s', Model.modelName);\n\n options = Object.assign({createdAt: 'createdAt', updatedAt: 'updatedAt', required: true}, options);\n\n debug('options', options);\n\n Model.defineProperty(options.createdAt, {type: Date, required: options.required, defaultFn: 'now'});\n Model.defineProperty(options.updatedAt, {type: Date, required: options.required});\n\n Model.observe('before save', (ctx, next) => {\n debug('ctx.options', ctx.options);\n if (ctx.options && ctx.options.skipUpdatedAt) { return next(); }\n if (ctx.instance) {\n debug('%s.%s before save: %s', ctx.Model.modelName, options.updatedAt, ctx.instance.id);\n ctx.instance[options.updatedAt] = new Date();\n } else {\n debug('%s.%s before update matching %j', ctx.Model.pluralModelName, options.updatedAt, ctx.where);\n ctx.data[options.updatedAt] = new Date();\n }\n next();\n });\n\n};\n"],"sourceRoot":"/Users/clarkbw/src/pi/loopback-ds-timestamp-mixin/es6"} \ No newline at end of file +{"version":3,"sources":["time-stamp.js"],"names":[],"mappings":";;;;;;;;;;sBAAmB,SAAS;;;;AAC5B,IAAM,KAAK,GAAG,yBAAQ,CAAC;;qBAER,UAAC,KAAK,EAAmB;MAAjB,OAAO,yDAAG,EAAE;;AAEjC,OAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;;AAEvD,SAAO,GAAG,SAAc,EAAC,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAC,EAAE,OAAO,CAAC,CAAC;;AAEnG,OAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;;AAE1B,OAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACtE,MAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,QAAQ,EAAE;AACrD,WAAO,CAAC,IAAI,CAAC,+FAA+F,CAAC,CAAC;GAC/G;AACD,OAAK,CAAC,QAAQ,CAAC,cAAc,GAAG,KAAK,CAAC;;AAEtC,OAAK,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,EAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAC,CAAC,CAAC;AACpG,OAAK,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,EAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAC,CAAC,CAAC;;AAElF,OAAK,CAAC,OAAO,CAAC,aAAa,EAAE,UAAC,GAAG,EAAE,IAAI,EAAK;AAC1C,SAAK,CAAC,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AAClC,QAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE;AAAE,aAAO,IAAI,EAAE,CAAC;KAAE;AAChE,QAAI,GAAG,CAAC,QAAQ,EAAE;AAChB,WAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxF,SAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;KAC9C,MAAM;AACL,WAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AAClG,SAAG,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;KAC1C;AACD,QAAI,EAAE,CAAC;GACR,CAAC,CAAC;CAEJ","file":"time-stamp.js","sourcesContent":["import _debug from './debug';\nconst debug = _debug();\n\nexport default (Model, options = {}) => {\n\n debug('TimeStamp mixin for Model %s', Model.modelName);\n\n options = Object.assign({createdAt: 'createdAt', updatedAt: 'updatedAt', required: true}, options);\n\n debug('options', options);\n\n debug('Model.settings.validateUpsert', Model.settings.validateUpsert);\n if (Model.settings.validateUpsert && options.required) {\n console.warn('TimeStamp mixin requires validateUpsert be false. See @clarkbw/loopback-ds-timestamp-mixin#10');\n }\n Model.settings.validateUpsert = false;\n\n Model.defineProperty(options.createdAt, {type: Date, required: options.required, defaultFn: 'now'});\n Model.defineProperty(options.updatedAt, {type: Date, required: options.required});\n\n Model.observe('before save', (ctx, next) => {\n debug('ctx.options', ctx.options);\n if (ctx.options && ctx.options.skipUpdatedAt) { return next(); }\n if (ctx.instance) {\n debug('%s.%s before save: %s', ctx.Model.modelName, options.updatedAt, ctx.instance.id);\n ctx.instance[options.updatedAt] = new Date();\n } else {\n debug('%s.%s before update matching %j', ctx.Model.pluralModelName, options.updatedAt, ctx.where);\n ctx.data[options.updatedAt] = new Date();\n }\n next();\n });\n\n};\n"],"sourceRoot":"/Users/clarkbw/src/pi/loopback-ds-timestamp-mixin/es6"} \ No newline at end of file