Merge pull request #1361 from nVitius/Fix/2.x/NestedProperties

Fix/2.x/nested properties
This commit is contained in:
Sakib Hasan 2017-07-13 21:34:01 -04:00 committed by GitHub
commit 1d8ca908f6
4 changed files with 140 additions and 59 deletions

View File

@ -351,28 +351,46 @@ Memory.prototype.fromDb = function(model, data) {
data = deserialize(data);
var props = this._models[model].properties;
for (var key in data) {
var val = data[key];
if (val === undefined || val === null) {
continue;
}
if (props[key]) {
switch (props[key].type.name) {
case 'Date':
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
break;
case 'Boolean':
val = Boolean(val);
break;
case 'Number':
val = Number(val);
break;
}
}
data[key] = val;
data[key] = this._castPropertyValue(key, data[key], props);
}
return data;
};
Memory.prototype._castPropertyValue = function(prop, val, props) {
var self = this;
if (val === undefined || val === null || !props[prop]) {
return val;
}
if (Array.isArray(val)) {
return val.map(function(val) {
return self._castPropertyValue(prop, val, props);
});
}
var isArray = Array.isArray(props[prop].type);
var propType = isArray ? props[prop].type[0] : props[prop].type;
switch (propType.name) {
case 'Date':
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
break;
case 'Boolean':
val = Boolean(val);
break;
case 'Number':
val = Number(val);
break;
case 'ModelConstructor':
for (var subProp in val) {
val[subProp] = this._castPropertyValue(subProp, val[subProp], propType.definition.properties);
}
break;
}
return val;
};
function getValue(obj, path) {
if (obj == null) {
return undefined;

View File

@ -1565,12 +1565,14 @@ function coerceArray(val) {
* @returns {Object} The coerced where clause
* @private
*/
DataAccessObject._coerce = function(where) {
DataAccessObject._coerce = function(where, props) {
var self = this;
if (!where) {
return where;
}
props = props || self.definition.properties;
var err;
if (typeof where !== 'object' || Array.isArray(where)) {
err = new Error(g.f('The where clause %j is not an {{object}}', where));
@ -1578,7 +1580,6 @@ DataAccessObject._coerce = function(where) {
throw err;
}
var props = self.definition.properties;
for (var p in where) {
// Handle logical operators
if (p === 'and' || p === 'or' || p === 'nor') {
@ -1597,6 +1598,22 @@ DataAccessObject._coerce = function(where) {
continue;
}
if (p.match(/\./)) {
var model = p.split('.')[0];
var prop = p.split('.').slice(1);
if (props[model]) {
var clause = {};
clause[prop] = where[p];
where[p] = Array.isArray(props[model].type) ?
self._coerce(clause, props[model].type[0].definition.properties)[prop] :
self._coerce(clause, props[model].type.definition.properties)[prop];
continue;
}
}
var DataType = props[p] && props[p].type;
if (!DataType) {
continue;

View File

@ -1374,6 +1374,11 @@ describe('DataAccessObject', function() {
age: Number,
vip: Boolean,
date: Date,
sub: {
date: Date,
bool: Boolean,
number: Number,
},
location: 'GeoPoint',
scores: [Number],
});
@ -1433,6 +1438,27 @@ describe('DataAccessObject', function() {
assert.deepEqual(where, {date: d});
});
it('coerces where clause for date types in nested properties', function() {
var d = new Date();
where = model._coerce({'sub.date': d});
assert.deepEqual(where, {'sub.date': d});
where = model._coerce({'sub.date': d.toISOString()});
assert.deepEqual(where, {'sub.date': d});
});
it('coerces where clause for Boolean types in nested properties', function() {
var bool = 'true';
where = model._coerce({'sub.bool': bool});
assert.strictEqual(where['sub.bool'], true);
});
it('coerces where clause for Number type in nested properties', function() {
var number = '123';
where = model._coerce({'sub.number': number});
assert.strictEqual(where['sub.number'], 123);
});
it('coerces where clause for boolean types', function() {
where = model._coerce({vip: 'true'});
assert.deepEqual(where, {vip: true});
@ -1518,8 +1544,7 @@ describe('DataAccessObject', function() {
INVALID_CLAUSES.forEach(function(where) {
var whereStr = JSON.stringify(where);
it('throws an error on malformed array-like object ' + whereStr,
function() {
it('throws an error on malformed array-like object ' + whereStr, function() {
assert.throws(
function() { model._coerce(where); },
/property has invalid clause/);
@ -1694,31 +1719,28 @@ describe('DataAccessObject', function() {
assert.deepEqual(where, {date: undefined});
});
it('does not coerce to a number for a simple value that produces NaN',
function() {
where = model._coerce({age: 'xyz'});
assert.deepEqual(where, {age: 'xyz'});
});
it('does not coerce to a number for a simple value that produces NaN', function() {
where = model._coerce({age: 'xyz'});
assert.deepEqual(where, {age: 'xyz'});
});
it('does not coerce to a number for a simple value in an array that produces NaN',
function() {
where = model._coerce({age: {inq: ['xyz', '12']}});
assert.deepEqual(where, {age: {inq: ['xyz', 12]}});
});
it('does not coerce to a number for a simple value in an array that produces NaN', function() {
where = model._coerce({age: {inq: ['xyz', '12']}});
assert.deepEqual(where, {age: {inq: ['xyz', 12]}});
});
// settings
it('gets settings in priority',
function() {
ds.settings.test = 'test';
assert.equal(model._getSetting('test'), ds.settings.test, 'Should get datasource setting');
ds.settings.test = undefined;
it('gets settings in priority', function() {
ds.settings.test = 'test';
assert.equal(model._getSetting('test'), ds.settings.test, 'Should get datasource setting');
ds.settings.test = undefined;
model.settings.test = 'test';
assert.equal(model._getSetting('test'), model.settings.test, 'Should get model settings');
model.settings.test = 'test';
assert.equal(model._getSetting('test'), model.settings.test, 'Should get model settings');
ds.settings.test = 'willNotGet';
assert.notEqual(model._getSetting('test'), ds.settings.test, 'Should not get datasource setting');
});
ds.settings.test = 'willNotGet';
assert.notEqual(model._getSetting('test'), ds.settings.test, 'Should not get datasource setting');
});
});
describe('ModelBuilder processing json files', function() {

View File

@ -157,6 +157,7 @@ describe('Memory connector', function() {
city: String,
state: String,
zipCode: String,
since: Date,
tags: [
{
tag: String,
@ -166,6 +167,7 @@ describe('Memory connector', function() {
friends: [
{
name: String,
since: Date,
},
],
});
@ -294,8 +296,7 @@ describe('Memory connector', function() {
});
});
it('should successfully extract 2 users using implied and & and',
function(done) {
it('should successfully extract 2 users using implied and & and', function(done) {
User.find({
where: {
name: 'John Lennon',
@ -435,15 +436,14 @@ describe('Memory connector', function() {
});
});
it('should work when a regex is provided without the regexp operator',
function(done) {
User.find({where: {name: /John.*/i}}, function(err, users) {
should.not.exist(err);
users.length.should.equal(1);
users[0].name.should.equal('John Lennon');
done();
});
});
it('should work when a regex is provided without the regexp operator', function(done) {
User.find({where: {name: /John.*/i}}, function(err, users) {
should.not.exist(err);
users.length.should.equal(1);
users[0].name.should.equal('John Lennon');
done();
});
});
it('should support the regexp operator with regex strings', function(done) {
User.find({where: {name: {regexp: '^J'}}}, function(err, users) {
@ -516,6 +516,28 @@ describe('Memory connector', function() {
});
});
it('should support date as nested property in query', function(done) {
var d = new Date('2017-01-01');
User.find({where: {'address.since': d}},
function(err, users) {
should.not.exist(err);
users.length.should.be.equal(1);
users[0].address.since.should.be.eql(d);
done();
});
});
it('should support date as array property in query', function(done) {
var d = new Date('1960-01-01');
User.find({where: {'friends.since': d}},
function(err, users) {
should.not.exist(err);
users.length.should.be.equal(2);
users[0].friends[0].since.should.be.eql(d);
done();
});
});
it('should deserialize values after saving in upsert', function(done) {
User.findOne({where: {seq: 1}}, function(err, paul) {
User.updateOrCreate({id: paul.id, name: 'Sir Paul McCartney'},
@ -553,15 +575,16 @@ describe('Memory connector', function() {
city: 'San Jose',
state: 'CA',
zipCode: '95131',
since: new Date('2017-01-01'),
tags: [
{tag: 'business'},
{tag: 'rent'},
],
},
friends: [
{name: 'Paul McCartney'},
{name: 'George Harrison'},
{name: 'Ringo Starr'},
{name: 'Paul McCartney', since: new Date('1960-01-01')},
{name: 'George Harrison', since: new Date('1960-01-01')},
{name: 'Ringo Starr', since: new Date('1960-01-01')},
],
children: ['Sean', 'Julian'],
},
@ -578,11 +601,12 @@ describe('Memory connector', function() {
city: 'San Mateo',
state: 'CA',
zipCode: '94065',
since: new Date('2017-02-01'),
},
friends: [
{name: 'John Lennon'},
{name: 'George Harrison'},
{name: 'Ringo Starr'},
{name: 'John Lennon', since: new Date('1960-01-01')},
{name: 'George Harrison', since: new Date('1960-01-01')},
{name: 'Ringo Starr', since: new Date('1960-01-01')},
],
children: ['Stella', 'Mary', 'Heather', 'Beatrice', 'James'],
},