// Copyright IBM Corp. 2016,2019. 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'; const DataSource = require('..').DataSource; const should = require('should'); describe('allowExtendedOperators', () => { function createTestModel(connectorSettings, modelSettings) { const ds = createTestDataSource(connectorSettings); 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) { connectorSettings = connectorSettings || {}; connectorSettings.connector = { initialize: (dataSource, cb) => { dataSource.connector = new TestConnector(dataSource); }, }; 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 const instanceFound = { value: filter.where.value, }; callback(null, [instanceFound]); } } function assertOperatorNotAllowed(err) { should.exist(err); err.message.should.match(/Operators "\$exists" are not allowed in query/); err.code.should.equal('OPERATOR_NOT_ALLOWED_IN_QUERY'); err.statusCode.should.equal(400); err.details.should.have.property('operators'); err.details.should.have.property('where'); } describe('dataSource.settings.allowExtendedOperators', () => { context('DAO.find()', () => { it('reports invalid operator by default', () => { const TestModel = createTestModel(); return TestModel.find(extendedQuery()).catch(err => { assertOperatorNotAllowed(err); }); }); 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 - ' + 'reports invalid operator', () => { const TestModel = createTestModel({allowExtendedOperators: true}, {allowExtendedOperators: false}); return TestModel.find(extendedQuery()).catch(err => { assertOperatorNotAllowed(err); }); }); 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 - ' + 'reports invalid operator', () => { const TestModel = createTestModel({allowExtendedOperators: true}); return TestModel.find(extendedQuery(), {allowExtendedOperators: false}).catch(err => { assertOperatorNotAllowed(err); }); }); 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 - ' + 'reports invalid operator', () => { const TestModel = createTestModel({allowExtendedOperators: true}, {allowExtendedOperators: false}); return TestModel.find(extendedQuery()).catch(err => { assertOperatorNotAllowed(err); }); }); 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}).catch(err => { assertOperatorNotAllowed(err); }); }); 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}).catch(err => { assertOperatorNotAllowed(err); }); }); 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}).catch(err => { assertOperatorNotAllowed(err); }); }); 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'); }); }); }); }); }); });