From 03753b6fe204ae6810f9e9d125527d720a9e8f8d Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Mon, 3 Jul 2017 10:04:37 -0700 Subject: [PATCH] Fix undefined properties in where https://github.com/strongloop/loopback/issues/2364 --- lib/dao.js | 19 +- test/normalize-undefined.test.js | 297 +++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 test/normalize-undefined.test.js diff --git a/lib/dao.js b/lib/dao.js index 38aeb5c1..cb93734c 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -778,7 +778,10 @@ DataAccessObject.upsertWithWhere = function(where, data, options, cb) { function callConnector() { try { - ctx.where = removeUndefined(ctx.where); + // Support an optional where object + var handleUndefined = Model._getSetting('normalizeUndefinedInQuery'); + // alter configuration of how removeUndefined handles undefined values + ctx.where = removeUndefined(ctx.where, handleUndefined); ctx.where = Model._coerce(ctx.where, options); update = removeUndefined(update); update = Model._coerce(update, options); @@ -2222,6 +2225,7 @@ DataAccessObject.destroyAll = function destroyAll(where, options, cb) { var hookState = {}; var query = {where: where}; + this.applyScope(query); where = query.where; @@ -2267,7 +2271,9 @@ DataAccessObject.destroyAll = function destroyAll(where, options, cb) { } else { try { // Support an optional where object - where = removeUndefined(where); + var handleUndefined = Model._getSetting('normalizeUndefinedInQuery'); + // alter configuration of how removeUndefined handles undefined values + where = removeUndefined(where, handleUndefined); where = Model._coerce(where, options); } catch (err) { return process.nextTick(function() { @@ -2424,7 +2430,9 @@ DataAccessObject.count = function(where, options, cb) { where = query.where; try { - where = removeUndefined(where); + var handleUndefined = Model._getSetting('normalizeUndefinedInQuery'); + // alter configuration of how removeUndefined handles undefined values + where = removeUndefined(where, handleUndefined); where = this._coerce(where, options); } catch (err) { process.nextTick(function() { @@ -2725,7 +2733,10 @@ DataAccessObject.updateAll = function(where, data, options, cb) { function doUpdate(where, data) { try { - where = removeUndefined(where); + // Support an optional where object + var handleUndefined = Model._getSetting('normalizeUndefinedInQuery'); + // alter configuration of how removeUndefined handles undefined values + where = removeUndefined(where, handleUndefined); where = Model._coerce(where, options); data = removeUndefined(data); data = Model._coerce(data, options); diff --git a/test/normalize-undefined.test.js b/test/normalize-undefined.test.js new file mode 100644 index 00000000..4c594326 --- /dev/null +++ b/test/normalize-undefined.test.js @@ -0,0 +1,297 @@ +// Copyright IBM Corp. 2014,2016. All Rights Reserved. +// Node module: loopback-datasource-juggler +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +'use strict'; +var jdb = require('../'); +var DataSource = jdb.DataSource; +var path = require('path'); +var fs = require('fs'); +var assert = require('assert'); +var async = require('async'); +var should = require('./init.js'); +var Memory = require('../lib/connectors/memory').Memory; + +describe('normalizeUndefinedInQuery', function() { + describe('with setting "throw"', function() { + var ds = new DataSource({ + connector: 'memory', + normalizeUndefinedInQuery: 'throw', + }); + + var User = ds.define('User', { + seq: {type: Number, index: true}, + name: {type: String, index: true, sort: true}, + email: {type: String, index: true}, + birthday: {type: Date, index: true}, + role: {type: String, index: true}, + order: {type: Number, index: true, sort: true}, + vip: {type: Boolean}, + address: { + street: String, + city: String, + state: String, + zipCode: String, + tags: [ + { + tag: String, + }, + ], + }, + friends: [ + { + name: String, + }, + ], + }); + + before(function(done) { + seed(User, done); + }); + + it('should throw if find where contains undefined', function(done) { + User.find({where: {name: undefined}}, function(err, users) { + should.exist(err); + done(); + }); + }); + + it('should throw if destroyAll where contains undefined', function(done) { + User.destroyAll({name: undefined}, function(err, count) { + should.exist(err); + done(); + }); + }); + + it('should throw if updateAll where contains undefined', function(done) { + User.updateAll({name: undefined}, {vip: false}, function(err, count) { + should.exist(err); + done(); + }); + }); + + it('should throw if upsertWithWhere where contains undefined', function(done) { + User.upsertWithWhere({name: undefined}, {vip: false}, function(err, count) { + should.exist(err); + done(); + }); + }); + + it('should throw if count where contains undefined', function(done) { + User.count({name: undefined}, function(err, count) { + should.exist(err); + done(); + }); + }); + }); + + describe('with setting "nullify"', function() { + var ds = new DataSource({ + connector: 'memory', + }); + + var User = ds.define('User', { + seq: {type: Number, index: true}, + name: {type: String, index: true, sort: true}, + email: {type: String, index: true}, + birthday: {type: Date, index: true}, + role: {type: String, index: true}, + order: {type: Number, index: true, sort: true}, + vip: {type: Boolean}, + address: { + street: String, + city: String, + state: String, + zipCode: String, + tags: [ + { + tag: String, + }, + ], + }, + friends: [ + { + name: String, + }, + ], + }, { + normalizeUndefinedInQuery: 'nullify', + }); + + before(function(done) { + seed(User, done); + }); + + it('should nullify if find where contains undefined', function(done) { + User.find({where: {role: undefined}}, function(err, users) { + should.not.exist(err); + users.length.should.eql(4); + done(); + }); + }); + + it('should nullify if updateAll where contains undefined', function(done) { + User.updateAll({role: undefined}, {vip: false}, function(err, count) { + should.not.exist(err); + count.count.should.eql(4); + done(); + }); + }); + + it('should nullify if upsertWithWhere where contains undefined', function(done) { + User.upsertWithWhere({role: undefined, order: 6}, {vip: false}, function(err, user) { + should.not.exist(err); + user.order.should.eql(6); + done(); + }); + }); + + it('should nullify if count where contains undefined', function(done) { + User.count({role: undefined}, function(err, count) { + should.not.exist(err); + count.should.eql(4); + done(); + }); + }); + + it('should nullify if destroyAll where contains undefined', function(done) { + User.destroyAll({role: undefined}, function(err, count) { + should.not.exist(err); + count.count.should.eql(4); + done(); + }); + }); + }); + + describe('with setting "ignore"', function() { + var ds = new DataSource({ + connector: 'memory', + }); + + var User = ds.define('User', { + seq: {type: Number, index: true}, + name: {type: String, index: true, sort: true}, + email: {type: String, index: true}, + birthday: {type: Date, index: true}, + role: {type: String, index: true}, + order: {type: Number, index: true, sort: true}, + vip: {type: Boolean}, + address: { + street: String, + city: String, + state: String, + zipCode: String, + tags: [ + { + tag: String, + }, + ], + }, + friends: [ + { + name: String, + }, + ], + }, { + normalizeUndefinedInQuery: 'ignore', + }); + + before(function(done) { + seed(User, done); + }); + + it('should ignore if find where contains undefined', function(done) { + User.find({where: {role: undefined}}, function(err, users) { + should.not.exist(err); + users.length.should.eql(6); + done(); + }); + }); + + it('should ignore if updateAll where contains undefined', function(done) { + User.updateAll({role: undefined}, {vip: false}, function(err, count) { + should.not.exist(err); + count.count.should.eql(6); + done(); + }); + }); + + it('should ignore if upsertWithWhere where contains undefined', function(done) { + User.upsertWithWhere({role: undefined, order: 6}, {vip: false}, function(err, user) { + should.not.exist(err); + user.order.should.eql(6); + done(); + }); + }); + + it('should ignore if count where contains undefined', function(done) { + User.count({role: undefined}, function(err, count) { + should.not.exist(err); + count.should.eql(6); + done(); + }); + }); + + it('should ignore if destroyAll where contains undefined', function(done) { + User.destroyAll({role: undefined}, function(err, count) { + should.not.exist(err); + count.count.should.eql(6); + done(); + }); + }); + }); +}); + +function seed(User, done) { + var beatles = [ + { + seq: 0, + name: 'John Lennon', + email: 'john@b3atl3s.co.uk', + role: 'lead', + birthday: new Date('1980-12-08'), + vip: true, + address: { + street: '123 A St', + city: 'San Jose', + state: 'CA', + zipCode: '95131', + tags: [{tag: 'business'}, {tag: 'rent'}], + }, + friends: [{name: 'Paul McCartney'}, {name: 'George Harrison'}, {name: 'Ringo Starr'}], + children: ['Sean', 'Julian'], + }, + { + seq: 1, + name: 'Paul McCartney', + email: 'paul@b3atl3s.co.uk', + role: 'lead', + birthday: new Date('1942-06-18'), + order: 1, + vip: true, + address: { + street: '456 B St', + city: 'San Mateo', + state: 'CA', + zipCode: '94065', + }, + friends: [{name: 'John Lennon'}, {name: 'George Harrison'}, {name: 'Ringo Starr'}], + children: ['Stella', 'Mary', 'Heather', 'Beatrice', 'James'], + }, + {seq: 2, name: 'George Harrison', role: null, order: 5, vip: false, children: ['Dhani']}, + {seq: 3, name: 'Ringo Starr', role: null, order: 6, vip: false}, + {seq: 4, name: 'Pete Best', role: null, order: 4, children: []}, + {seq: 5, name: 'Stuart Sutcliffe', role: null, order: 3, vip: true}, + ]; + + async.series( + [ + User.destroyAll.bind(User), + function(cb) { + async.each(beatles, User.create.bind(User), cb); + }, + ], + done + ); +}