Skip to content

Commit

Permalink
Change Transformer static class to getResponse function and replace e…
Browse files Browse the repository at this point in the history
…slint with biome
  • Loading branch information
geoperez committed Nov 15, 2024
1 parent a9942f9 commit b4436ec
Show file tree
Hide file tree
Showing 3 changed files with 497 additions and 0 deletions.
36 changes: 36 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 4,
"lineEnding": "crlf",
"lineWidth": 120
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"noParameterAssign": "off"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "single"
}
}
}
293 changes: 293 additions & 0 deletions src/getResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
import {
AggregateFunctions,
ColumnDataType,
type ColumnModel,
ColumnSortDirection,
CompareOperators,
columnHasFilter,
isDateColum,
} from './Models';
import type GridRequest from './Models/GridRequest';
import GridResponse from './Models/GridResponse';
import { areDatesEqual, dateIsBetween, isDateAfter, isDateBefore } from './dateUtils';
import { parsePayload } from './utils';

const partialfilteringCurry = (column: ColumnModel) => (data: unknown[], action: (f: string) => boolean) =>
data.filter((row) =>
typeof row[column.name] === 'undefined' || row[column.name] === null ? false : action(row[column.name]),
);

const equalsDateFilter = (column: ColumnModel) => (row) => areDatesEqual(column, row[column.name], column.filterText);
const equalsStringFilter = (column: ColumnModel) => (x: string) => x.toLowerCase() === column.filterText.toLowerCase();
const equalsFilter = (column: ColumnModel) => (row) => row[column.name] === column.filterText;

const filterBetween = (column: ColumnModel) => (row) =>
row[column.name] >= column.filterText && row[column.name] <= column.filterArgument[0];

const filterByEquals = (column: ColumnModel, isDate: boolean, subset: unknown[], partialfiltering) => {
if (isDate) return subset.filter(equalsDateFilter(column));

if (column.dataType === ColumnDataType.String) return partialfiltering(subset, equalsStringFilter(column));

return subset.filter(equalsFilter(column));
};

const sortColumnsByOrder = (a: ColumnModel, b: ColumnModel) => {
if (a.sortOrder > b.sortOrder) return 1;
return b.sortOrder > a.sortOrder ? -1 : 0;
};

const distinctReducer = (column: ColumnModel) => (list: unknown[], r: unknown) => {
if (list.indexOf(r[column.name]) === -1) list.push(r[column.name]);

return list;
};

const filterAggregateColumns = (column: ColumnModel) =>
column.aggregate && column.aggregate.toLowerCase() !== AggregateFunctions.None.toLowerCase();

const minReducer = (column: ColumnModel) => (min, r) => (r[column.name] < min ? r[column.name] : min);
const maxReducer = (column: ColumnModel) => (max, r) => (r[column.name] > max ? r[column.name] : max);
const sumReducer = (column: ColumnModel) => (sum, r) =>
typeof r[column.name] === 'undefined' ? sum : sum + r[column.name];

const sortData = (sorts: { name: string; asc: boolean }[]) => (a, b) => {
let result = 0;

for (const current of sorts) {
const reverse = current.asc ? 1 : -1;

if (typeof a[current.name] === 'undefined' || typeof b[current.name] === 'undefined') {
result = reverse * -1;
break;
}

if (a[current.name] < b[current.name]) {
result = reverse * -1;
break;
}

if (a[current.name] > b[current.name]) {
result = reverse * 1;
break;
}
}

return result;
};

export const getResponse = (request: GridRequest, dataSource: unknown[]): GridResponse => {
const response = new GridResponse(request.counter);
response.totalRecordCount = dataSource.length;

let data = applyFreeTextSearch(request, dataSource);
data = applyFiltering(request, data);
data = applySorting(request, data);

response.filteredRecordCount = data.length;

if (request.take > -1) {
response.totalPages = Math.ceil(response.filteredRecordCount / request.take);

if (response.totalPages > 0) {
response.currentPage = request.skip / request.take + 1;
}
}

response.aggregationPayload = getAggregatePayload(request, data);

const sliceSize = request.take === -1 ? data.length : request.skip + request.take;

response.payload = data
.slice(request.skip, sliceSize)
.map((row: Record<string, unknown>) => parsePayload(row, request.columns));

return response;
};

const applyFreeTextSearch = (request: GridRequest, subset: unknown[]): unknown[] => {
if (request.searchText) {
const searchableColumns = request.columns.filter((x: ColumnModel) => x.searchable);

if (searchableColumns.length > 0) {
const filter = request.searchText.toLowerCase();
if (filter === '') {
return subset;
}

return subset.filter((item) =>
searchableColumns.some((x: ColumnModel) => {
if (typeof item[x.name] === 'undefined' || item[x.name] === null) {
return false;
}
return item[x.name].toLowerCase().indexOf(filter) > -1;
}),
);
}
}

return subset;
};

