feat: 🎸 tweak resource-filter service and apply to sessions
This commit is contained in:
7 files changed
+108
-85
No files matched your search
@@ -0,0 +1,87 @@
|
||||
import Service from '@ember/service';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* Takes a POJO representing a query and converts it to a boolean expression
|
||||
* string which is understood by the API.
|
||||
*
|
||||
* @example
|
||||
* const filter = new ResourceFilter({
|
||||
* type: 'oidc',
|
||||
* status: ['active', 'pending']
|
||||
* });
|
||||
* console.log(filter.queryExpression);
|
||||
* // ("/item/type" == "oidc") and ("/item/status" == "active" or "/item/status" == "pending")
|
||||
*/
|
||||
export class ResourceFilter {
|
||||
// =attributes
|
||||
|
||||
#filterObject;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
get queryExpression() {
|
||||
const clauses = Object.keys(this.#filterObject)
|
||||
.map((key) => {
|
||||
const value = this.#filterObject[key];
|
||||
const valuesArray = Array.isArray(value) ? value : [value];
|
||||
return this.parenthetical(
|
||||
this.or(valuesArray.map((value) => this.equals(key, value)))
|
||||
);
|
||||
})
|
||||
.flat();
|
||||
return this.and(clauses);
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* @param {object} filterObject
|
||||
*/
|
||||
constructor(filterObject = {}) {
|
||||
this.#filterObject = Object.assign({}, filterObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param value
|
||||
*/
|
||||
equals(key, value) {
|
||||
return `"/item/${key}" == "${value}"`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} clauses
|
||||
*/
|
||||
and(clauses) {
|
||||
return clauses.join(' and ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} clauses
|
||||
*/
|
||||
or(clauses) {
|
||||
return clauses.join(' or ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
*/
|
||||
parenthetical(expression) {
|
||||
return `(${expression})`;
|
||||
}
|
||||
}
|
||||
|
||||
export default class ResourceFilterStoreService extends Service {
|
||||
@service store;
|
||||
|
||||
queryBy(modelName, filterObject = {}, storeQuery = {}) {
|
||||
console.log(filterObject);
|
||||
const filter = new ResourceFilter(filterObject);
|
||||
const query = Object.assign({}, storeQuery);
|
||||
// Apply filters to store query when available
|
||||
if (filter.queryExpression) query.filter = filter.queryExpression;
|
||||
return this.store.query(modelName, query);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import Service from '@ember/service';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
class ResourceFilters {
|
||||
buildQuery(resourceFilters) {
|
||||
const filters = this._buildFilters(resourceFilters);
|
||||
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) {
|
||||
// Coerce single string filters into an array for easy mapping
|
||||
if (!Array.isArray(values)) values = [values];
|
||||
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 ResourceFilteringStoreService extends Service {
|
||||
@service store;
|
||||
|
||||
queryBy(modelName, resourceParams, resourceFilters) {
|
||||
const filterQuery = new ResourceFilters().buildQuery(resourceFilters);
|
||||
// Apply filters to store query when available
|
||||
if (filterQuery) resourceParams.filter = filterQuery;
|
||||
return this.store.query(modelName, resourceParams);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/services/resource-filter-store';
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from 'api/services/resource-filtering-store';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { ResourceFilter } from 'api/services/resource-filter-store';
|
||||
|
||||
module('Unit | Service | resource-filter-store', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exports a ResourceFilter class that converts filterObjects to query expressions', function (assert) {
|
||||
assert.ok(ResourceFilter);
|
||||
const filter = new ResourceFilter({
|
||||
type: 'oidc',
|
||||
status: ['active', 'pending'],
|
||||
});
|
||||
const expected =
|
||||
'("/item/type" == "oidc") and ("/item/status" == "active" or "/item/status" == "pending")';
|
||||
assert.equal(filter.queryExpression, expected);
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { module, test, skip } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Service | resource-filtering-store', function (hooks) {
|
||||
setupTest(hooks);
|
||||
let service, store;
|
||||
hooks.beforeEach(function () {
|
||||
service = this.owner.lookup('service:resource-filtering-store');
|
||||
store = this.owner.lookup('service:store');
|
||||
|
||||
store.createRecord('session', {
|
||||
id: 's_1',
|
||||
scope: {
|
||||
id: '123',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
assert.ok(service);
|
||||
});
|
||||
|
||||
// TODO: Is it possible to capture mirage API requests in this test?
|
||||
skip('it supports query filters', function (assert) {
|
||||
service.queryBy('session', {}, { scope_id: '123' });
|
||||
assert.ok(service);
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,7 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
|
||||
|
||||
@service ipc;
|
||||
@service session;
|
||||
@service resourceFilterStore;
|
||||
|
||||
// =attributes
|
||||
|
||||
@@ -52,8 +53,7 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
|
||||
const { id: scope_id } = this.modelFor('scopes.scope');
|
||||
const { user_id } = this.session.data.authenticated;
|
||||
await this.store.query('target', { recursive: true, scope_id });
|
||||
return await this.store.query('session', {
|
||||
filter: `"/item/user_id" == "${user_id}"`,
|
||||
return await this.resourceFilterStore.queryBy('session', { user_id }, {
|
||||
recursive: true,
|
||||
scope_id
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user