From 328126440ffaf902a25c619cde42594a573a4745 Mon Sep 17 00:00:00 2001 From: "ics_home\\ananin" Date: Tue, 8 Dec 2020 09:57:18 +0500 Subject: [PATCH] Add InPredicate and tests --- CHANGELOG.md | 2 + addon/query/indexeddb-adapter.js | 19 +++++- addon/query/js-adapter.js | 18 ++++- addon/query/odata-adapter.js | 16 +++++ addon/query/predicate.js | 48 +++++++++++++ .../base/base-reading-in-predicates-test.js | 68 +++++++++++++++++++ .../odata/odata-reading-in-predicates-test.js | 6 ++ .../offline-reading-in-predicates-test.js | 6 ++ tests/unit/query/in-predicate-test.js | 20 ++++++ tests/unit/query/indexeddb-adapter-test.js | 34 +++++++++- tests/unit/query/js-adapter-test.js | 64 ++++++++++++++++- tests/unit/query/odata-adapter-test.js | 14 +++- 12 files changed, 309 insertions(+), 6 deletions(-) create mode 100644 tests/unit/CRUD/base/base-reading-in-predicates-test.js create mode 100644 tests/unit/CRUD/odata/odata-reading-in-predicates-test.js create mode 100644 tests/unit/CRUD/offline/offline-reading-in-predicates-test.js create mode 100644 tests/unit/query/in-predicate-test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a30a18c..c0a3c4e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log ## [Unreleased] +### Added +- The `InPredicate`. ## [2.6.0-beta.0] - 2020-10-06 ### Added diff --git a/addon/query/indexeddb-adapter.js b/addon/query/indexeddb-adapter.js index e7befaeb..96a929d1 100644 --- a/addon/query/indexeddb-adapter.js +++ b/addon/query/indexeddb-adapter.js @@ -4,6 +4,7 @@ import Ember from 'ember'; import FilterOperator from './filter-operator'; +import Condition from 'ember-flexberry-data/query/condition'; import { SimplePredicate, ComplexPredicate, @@ -13,7 +14,8 @@ import { GeographyPredicate, GeometryPredicate, TruePredicate, - FalsePredicate + FalsePredicate, + InPredicate } from './predicate'; import BaseAdapter from './base-adapter'; import JSAdapter from 'ember-flexberry-data/query/js-adapter'; @@ -701,6 +703,21 @@ function updateWhereClause(store, table, query) { return table.limit(0); } + if (predicate instanceof InPredicate) { + let predicates = []; + + predicate.valueArray.forEach(value => { + let sp = new SimplePredicate(predicate.attributePath, FilterOperator.Eq, value); + predicates.push(sp); + }); + + let finalPredicate = new ComplexPredicate(Condition.Or, ...predicates); + let queriWithIn = query; + queriWithIn.predicate = finalPredicate; + + return updateWhereClause(store, table, query); + } + if (predicate instanceof ComplexPredicate) { let datePredicates = predicate.predicates.filter(pred => pred instanceof DatePredicate); let jsAdapter = datePredicates.length > 0 ? new JSAdapter(Ember.getOwner(store).lookup('service:moment')) : new JSAdapter(); diff --git a/addon/query/js-adapter.js b/addon/query/js-adapter.js index 686a7e9d..1cf1f33c 100644 --- a/addon/query/js-adapter.js +++ b/addon/query/js-adapter.js @@ -9,7 +9,8 @@ import { GeographyPredicate, GeometryPredicate, TruePredicate, - FalsePredicate + FalsePredicate, + InPredicate } from './predicate'; import FilterOperator from './filter-operator'; import Condition from './condition'; @@ -257,7 +258,8 @@ export default class JSAdapter extends BaseAdapter { let b6 = predicate instanceof GeometryPredicate; let b7 = predicate instanceof TruePredicate; let b8 = predicate instanceof FalsePredicate; - if (b1 || b2 || b3 || b4 || b7 || b8) { + let b9 = predicate instanceof InPredicate; + if (b1 || b2 || b3 || b4 || b7 || b8 || b9) { let filterFunction = this.getAttributeFilterFunction(predicate, options); return this.getFilterFunctionAnd([filterFunction]); } @@ -378,6 +380,18 @@ export default class JSAdapter extends BaseAdapter { return () => false; } + if (predicate instanceof InPredicate) { + return (i) => { + let attribute = _this.getValue(i, predicate.attributePath); + let valuesArray = predicate.valueArray; + + let dataExist = !Ember.isNone(attribute) && !Ember.isNone(valuesArray); + let valueIncludes = dataExist && valuesArray.indexOf(attribute) !== -1; + + return valueIncludes; + }; + } + if (predicate instanceof DetailPredicate) { let detailFilter = _this.buildFilter(predicate.predicate, options); if (predicate.isAll) { diff --git a/addon/query/odata-adapter.js b/addon/query/odata-adapter.js index 43a4c663..389f697a 100644 --- a/addon/query/odata-adapter.js +++ b/addon/query/odata-adapter.js @@ -2,6 +2,7 @@ import Ember from 'ember'; import DS from 'ember-data'; import BaseAdapter from './base-adapter'; +import Condition from 'ember-flexberry-data/query/condition'; import { SimplePredicate, ComplexPredicate, @@ -14,6 +15,7 @@ import { IsOfPredicate, TruePredicate, FalsePredicate, + InPredicate } from './predicate'; import FilterOperator from './filter-operator'; import Information from '../utils/information'; @@ -321,6 +323,20 @@ export default class ODataAdapter extends BaseAdapter { return 'false'; } + if (predicate instanceof InPredicate) { + let separator = ` ${Condition.Or} `; + + let result = predicate.valueArray + .map(i => { + let sp = new SimplePredicate(predicate.attributePath, FilterOperator.Eq, i); + return this._convertPredicateToODataFilterClause(sp, modelName, prefix, level + 1); + }).join(separator); + + let lp = level > 0 ? '(' : ''; + let rp = level > 0 ? ')' : ''; + return lp + result + rp; + } + throw new Error(`Unknown predicate '${predicate}'`); } diff --git a/addon/query/predicate.js b/addon/query/predicate.js index 80266e73..372b74ca 100644 --- a/addon/query/predicate.js +++ b/addon/query/predicate.js @@ -1,3 +1,4 @@ +import Ember from 'ember'; import FilterOperator from './filter-operator'; import Condition from './condition'; import moment from 'moment'; @@ -681,6 +682,53 @@ export class FalsePredicate extends BasePredicate { } } +/** + * The class of in predicate. + * + * @namespace Query + * @class InPredicate + * @extends BasePredicate + * @constructor + */ +export class InPredicate extends BasePredicate { + constructor(attributePath, valueArray) { + super(); + + if (!attributePath) { + throw new Error('Attribute path is required for inPredicate constructor.'); + } + + if (!Ember.isArray(valueArray) && valueArray.length === 0) { + throw new Error('Array of compared values is required for inPredicate constructor.'); + } + + this._attributePath = attributePath; + this._valueArray = valueArray; + } + + /** + * The path to the attribute for predicate. + * + * @property attributePath + * @type {String} + * @public + */ + get attributePath() { + return this._attributePath; + } + + /** + * Array of values, that check of attribute exist. + * + * @property valueArray + * @type {Array} + * @public + */ + get valueArray() { + return this._valueArray; + } +} + /** * Combines specified predicates using `and` logic condition. * diff --git a/tests/unit/CRUD/base/base-reading-in-predicates-test.js b/tests/unit/CRUD/base/base-reading-in-predicates-test.js new file mode 100644 index 00000000..20a17ac7 --- /dev/null +++ b/tests/unit/CRUD/base/base-reading-in-predicates-test.js @@ -0,0 +1,68 @@ +import Ember from 'ember'; +import QueryBuilder from 'ember-flexberry-data/query/builder'; +import { InPredicate } from 'ember-flexberry-data/query/predicate'; + +export default function readingComplexPredicates(store, assert) { + assert.expect(3); + let done = assert.async(); + + Ember.run(() => { + initTestData(store) + + .then(() => { + let filterArrayValues = ['Vasya', 'SpiderMan', 'Batman']; + let ip = new InPredicate('name', filterArrayValues); + + let builder = new QueryBuilder(store, 'ember-flexberry-dummy-application-user').where(ip); + return store.query('ember-flexberry-dummy-application-user', builder.build()) + .then((data) => { + assert.equal(data.get('length'), 1, `Predicate "in" | Length`); + assert.ok(data.any(item => item.get('name') === 'Vasya'), `Predicate "in" | Data`); + }); + }) + + .then(() => { + let filterArrayValues = ['CaptainAmerica', 'SpiderMan', 'Batman']; + let ip = new InPredicate('name', filterArrayValues); + + let builder = new QueryBuilder(store, 'ember-flexberry-dummy-application-user').where(ip); + return store.query('ember-flexberry-dummy-application-user', builder.build()) + .then((data) => { + assert.equal(data.get('length'), 0, `Predicate "out" | Length`); + }); + }) + .catch((e) => { + console.log(e, e.message); + throw e; + }) + .finally(done); + }); +} + +function initTestData(store) { + return Ember.RSVP.Promise.all([ + store.createRecord('ember-flexberry-dummy-application-user', { + name: 'Vasya', + eMail: '1@mail.ru', + karma: 4 + }).save(), + + store.createRecord('ember-flexberry-dummy-application-user', { + name: 'Oleg', + eMail: '2@mail.ru', + karma: 5 + }).save(), + + store.createRecord('ember-flexberry-dummy-application-user', { + name: 'Oleg', + eMail: '3@mail.ru', + karma: 7 + }).save(), + + store.createRecord('ember-flexberry-dummy-application-user', { + name: 'Andrey', + eMail: '4@mail.ru', + karma: 6 + }).save() + ]); +} diff --git a/tests/unit/CRUD/odata/odata-reading-in-predicates-test.js b/tests/unit/CRUD/odata/odata-reading-in-predicates-test.js new file mode 100644 index 00000000..5b535fd5 --- /dev/null +++ b/tests/unit/CRUD/odata/odata-reading-in-predicates-test.js @@ -0,0 +1,6 @@ +import executeTest from './execute-odata-test'; +import readingInPredicates from '../base/base-reading-in-predicates-test'; + +executeTest('reading | predicates | in predicates', (store, assert) => { + readingInPredicates(store, assert); +}); diff --git a/tests/unit/CRUD/offline/offline-reading-in-predicates-test.js b/tests/unit/CRUD/offline/offline-reading-in-predicates-test.js new file mode 100644 index 00000000..738bad63 --- /dev/null +++ b/tests/unit/CRUD/offline/offline-reading-in-predicates-test.js @@ -0,0 +1,6 @@ +import executeTest from './execute-offline-test'; +import readingInPredicates from '../base/base-reading-in-predicates-test'; + +executeTest('reading | predicates | in predicates', (store, assert) => { + readingInPredicates(store, assert); +}); diff --git a/tests/unit/query/in-predicate-test.js b/tests/unit/query/in-predicate-test.js new file mode 100644 index 00000000..6dc20849 --- /dev/null +++ b/tests/unit/query/in-predicate-test.js @@ -0,0 +1,20 @@ +import { module, test } from 'qunit'; + +import { InPredicate } from 'ember-flexberry-data/query/predicate'; + +module('query'); + +test('predicate | in | constructor', function (assert) { + assert.throws(() => new InPredicate(), Error); + assert.throws(() => new InPredicate(''), Error); + assert.throws(() => new InPredicate(null), Error); + assert.throws(() => new InPredicate(null, ''), Error); + assert.throws(() => new InPredicate('', null), Error); + assert.throws(() => new InPredicate('', ''), Error); + assert.throws(() => new InPredicate('', 2), Error); + + let p = new InPredicate('City', ['Perm', 'Moscow', 'Paris']); + + assert.ok(p); + assert.equal(p.attributePath, 'City'); +}); diff --git a/tests/unit/query/indexeddb-adapter-test.js b/tests/unit/query/indexeddb-adapter-test.js index 5aafb263..3c9ab852 100644 --- a/tests/unit/query/indexeddb-adapter-test.js +++ b/tests/unit/query/indexeddb-adapter-test.js @@ -13,7 +13,8 @@ import { GeographyPredicate, GeometryPredicate, TruePredicate, - FalsePredicate + FalsePredicate, + InPredicate } from 'ember-flexberry-data/query/predicate'; import Condition from 'ember-flexberry-data/query/condition'; @@ -1634,3 +1635,34 @@ test('adapter | indexeddb | geometry predicate | intersects', (assert) => { assert.equal(result.data[2].id, 3); }); }); + +test('adapter | indexeddb | in predicate', (assert) => { + let data = { + employee: [ + { id: 1, City: 'Perm' }, + { id: 2, City: 'Paris' }, + { id: 3, City: 'Bobruisk' } + ] + }; + + let filterArrayValues = ['Perm', 'New Yourk', 'Moscow']; + + let ip1 = new InPredicate('City', filterArrayValues); + let builder = new QueryBuilder(storeIndexedbAdapterTest, modelNameIndexedbAdapterTest).where(ip1); + + executeTest(data, builder.build(), assert, (result) => { + assert.ok(result.data); + assert.equal(result.data.length, 1); + assert.equal(result.data[0].id, 1); + }); + + filterArrayValues = ['Mumbai', 'New Yourk', 'Moscow']; + + ip1 = new InPredicate('City', filterArrayValues); + builder = new QueryBuilder(storeIndexedbAdapterTest, modelNameIndexedbAdapterTest).where(ip1); + + executeTest(data, builder.build(), assert, (result) => { + assert.ok(result.data); + assert.equal(result.data.length, 0); + }); +}); diff --git a/tests/unit/query/js-adapter-test.js b/tests/unit/query/js-adapter-test.js index e296bc0b..3e76e884 100644 --- a/tests/unit/query/js-adapter-test.js +++ b/tests/unit/query/js-adapter-test.js @@ -12,7 +12,8 @@ import { GeographyPredicate, GeometryPredicate, TruePredicate, - FalsePredicate + FalsePredicate, + InPredicate } from 'ember-flexberry-data/query/predicate'; import startApp from '../../helpers/start-app'; @@ -687,3 +688,64 @@ test('adapter | js | false predicate | detail predicate', (assert) => { assert.ok(result); assert.equal(result.length, 0); }); + +test('adapter | js | in predicate', (assert) => { + const data = [ + { id: 1, City: 'Perm' }, + { id: 2, City: 'Paris' }, + { id: 3, City: 'Bobruisk' } + ]; + + let filterArrayValues = ['Perm', 'New Yourk', 'Moscow']; + + let ip1 = new InPredicate('City', filterArrayValues); + let builder = new QueryBuilder(store, 'employee').where(ip1); + let filter = adapter.buildFunc(builder.build()); + + let result = filter(data); + assert.ok(result); + assert.equal(result.length, 1); + assert.equal(result[0].id, 1); + + filterArrayValues = ['London', 'New Yourk', 'Moscow']; + + ip1 = new InPredicate('City', filterArrayValues); + builder = new QueryBuilder(store, 'employee').where(ip1); + filter = adapter.buildFunc(builder.build()); + + result = filter(data); + assert.ok(result); + assert.equal(result.length, 0); +}); + +test('adapter | js | in predicate | complex predicate', (assert) => { + const data = [ + { id: 1, Country: 'Argentina' }, + { id: 2, Country: 'Paragwaj' }, + { id: 3, Country: 'Russia' } + ]; + + let sp1 = new SimplePredicate('Country', FilterOperator.Eq, 'Argentina'); + + let filterArrayValues = ['Argentina', 'Uganda', 'USA']; + let ip1 = new InPredicate('Country', filterArrayValues); + + let cp1 = new ComplexPredicate(Condition.Or, sp1, ip1); + let builder = new QueryBuilder(store, 'employee').where(cp1); + let filter = adapter.buildFunc(builder.build()); + + let result = filter(data); + assert.ok(result); + assert.equal(result.length, 1); + + filterArrayValues = ['Italy', 'Uganda', 'USA']; + ip1 = new InPredicate('Country', filterArrayValues); + + cp1 = new ComplexPredicate(Condition.And, sp1, ip1); + builder = new QueryBuilder(store, 'employee').where(cp1); + filter = adapter.buildFunc(builder.build()); + + result = filter(data); + assert.ok(result); + assert.equal(result.length, 0); +}); diff --git a/tests/unit/query/odata-adapter-test.js b/tests/unit/query/odata-adapter-test.js index 44f057e4..24ed3a69 100644 --- a/tests/unit/query/odata-adapter-test.js +++ b/tests/unit/query/odata-adapter-test.js @@ -14,7 +14,8 @@ import { NotPredicate, IsOfPredicate, TruePredicate, - FalsePredicate + FalsePredicate, + InPredicate } from 'ember-flexberry-data/query/predicate'; import ODataAdapter from 'ember-flexberry-data/query/odata-adapter'; import startApp from '../../helpers/start-app'; @@ -800,6 +801,17 @@ test('adapter | odata | false predicate | details predicate', function (assert) runTest(assert, builder, 'EmberFlexberryDummyComments', `$filter=UserVotes/all(f:false)&$select=__PrimaryKey`); }); +test('adapter | odata | in predicate', (assert) => { + let filterArrayValues = ['Vasya', 'SpiderMan', 'Batman']; + let ip = new InPredicate('firstName', filterArrayValues); + + // Act. + let builder = new QueryBuilder(store, 'customer').where(ip); + + // Act && Assert. + runTest(assert, builder, 'Customers', `$filter=FirstName eq 'Vasya' or FirstName eq 'SpiderMan' or FirstName eq 'Batman'&$select=CustomerID`); +}); + function runTest(assert, builder, modelPath, expectedUrl) { let adapter = new ODataAdapter(`${baseUrl}/${modelPath}`, store); let url = adapter.getODataFullUrl(builder.build());