const applyFiltering = (request: GridRequest, subset: unknown[]): unknown[] => {
// biome-ignore lint/complexity/noForEach: <explanation>
request.columns
.filter((column: ColumnModel) => columnHasFilter(column))
.forEach((column: ColumnModel) => {
const isDate = isDateColum(column);
const partialfiltering = partialfilteringCurry(column);

switch (column.filterOperator) {
case CompareOperators.Equals:
subset = filterByEquals(column, isDate, subset, partialfiltering);
break;
case CompareOperators.NotEquals:
if (column.dataType === ColumnDataType.String) {
subset = partialfiltering(
subset,
(x: string) => x.toLowerCase() !== column.filterText.toLowerCase(),
);
} else {
subset = subset.filter((row) => row[column.name] !== column.filterText);
}
break;
case CompareOperators.Contains:
subset = partialfiltering(
subset,
(x) => x.toLowerCase().indexOf(column.filterText.toLowerCase()) >= 0,
);
subset = partialfiltering(
subset,
(x: string) => x.toLowerCase().indexOf(column.filterText.toLowerCase()) >= 0,
);
break;
case CompareOperators.NotContains:
subset = partialfiltering(
subset,
(x: string) => x.toLowerCase().indexOf(column.filterText.toLowerCase()) < 0,
);
break;
case CompareOperators.StartsWith:
subset = partialfiltering(subset, (x: string) =>
x.toLowerCase().startsWith(column.filterText.toLowerCase()),
);
break;
case CompareOperators.NotStartsWith:
subset = partialfiltering(
subset,
(x: string) => !x.toLowerCase().startsWith(column.filterText.toLowerCase()),
);
break;
case CompareOperators.EndsWith:
subset = partialfiltering(subset, (x: string) =>
x.toLowerCase().endsWith(column.filterText.toLowerCase()),
);
break;
case CompareOperators.NotEndsWith:
subset = partialfiltering(
subset,
(x: string) => !x.toLowerCase().endsWith(column.filterText.toLowerCase()),
);
break;
case CompareOperators.Gt:
if (isDate) {
subset = subset.filter((row) => isDateAfter(column, row[column.name], column.filterText));
} else {
subset = subset.filter((row) => row[column.name] > column.filterText);
}
break;
case CompareOperators.Gte:
if (isDate) {
subset = subset.filter((row) => isDateAfter(column, row[column.name], column.filterText, true));
} else {
subset = subset.filter((row) => row[column.name] >= column.filterText);
}
break;
case CompareOperators.Lt:
if (isDate) {
subset = subset.filter((row) => isDateBefore(column, row[column.name], column.filterText));
} else {
subset = subset.filter((row) => row[column.name] < column.filterText);
}
break;
case CompareOperators.Lte:
if (isDate) {
subset = subset.filter((row) =>
isDateBefore(column, row[column.name], column.filterText, true),
);
} else {
subset = subset.filter((row) => row[column.name] <= column.filterText);
}
break;
case CompareOperators.Between:
if (isDate) {
subset = subset.filter((row) =>
dateIsBetween(
column,
column.filterText,
column.filterArgument[0].toString(),
row[column.name],
),
);
} else {
subset = subset.filter(filterBetween(column));
}
break;
default:
throw new Error('Unsupported Compare Operator');
}
});

return subset;
};

const applySorting = (request: GridRequest, subset: unknown[]): unknown[] => {
const sortedColumns = request.columns.filter((column: ColumnModel) => column.sortOrder > 0);

let sorts: { name: string; asc: boolean }[] = [{ name: request.columns[0].name, asc: true }];

if (sortedColumns.length > 0) {
sortedColumns.sort(sortColumnsByOrder);

sorts = sortedColumns.map((y: ColumnModel) => ({
name: y.name,
asc: y.sortDirection === ColumnSortDirection.Ascending,
}));
}

subset.sort(sortData(sorts));

return subset;
};

const getAggregatePayload = (request: GridRequest, subset: unknown[]): Record<string, number> => {
const aggregateColumns = request.columns.filter(filterAggregateColumns);

return aggregateColumns.reduce((prev, column) => {
switch (column.aggregate.toLowerCase()) {
case AggregateFunctions.Sum.toLowerCase():
prev[column.name] = subset.length === 0 ? 0 : subset.reduce(sumReducer(column), 0);
break;
case AggregateFunctions.Average.toLowerCase():
prev[column.name] =
subset.length === 0 ? 0 : Number(subset.reduce(sumReducer(column), 0)) / subset.length;
break;
case AggregateFunctions.Max.toLowerCase():
prev[column.name] = subset.length === 0 ? 0 : subset.reduce(maxReducer(column), subset[0][column.name]);
break;
case AggregateFunctions.Min.toLowerCase():
prev[column.name] = subset.length === 0 ? 0 : subset.reduce(minReducer(column), subset[0][column.name]);
break;
case AggregateFunctions.Count.toLowerCase():
prev[column.name] = subset.length;
break;
case AggregateFunctions.DistinctCount.toLowerCase():
prev[column.name] = subset.length === 0 ? 0 : (subset.reduce(distinctReducer(column), []) as []).length;
break;
default:
throw new Error('Unsupported aggregate function');
}

return prev;
}, {});
};
Loading

0 comments on commit b4436ec

Please sign in to comment.