Refactor logic of options.allowExtendedOperators
Implement logic to allowExtendedOperators options per request, per Model and Datasource.
This commit is contained in:
parent
023856cbe9
commit
733ad1a024
62
lib/dao.js
62
lib/dao.js
|
@ -181,6 +181,31 @@ DataAccessObject.getConnector = function() {
|
|||
return this.getDataSource().connector;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify if allowExtendedOperators is enabled
|
||||
* @options {Object} [options] Optional options to use.
|
||||
* @property {Boolean} allowExtendedOperators.
|
||||
* @returns {Boolean} Returns `true` if allowExtendedOperators is enabled, else `false`.
|
||||
*/
|
||||
DataAccessObject._allowExtendedOperators = function(options) {
|
||||
options = options || {};
|
||||
|
||||
var Model = this;
|
||||
var dsSettings = this.getDataSource().settings;
|
||||
var allowExtendedOperators = dsSettings.allowExtendedOperators;
|
||||
// options settings enable allowExtendedOperators per request (for example if
|
||||
// enable allowExtendedOperators only server side);
|
||||
// model settings enable allowExtendedOperators only for specific model.
|
||||
// dataSource settings enable allowExtendedOperators globally (all models);
|
||||
// options -> model -> dataSource (connector)
|
||||
if (options.hasOwnProperty('allowExtendedOperators')) {
|
||||
allowExtendedOperators = options.allowExtendedOperators === true;
|
||||
} else if (Model.settings && Model.settings.hasOwnProperty('allowExtendedOperators')) {
|
||||
allowExtendedOperators = Model.settings.allowExtendedOperators === true;
|
||||
}
|
||||
return allowExtendedOperators;
|
||||
};
|
||||
|
||||
// Empty callback function
|
||||
function noCallback(err, result) {
|
||||
// NOOP
|
||||
|
@ -720,9 +745,9 @@ DataAccessObject.upsertWithWhere = function(where, data, options, cb) {
|
|||
function callConnector() {
|
||||
try {
|
||||
ctx.where = removeUndefined(ctx.where);
|
||||
ctx.where = Model._coerce(ctx.where);
|
||||
ctx.where = Model._coerce(ctx.where, options);
|
||||
update = removeUndefined(update);
|
||||
update = Model._coerce(update);
|
||||
update = Model._coerce(update, options);
|
||||
} catch (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
|
@ -1110,7 +1135,7 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb)
|
|||
query.limit = 1;
|
||||
|
||||
try {
|
||||
this._normalize(query);
|
||||
this._normalize(query, options);
|
||||
} catch (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
|
@ -1413,10 +1438,12 @@ var operators = {
|
|||
/*
|
||||
* Normalize the filter object and throw errors if invalid values are detected
|
||||
* @param {Object} filter The query filter object
|
||||
* @options {Object} [options] Optional options to use.
|
||||
* @property {Boolean} allowExtendedOperators.
|
||||
* @returns {Object} The normalized filter object
|
||||
* @private
|
||||
*/
|
||||
DataAccessObject._normalize = function(filter) {
|
||||
DataAccessObject._normalize = function(filter, options) {
|
||||
if (!filter) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -1497,7 +1524,7 @@ DataAccessObject._normalize = function(filter) {
|
|||
var handleUndefined = this._getSetting('normalizeUndefinedInQuery');
|
||||
// alter configuration of how removeUndefined handles undefined values
|
||||
filter = removeUndefined(filter, handleUndefined);
|
||||
this._coerce(filter.where);
|
||||
this._coerce(filter.where, options);
|
||||
return filter;
|
||||
};
|
||||
|
||||
|
@ -1555,15 +1582,19 @@ function coerceArray(val) {
|
|||
/*
|
||||
* Coerce values based the property types
|
||||
* @param {Object} where The where clause
|
||||
* @options {Object} [options] Optional options to use.
|
||||
* @property {Boolean} allowExtendedOperators.
|
||||
* @returns {Object} The coerced where clause
|
||||
* @private
|
||||
*/
|
||||
DataAccessObject._coerce = function(where) {
|
||||
DataAccessObject._coerce = function(where, options) {
|
||||
var self = this;
|
||||
if (!where) {
|
||||
return where;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
var err;
|
||||
if (typeof where !== 'object' || Array.isArray(where)) {
|
||||
err = new Error(g.f('The where clause %j is not an {{object}}', where));
|
||||
|
@ -1585,7 +1616,7 @@ DataAccessObject._coerce = function(where) {
|
|||
}
|
||||
|
||||
for (var k = 0; k < clauses.length; k++) {
|
||||
self._coerce(clauses[k]);
|
||||
self._coerce(clauses[k], options);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -1703,8 +1734,7 @@ DataAccessObject._coerce = function(where) {
|
|||
}
|
||||
} else {
|
||||
if (val != null) {
|
||||
const dsSettings = this.getDataSource().settings;
|
||||
const allowExtendedOperators = dsSettings.allowExtendedOperators;
|
||||
const allowExtendedOperators = self._allowExtendedOperators(options);
|
||||
if (operator === null && val instanceof RegExp) {
|
||||
// Normalize {name: /A/} to {name: {regexp: /A/}}
|
||||
operator = 'regexp';
|
||||
|
@ -1831,7 +1861,7 @@ DataAccessObject.find = function find(query, options, cb) {
|
|||
'all() must be implemented by the connector');
|
||||
|
||||
try {
|
||||
this._normalize(query);
|
||||
this._normalize(query, options);
|
||||
} catch (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
|
@ -2189,7 +2219,7 @@ DataAccessObject.destroyAll = function destroyAll(where, options, cb) {
|
|||
try {
|
||||
// Support an optional where object
|
||||
where = removeUndefined(where);
|
||||
where = Model._coerce(where);
|
||||
where = Model._coerce(where, options);
|
||||
} catch (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
|
@ -2345,7 +2375,7 @@ DataAccessObject.count = function(where, options, cb) {
|
|||
|
||||
try {
|
||||
where = removeUndefined(where);
|
||||
where = this._coerce(where);
|
||||
where = this._coerce(where, options);
|
||||
} catch (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
|
@ -2616,9 +2646,9 @@ DataAccessObject.updateAll = function(where, data, options, cb) {
|
|||
function doUpdate(where, data) {
|
||||
try {
|
||||
where = removeUndefined(where);
|
||||
where = Model._coerce(where);
|
||||
where = Model._coerce(where, options);
|
||||
data = removeUndefined(data);
|
||||
data = Model._coerce(data);
|
||||
data = Model._coerce(data, options);
|
||||
} catch (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
|
@ -3083,9 +3113,7 @@ function(data, options, cb) {
|
|||
if (isPKMissing(Model, cb))
|
||||
return cb.promise;
|
||||
|
||||
var allowExtendedOperators = connector.settings &&
|
||||
connector.settings.allowExtendedOperators;
|
||||
|
||||
var allowExtendedOperators = Model._allowExtendedOperators(options);
|
||||
var strict = this.__strict;
|
||||
var model = Model.modelName;
|
||||
var hookState = {};
|
||||
|
|
|
@ -8,32 +8,17 @@
|
|||
const DataSource = require('..').DataSource;
|
||||
const should = require('should');
|
||||
|
||||
describe('Model.settings.allowExtendedOperators', () => {
|
||||
context('DAO.find()', () => {
|
||||
it('converts extended operators to string value by default', () => {
|
||||
const TestModel = createTestModel();
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves extended operators wit allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
function extendedQuery() {
|
||||
// datasource modifies the query,
|
||||
// we have to build a new object for each test
|
||||
return {where: {value: {$exists: true}}};
|
||||
}
|
||||
});
|
||||
|
||||
function createTestModel(connectorSettings) {
|
||||
describe('allowExtendedOperators', () => {
|
||||
function createTestModel(connectorSettings, modelSettings) {
|
||||
const ds = createTestDataSource(connectorSettings);
|
||||
return ds.createModel('TestModel', {value: String});
|
||||
const TestModel = ds.createModel('TestModel', {value: String}, modelSettings);
|
||||
|
||||
TestModel.observe('persist', function(ctx, next) {
|
||||
ctx.Model.lastPersistedData = ctx.data;
|
||||
next();
|
||||
});
|
||||
|
||||
return TestModel;
|
||||
}
|
||||
|
||||
function createTestDataSource(connectorSettings) {
|
||||
|
@ -47,16 +32,309 @@ describe('Model.settings.allowExtendedOperators', () => {
|
|||
return new DataSource(connectorSettings);
|
||||
}
|
||||
|
||||
function extendedQuery() {
|
||||
// datasource modifies the query,
|
||||
// we have to build a new object for each test
|
||||
return {where: {value: {$exists: true}}};
|
||||
}
|
||||
|
||||
function setCustomData() {
|
||||
return {$set: {value: 'changed'}};
|
||||
}
|
||||
|
||||
function updateShouldHaveFailed() {
|
||||
throw new Error('updateAttributes() should have failed.');
|
||||
}
|
||||
|
||||
class TestConnector {
|
||||
constructor(dataSource) {
|
||||
}
|
||||
|
||||
create(model, data, options, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
updateAttributes(model, id, data, options, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
all(model, filter, options, callback) {
|
||||
// return the raw "value" query
|
||||
var instanceFound = {
|
||||
let instanceFound = {
|
||||
value: filter.where.value,
|
||||
};
|
||||
callback(null, [instanceFound]);
|
||||
}
|
||||
}
|
||||
|
||||
describe('dataSource.settings.allowExtendedOperators', () => {
|
||||
context('DAO.find()', () => {
|
||||
it('converts extended operators to string value by default', () => {
|
||||
const TestModel = createTestModel();
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves extended operators with allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` override data source settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true}, {allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` override data source settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override data source settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override data source settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('DAO.updateAttributes()', () => {
|
||||
it('`options.allowExtendedOperators` override data source settings - disable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false}, {strict: true});
|
||||
return TestModel.create({value: 'test'}).then((instance) => {
|
||||
return instance.updateAttributes(setCustomData(), {allowExtendedOperators: true})
|
||||
.then(() => {
|
||||
should(TestModel.lastPersistedData).eql(setCustomData());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override data source settings - enable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true}, {strict: true});
|
||||
return TestModel.create({value: 'test'}).then((inst) => {
|
||||
return inst.updateAttributes(setCustomData(), {allowExtendedOperators: false})
|
||||
.then(updateShouldHaveFailed, function onError(err) {
|
||||
should.exist(err);
|
||||
should(err.name).equal('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` override data source settings - ' +
|
||||
'disable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false},
|
||||
{strict: true, allowExtendedOperators: true});
|
||||
return TestModel.create({value: 'test'}).then((instance) => {
|
||||
return instance.updateAttributes(setCustomData()).then(() => {
|
||||
should(TestModel.lastPersistedData).eql(setCustomData());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` override data source settings - ' +
|
||||
'enable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true},
|
||||
{strict: true, allowExtendedOperators: false});
|
||||
return TestModel.create({value: 'test'}).then((inst) => {
|
||||
return inst.updateAttributes(setCustomData())
|
||||
.then(updateShouldHaveFailed, function onError(err) {
|
||||
should.exist(err);
|
||||
should(err.name).equal('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Model.settings.allowExtendedOperators', () => {
|
||||
context('DAO.find()', () => {
|
||||
it('preserves extended operators with allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel({}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor Model settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true}, {allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor Model settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery()).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override Model settings - converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` Model settings - preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('DAO.updateAttributes()', () => {
|
||||
it('`options.allowExtendedOperators` override Model settings - disable strict check', () => {
|
||||
const TestModel = createTestModel({}, {strict: true, allowExtendedOperators: false});
|
||||
return TestModel.create({value: 'test'}).then((instance) => {
|
||||
return instance.updateAttributes(setCustomData(), {allowExtendedOperators: true})
|
||||
.then(() => {
|
||||
should(TestModel.lastPersistedData).eql(setCustomData());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`options.allowExtendedOperators` override Model settings - enabled strict check', () => {
|
||||
const TestModel = createTestModel({}, {strict: true, allowExtendedOperators: true});
|
||||
return TestModel.create({value: 'test'}).then((inst) => {
|
||||
return inst.updateAttributes(setCustomData(), {allowExtendedOperators: false})
|
||||
.then(updateShouldHaveFailed, function onError(err) {
|
||||
should.exist(err);
|
||||
should(err.name).equal('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor Model settings - disable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false},
|
||||
{strict: true, allowExtendedOperators: true});
|
||||
return TestModel.create({value: 'test'}).then((instance) => {
|
||||
return instance.updateAttributes(setCustomData()).then(() => {
|
||||
should(TestModel.lastPersistedData).eql(setCustomData());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor Model settings - ' +
|
||||
'enable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true},
|
||||
{strict: true, allowExtendedOperators: false});
|
||||
return TestModel.create({value: 'test'}).then((inst) => {
|
||||
return inst.updateAttributes(setCustomData())
|
||||
.then(updateShouldHaveFailed, function onError(err) {
|
||||
should.exist(err);
|
||||
should(err.name).equal('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('options.allowExtendedOperators', () => {
|
||||
context('DAO.find()', () => {
|
||||
it('preserves extended operators with allowExtendedOperators set', () => {
|
||||
const TestModel = createTestModel();
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'converts extended operators', () => {
|
||||
const TestModel = createTestModel({}, {allowExtendedOperators: true});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).then((results) => {
|
||||
should(results[0].value).eql('[object Object]');
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` honor options settings - ' +
|
||||
'preserves extended operators', () => {
|
||||
const TestModel = createTestModel({}, {allowExtendedOperators: false});
|
||||
return TestModel.find(extendedQuery(), {allowExtendedOperators: true}).then((results) => {
|
||||
should(results[0].value).eql({$exists: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('DAO.updateAttributes()', () => {
|
||||
it('`Model.settings.allowExtendedOperators` honor options settings - disable strict check', () => {
|
||||
const TestModel = createTestModel({}, {strict: true, allowExtendedOperators: false});
|
||||
return TestModel.create({value: 'test'}).then((instance) => {
|
||||
return instance.updateAttributes(setCustomData(), {allowExtendedOperators: true})
|
||||
.then(() => {
|
||||
should(TestModel.lastPersistedData).eql(setCustomData());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`Model.settings.allowExtendedOperators` honor options settings - enable strict check', () => {
|
||||
const TestModel = createTestModel({}, {strict: true, allowExtendedOperators: true});
|
||||
return TestModel.create({value: 'test'}).then((inst) => {
|
||||
return inst.updateAttributes(setCustomData(), {allowExtendedOperators: false})
|
||||
.then(updateShouldHaveFailed, function onError(err) {
|
||||
should.exist(err);
|
||||
should(err.name).equal('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor options settings - disable strict check', () => {
|
||||
const TestModel = createTestModel({}, {strict: true});
|
||||
return TestModel.create({value: 'test'}).then((instance) => {
|
||||
return instance.updateAttributes(setCustomData(), {allowExtendedOperators: true})
|
||||
.then(() => {
|
||||
should(TestModel.lastPersistedData).eql(setCustomData());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('`dataSource.settings.allowExtendedOperators` honor options settings - enable strict check', () => {
|
||||
const TestModel = createTestModel({allowExtendedOperators: true}, {strict: true});
|
||||
return TestModel.create({value: 'test'}).then((inst) => {
|
||||
return inst.updateAttributes(setCustomData(), {allowExtendedOperators: false})
|
||||
.then(updateShouldHaveFailed, function onError(err) {
|
||||
should.exist(err);
|
||||
should(err.name).equal('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue