@unito/integration-sdk
Version:
Integration SDK
72 lines (60 loc) • 2.3 kB
text/typescript
import { OperatorType, OperatorTypes } from '@unito/integration-api';
import { Request, Response, NextFunction } from 'express';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Express {
interface Locals {
filters: Filter[];
}
}
}
/**
* Represents one filter parsed from the filters query param.
*
* Given a filters query param containing:
* `filters=[...],name=John,[...]`
*
* Filter for `name=John` will be:
* `{ field: 'name', operator: 'EQUAL', values: ['John'] }`
*/
export type Filter = {
field: string;
operator: OperatorType;
// Without the schema of the item,
// we can't determine the types of the values (number, boolean, etc).
values: string[] | undefined;
};
// The operators are ordered by their symbol length, in descending order.
// This is necessary because the symbol of an operator can be
// a subset of the symbol of another operator.
//
// For example, the symbol "=" (EQUAL) is a subset of the symbol "!=" (NOT_EQUAL).
const ORDERED_OPERATORS = Object.values(OperatorTypes).sort((o1, o2) => o2.length - o1.length);
function extractFilters(req: Request, res: Response, next: NextFunction) {
const rawFilters = req.query.filter;
res.locals.filters = [];
if (typeof rawFilters === 'string') {
for (const rawFilter of rawFilters.split(',')) {
// Find the operator that appears earliest in the string.
// When two operators start at the same index (e.g. ">=" and ">" both at position 5),
// the longer one wins because ORDERED_OPERATORS is sorted longest-first.
let bestIdx = -1;
let bestOperator: OperatorType | undefined;
for (const operator of ORDERED_OPERATORS) {
const idx = rawFilter.indexOf(operator);
if (idx !== -1 && (bestIdx === -1 || idx < bestIdx)) {
bestIdx = idx;
bestOperator = operator;
}
}
if (bestIdx !== -1 && bestOperator) {
const field = rawFilter.slice(0, bestIdx);
const valuesRaw = rawFilter.slice(bestIdx + bestOperator.length);
const values = valuesRaw ? valuesRaw.split('|').map(decodeURIComponent) : [];
res.locals.filters.push({ field, operator: bestOperator, values });
}
}
}
next();
}
export default extractFilters;