From df7bd5671a11c49dc907c199d1049e8f5914144f Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 19 Oct 2021 16:56:22 -0400 Subject: [PATCH] feat: Refactor to a filterable-store --- addons/api/addon/services/filterable-store.js | 68 +++++++++++++++++++ addons/api/app/services/filterable-store.js | 1 + .../unit/services/filterable-store-test.js | 32 +++++++++ 3 files changed, 101 insertions(+) create mode 100644 addons/api/addon/services/filterable-store.js create mode 100644 addons/api/app/services/filterable-store.js create mode 100644 addons/api/tests/unit/services/filterable-store-test.js diff --git a/addons/api/addon/services/filterable-store.js b/addons/api/addon/services/filterable-store.js new file mode 100644 index 000000000..579e06178 --- /dev/null +++ b/addons/api/addon/services/filterable-store.js @@ -0,0 +1,68 @@ +import Store from '@ember-data/store'; + +class FilterQuery { + _config; + constructor(config) { + this._config = config; + } + + get query() { + const filters = this._buildFilters(this._config); + return this._joinFilters(filters, { inclusive: false }); + } + + /** + * Iterate through filter config to generate array of filter strings + * @param {object} config + */ + _buildFilters(config) { + let filters = []; + for (const key in config) { + filters.push(this._generateQuery(`/item/${key}`, config[`${key}`])); + } + return filters; + } + + /** + * For a single filter, build filter query + * @param {string} selector + * @param {*} values + */ + _generateQuery(selector, values) { + // TODO: Check for value type + const filters = values.map((value) => `"${selector}" == "${value}"`); + return this._joinFilters(filters, { inclusive: true }); + } + + /** + * Combine filter strings + * @param {array} filters + * @param {object} inclusive + */ + _joinFilters(filters, { inclusive }) { + return filters.join(inclusive ? ' or ' : ' and '); + } +} +export default class FilterableStoreService extends Store { + /** + * Filterable store queries using IDs + * Usage: + * // Filter for a user in sessions + * this.store.filterByIds('session', { scope_id, recursive: true }, { user: ['u_0987654321']}) + * // Filter for status in pending or terminated state and for a session id + * this.store.filterByIds('session', { scope_id }, + * { status: ['pending', 'terminated' ], target: ['s_U5EoFe4Jba']} + * ) + * // To remove filters + * this.store.filterByIds('session', { scope_id }) + * @param {string} modelName + * @param {object} queryOptions + * @param {string} filterQuery + */ + filterByIds(modelName, queryOptions, filterOptions) { + const filterQuery = new FilterQuery(filterOptions); + // Apply filters, if available + if (filterQuery.query) queryOptions.filter = filterQuery; + return this.query(modelName, queryOptions); + } +} diff --git a/addons/api/app/services/filterable-store.js b/addons/api/app/services/filterable-store.js new file mode 100644 index 000000000..c35db1dfc --- /dev/null +++ b/addons/api/app/services/filterable-store.js @@ -0,0 +1 @@ +export { default } from 'api/services/filterable-store'; diff --git a/addons/api/tests/unit/services/filterable-store-test.js b/addons/api/tests/unit/services/filterable-store-test.js new file mode 100644 index 000000000..149d1d992 --- /dev/null +++ b/addons/api/tests/unit/services/filterable-store-test.js @@ -0,0 +1,32 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Service | filterable-store', function (hooks) { + setupTest(hooks); + + let store; + + hooks.beforeEach(function () { + store = this.owner.lookup('service:filterable-store'); + + store.createRecord('account', { + id: 'i_1234567890', + scope: { + id: '123', + }, + }); + }); + + test('it supports query filtering', function (assert) { + tore.filterByIds('account', { scope_id: '123' }); + assert.ok(store); + }); + + test('it supports single attribute filters', function (assert) { + assert.ok(store); + }); + + test('it supports multiple attribute filters', function (assert) { + assert.ok(store); + }); +});