mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
fix: resource advanced view filter.
This commit is contained in:
@@ -3,6 +3,7 @@ import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbst
|
||||
import {
|
||||
buildFilterQuery,
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
import { IFilterRole } from 'interfaces';
|
||||
|
||||
export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
||||
/**
|
||||
@@ -10,31 +11,17 @@ export default class FilterRoles extends DynamicFilterRoleAbstructor {
|
||||
* @param {Array} filterRoles -
|
||||
* @param {Array} resourceFields -
|
||||
*/
|
||||
constructor(filterRoles, resourceFields) {
|
||||
constructor(filterRoles: IFilterRole[]) {
|
||||
super();
|
||||
|
||||
this.filterRoles = filterRoles.map((role, index) => ({
|
||||
...role,
|
||||
index: index + 1,
|
||||
columnKey: role.field_key,
|
||||
condition: role.comparator === 'AND' ? '&&' : '||',
|
||||
}));
|
||||
this.resourceFields = resourceFields;
|
||||
this.filterRoles = filterRoles;
|
||||
}
|
||||
|
||||
validateFilterRoles() {
|
||||
const filterFieldsKeys = this.filterRoles.map((r) => r.field_key);
|
||||
const resourceFieldsKeys = this.resourceFields.map((r) => r.key);
|
||||
|
||||
return difference(filterFieldsKeys, resourceFieldsKeys);
|
||||
}
|
||||
|
||||
// @private
|
||||
buildLogicExpression() {
|
||||
private buildLogicExpression(): string {
|
||||
let expression = '';
|
||||
this.filterRoles.forEach((role, index) => {
|
||||
expression += (index === 0)
|
||||
? `${role.index} ` : `${role.condition} ${role.index} `;
|
||||
expression += (index === 0) ?
|
||||
`${role.index} ` :
|
||||
`${role.condition} ${role.index} `;
|
||||
});
|
||||
return expression.trim();
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
export default class DynamicFilterAbstructor {
|
||||
constructor() {
|
||||
this.filterRoles = [];
|
||||
this.tableName = '';
|
||||
}
|
||||
|
||||
setTableName(tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
buildLogicExpression() {}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
validateFilterRoles() {}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
buildQuery() {}
|
||||
}
|
||||
10
server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts
Normal file
10
server/src/lib/DynamicFilter/DynamicFilterRoleAbstructor.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IFilterRole, IDynamicFilter } from "interfaces";
|
||||
|
||||
export default class DynamicFilterAbstructor implements IDynamicFilter {
|
||||
filterRoles: IFilterRole[] = [];
|
||||
tableName: string;
|
||||
|
||||
setTableName(tableName) {
|
||||
this.tableName = tableName;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||
import { getRoleFieldColumn } from 'lib/ViewRolesBuilder';
|
||||
|
||||
export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
||||
constructor(sortByFieldKey, sortDirection) {
|
||||
super();
|
||||
|
||||
this.filterRoles = {
|
||||
columnKey: sortByFieldKey,
|
||||
value: sortDirection,
|
||||
comparator: 'sort_by',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds database query of sort by column on the given direction.
|
||||
*/
|
||||
buildQuery() {
|
||||
const { columnKey = null, value = null } = this.filterRoles;
|
||||
|
||||
return (builder) => {
|
||||
const fieldRelation = getRoleFieldColumn(this.tableName, columnKey);
|
||||
const comparatorColumn =
|
||||
fieldRelation.relationColumn ||
|
||||
`${this.tableName}.${fieldRelation.column}`;
|
||||
|
||||
if (columnKey) {
|
||||
builder.orderBy(`${comparatorColumn}`, value.toLowerCase());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
40
server/src/lib/DynamicFilter/DynamicFilterSortBy.ts
Normal file
40
server/src/lib/DynamicFilter/DynamicFilterSortBy.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||
import { getRoleFieldColumn, validateFieldKeyExistance } from 'lib/ViewRolesBuilder';
|
||||
|
||||
export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
|
||||
sortRole: { fieldKey: string, order: string } = {};
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {string} sortByFieldKey
|
||||
* @param {string} sortDirection
|
||||
*/
|
||||
constructor(sortByFieldKey: string, sortDirection: string) {
|
||||
super();
|
||||
|
||||
this.sortRole = {
|
||||
fieldKey: sortByFieldKey,
|
||||
order: sortDirection,
|
||||
};
|
||||
}
|
||||
|
||||
validate() {
|
||||
validateFieldKeyExistance(this.tableName, this.sortRole.fieldKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds database query of sort by column on the given direction.
|
||||
*/
|
||||
buildQuery() {
|
||||
return (builder) => {
|
||||
const fieldRelation = getRoleFieldColumn(this.tableName, this.sortRole.fieldKey);
|
||||
const comparatorColumn =
|
||||
fieldRelation.relationColumn ||
|
||||
`${this.tableName}.${fieldRelation.column}`;
|
||||
|
||||
if (this.sortRole.fieldKey) {
|
||||
builder.orderBy(`${comparatorColumn}`, this.sortRole.order);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IFilterRole } from 'interfaces';
|
||||
import DynamicFilterRoleAbstructor from 'lib/DynamicFilter/DynamicFilterRoleAbstructor';
|
||||
import {
|
||||
validateViewRoles,
|
||||
@@ -5,20 +6,20 @@ import {
|
||||
} from 'lib/ViewRolesBuilder';
|
||||
|
||||
export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
logicExpression: string;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {*} filterRoles -
|
||||
* @param {*} logicExpression -
|
||||
* @param {*} filterRoles - Filter roles.
|
||||
* @param {*} logicExpression - Logic expression.
|
||||
*/
|
||||
constructor(filterRoles, logicExpression) {
|
||||
constructor(filterRoles: IFilterRole[], logicExpression: string) {
|
||||
super();
|
||||
|
||||
this.filterRoles = filterRoles;
|
||||
this.logicExpression = logicExpression
|
||||
.replace('AND', '&&')
|
||||
.replace('OR', '||');
|
||||
|
||||
this.tableName = '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +32,7 @@ export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
|
||||
/**
|
||||
* Validates filter roles.
|
||||
*/
|
||||
validateFilterRoles() {
|
||||
validate() {
|
||||
return validateViewRoles(this.filterRoles, this.logicExpression);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,46 @@
|
||||
import { difference } from 'lodash';
|
||||
import { difference, filter } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { Lexer } from 'lib/LogicEvaluation/Lexer';
|
||||
import Parser from 'lib/LogicEvaluation/Parser';
|
||||
import QueryParser from 'lib/LogicEvaluation/QueryParser';
|
||||
import resourceFieldsKeys from 'data/ResourceFieldsKeys';
|
||||
import { IFilterRole } from 'interfaces';
|
||||
|
||||
// const role = {
|
||||
// compatotor: String,
|
||||
// value: String,
|
||||
// columnKey: String,
|
||||
// columnSlug: String,
|
||||
// index: Number,
|
||||
// }
|
||||
const numberRoleQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
switch (role.comparator) {
|
||||
case 'equals':
|
||||
case 'equal':
|
||||
default:
|
||||
return (builder) => {
|
||||
builder.where(comparatorColumn, '=', role.value);
|
||||
};
|
||||
case 'not_equals':
|
||||
case 'not_equal':
|
||||
return (builder) => {
|
||||
builder.whereNot(comparatorColumn, role.value);
|
||||
};
|
||||
case 'bigger_than':
|
||||
case 'bigger':
|
||||
return (builder) => {
|
||||
builder.where(comparatorColumn, '>', role.value);
|
||||
};
|
||||
case 'bigger_or_equals':
|
||||
return (builder) => {
|
||||
builder.where(comparatorColumn, '>=', role.value);
|
||||
};
|
||||
case 'smaller_than':
|
||||
case 'smaller':
|
||||
return (builder) => {
|
||||
builder.where(comparatorColumn, '<', role.value);
|
||||
};
|
||||
case 'smaller_or_equals':
|
||||
return (builder) => {
|
||||
builder.where(comparatorColumn, '<=', role.value);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const textRoleQueryBuilder = (role, comparatorColumn) => {
|
||||
const textRoleQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
switch (role.comparator) {
|
||||
case 'equals':
|
||||
default:
|
||||
@@ -39,7 +65,7 @@ const textRoleQueryBuilder = (role, comparatorColumn) => {
|
||||
}
|
||||
};
|
||||
|
||||
const dateQueryBuilder = (role, comparatorColumn) => {
|
||||
const dateQueryBuilder = (role: IFilterRole, comparatorColumn: string) => {
|
||||
switch(role.comparator) {
|
||||
case 'after':
|
||||
case 'before':
|
||||
@@ -81,11 +107,11 @@ const dateQueryBuilder = (role, comparatorColumn) => {
|
||||
/**
|
||||
* Get field column metadata and its relation with other tables.
|
||||
* @param {String} tableName - Table name of target column.
|
||||
* @param {String} columnKey - Target column key that stored in resource field.
|
||||
* @param {String} fieldKey - Target column key that stored in resource field.
|
||||
*/
|
||||
export function getRoleFieldColumn(tableName, columnKey) {
|
||||
export function getRoleFieldColumn(tableName: string, fieldKey: string) {
|
||||
const tableFields = resourceFieldsKeys[tableName];
|
||||
return (tableFields[columnKey]) ? tableFields[columnKey] : null;
|
||||
return (tableFields[fieldKey]) ? tableFields[fieldKey] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,13 +119,15 @@ export function getRoleFieldColumn(tableName, columnKey) {
|
||||
* @param {String} tableName -
|
||||
* @param {Object} role -
|
||||
*/
|
||||
export function buildRoleQuery(tableName, role) {
|
||||
const fieldRelation = getRoleFieldColumn(tableName, role.columnKey);
|
||||
export function buildRoleQuery(tableName: string, role: IFilterRole) {
|
||||
const fieldRelation = getRoleFieldColumn(tableName, role.fieldKey);
|
||||
const comparatorColumn = fieldRelation.relationColumn || `${tableName}.${fieldRelation.column}`;
|
||||
|
||||
switch (fieldRelation.columnType) {
|
||||
case 'number':
|
||||
return numberRoleQueryBuilder(role, comparatorColumn);
|
||||
case 'date':
|
||||
return dateQueryBuilder(role, comparatorColumn);
|
||||
return dateQueryBuilder(role, comparatorColumn);
|
||||
case 'text':
|
||||
case 'varchar':
|
||||
default:
|
||||
@@ -112,7 +140,7 @@ export function buildRoleQuery(tableName, role) {
|
||||
* @param {String} column -
|
||||
* @return {String} - join relation table.
|
||||
*/
|
||||
export const getTableFromRelationColumn = (column) => {
|
||||
export const getTableFromRelationColumn = (column: string) => {
|
||||
const splitedColumn = column.split('.');
|
||||
return (splitedColumn.length > 0) ? splitedColumn[0] : '';
|
||||
};
|
||||
@@ -122,10 +150,10 @@ export const getTableFromRelationColumn = (column) => {
|
||||
* @param {String} tableName -
|
||||
* @param {Array} roles -
|
||||
*/
|
||||
export function buildFilterRolesJoins(tableName, roles) {
|
||||
export function buildFilterRolesJoins(tableName: string, roles: IFilterRole[]) {
|
||||
return (builder) => {
|
||||
roles.forEach((role) => {
|
||||
const fieldColumn = getRoleFieldColumn(tableName, role.columnKey);
|
||||
const fieldColumn = getRoleFieldColumn(tableName, role.fieldKey);
|
||||
|
||||
if (fieldColumn.relation) {
|
||||
const joinTable = getTableFromRelationColumn(fieldColumn.relation);
|
||||
@@ -135,7 +163,7 @@ export function buildFilterRolesJoins(tableName, roles) {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSortColumnJoin(tableName, sortColumnKey) {
|
||||
export function buildSortColumnJoin(tableName: string, sortColumnKey: string) {
|
||||
return (builder) => {
|
||||
const fieldColumn = getRoleFieldColumn(tableName, sortColumnKey);
|
||||
|
||||
@@ -152,7 +180,7 @@ export function buildSortColumnJoin(tableName, sortColumnKey) {
|
||||
* @param {Array} roles -
|
||||
* @return {Function}
|
||||
*/
|
||||
export function buildFilterRolesQuery(tableName, roles, logicExpression = '') {
|
||||
export function buildFilterRolesQuery(tableName: string, roles: IFilterRole[], logicExpression: string = '') {
|
||||
const rolesIndexSet = {};
|
||||
|
||||
roles.forEach((role) => {
|
||||
@@ -176,32 +204,12 @@ export function buildFilterRolesQuery(tableName, roles, logicExpression = '') {
|
||||
* @param {Array} roles -
|
||||
* @param {String} logicExpression -
|
||||
*/
|
||||
export const buildFilterQuery = (tableName, roles, logicExpression) => {
|
||||
export const buildFilterQuery = (tableName: string, roles, logicExpression: string) => {
|
||||
return (builder) => {
|
||||
buildFilterRolesQuery(tableName, roles, logicExpression)(builder);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the view logic expression.
|
||||
* @param {String} logicExpression -
|
||||
* @param {Array} indexes -
|
||||
*/
|
||||
export function validateFilterLogicExpression(logicExpression, indexes) {
|
||||
const logicExpIndexes = logicExpression.match(/\d+/g) || [];
|
||||
return !difference(logicExpIndexes.map(Number), indexes).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates view roles.
|
||||
* @param {Array} roles -
|
||||
* @param {String} logicExpression -
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export function validateViewRoles(roles, logicExpression) {
|
||||
return validateFilterLogicExpression(logicExpression, roles.map((r) => r.index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapes the view roles to view conditionals.
|
||||
* @param {Array} viewRoles -
|
||||
@@ -211,9 +219,10 @@ export function mapViewRolesToConditionals(viewRoles) {
|
||||
return viewRoles.map((viewRole) => ({
|
||||
comparator: viewRole.comparator,
|
||||
value: viewRole.value,
|
||||
index: viewRole.index,
|
||||
|
||||
columnKey: viewRole.field.key,
|
||||
slug: viewRole.field.slug,
|
||||
index: viewRole.index,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -231,7 +240,7 @@ export function mapFilterRolesToDynamicFilter(roles) {
|
||||
* @param {String} columnKey -
|
||||
* @param {String} sortDirection -
|
||||
*/
|
||||
export function buildSortColumnQuery(tableName, columnKey, sortDirection) {
|
||||
export function buildSortColumnQuery(tableName: string, columnKey: string, sortDirection: string) {
|
||||
const fieldRelation = getRoleFieldColumn(tableName, columnKey);
|
||||
const sortColumn = fieldRelation.relation || `${tableName}.${fieldRelation.column}`;
|
||||
|
||||
@@ -239,4 +248,27 @@ export function buildSortColumnQuery(tableName, columnKey, sortDirection) {
|
||||
builder.orderBy(sortColumn, sortDirection);
|
||||
buildSortColumnJoin(tableName, columnKey)(builder);
|
||||
};
|
||||
}
|
||||
|
||||
export function validateFilterLogicExpression(logicExpression: string, indexes) {
|
||||
const logicExpIndexes = logicExpression.match(/\d+/g) || [];
|
||||
const diff = !difference(logicExpIndexes.map(Number), indexes).length;
|
||||
|
||||
}
|
||||
|
||||
export function validateRolesLogicExpression(logicExpression: string, roles: IFilterRole[]) {
|
||||
return validateFilterLogicExpression(logicExpression, roles.map((r) => r.index));
|
||||
}
|
||||
|
||||
export function validateFieldKeyExistance(tableName: string, fieldKey: string) {
|
||||
if (!resourceFieldsKeys?.[tableName]?.[fieldKey])
|
||||
return fieldKey;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
export function validateFilterRolesFieldsExistance(tableName, filterRoles: IFilterRole[]) {
|
||||
return filterRoles.filter((filterRole: IFilterRole) => {
|
||||
return validateFieldKeyExistance(tableName, filterRole.fieldKey);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user