feat: extend filter with enable all ops config
This commit is contained in:
parent
0c45c6da63
commit
3fa48d4f63
20
README.md
20
README.md
@ -370,12 +370,28 @@ const config: PaginateConfig<CatEntity> = {
|
|||||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Single Filters
|
## Filters
|
||||||
|
|
||||||
Filter operators must be whitelisted per column in `PaginateConfig`.
|
Filter operators must be whitelisted per column in `PaginateConfig`.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
#### Code
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const config: PaginateConfig<CatEntity> = {
|
||||||
|
// ...
|
||||||
|
filterableColumns: {
|
||||||
|
// Whitelist operators individually
|
||||||
|
id: [FilterOperator.EQ, FilterSuffix.NOT],
|
||||||
|
'toys.name': [FilterOperator.IN],
|
||||||
|
|
||||||
|
// Whitelist all operators on a single column
|
||||||
|
age: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`?filter.name=$eq:Milo` is equivalent with `?filter.name=Milo`
|
`?filter.name=$eq:Milo` is equivalent with `?filter.name=Milo`
|
||||||
|
|
||||||
`?filter.age=$btw:4,6` where column `age` is between `4` and `6`
|
`?filter.age=$btw:4,6` where column `age` is between `4` and `6`
|
||||||
@ -398,7 +414,7 @@ Filter operators must be whitelisted per column in `PaginateConfig`.
|
|||||||
|
|
||||||
## Multi Filters
|
## Multi Filters
|
||||||
|
|
||||||
Multi filters are filters that can be applied to a single column with a comparator. As for single filters, multi filters must be whitelisted per column in `PaginateConfig`.
|
Multi filters are filters that can be applied to a single column with a comparator.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ export function getFilterTokens(raw?: string): FilterToken | null {
|
|||||||
|
|
||||||
export function parseFilter(
|
export function parseFilter(
|
||||||
query: PaginateQuery,
|
query: PaginateQuery,
|
||||||
filterableColumns?: { [column: string]: (FilterOperator | FilterSuffix)[] }
|
filterableColumns?: { [column: string]: (FilterOperator | FilterSuffix)[] | true }
|
||||||
): ColumnsFilters {
|
): ColumnsFilters {
|
||||||
const filter: ColumnsFilters = {}
|
const filter: ColumnsFilters = {}
|
||||||
if (!filterableColumns || !query.filter) {
|
if (!filterableColumns || !query.filter) {
|
||||||
@ -242,20 +242,28 @@ export function parseFilter(
|
|||||||
const statements = !Array.isArray(input) ? [input] : input
|
const statements = !Array.isArray(input) ? [input] : input
|
||||||
for (const raw of statements) {
|
for (const raw of statements) {
|
||||||
const token = getFilterTokens(raw)
|
const token = getFilterTokens(raw)
|
||||||
|
if (!token) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (allowedOperators === true) {
|
||||||
|
if (token.operator && !isOperator(token.operator)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (token.suffix && !isSuffix(token.suffix)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (
|
if (
|
||||||
!token ||
|
token.operator &&
|
||||||
!(
|
token.operator !== FilterOperator.EQ &&
|
||||||
allowedOperators.includes(token.operator) ||
|
!allowedOperators.includes(token.operator)
|
||||||
(token.suffix === FilterSuffix.NOT &&
|
|
||||||
allowedOperators.includes(token.suffix) &&
|
|
||||||
token.operator === FilterOperator.EQ) ||
|
|
||||||
(token.suffix &&
|
|
||||||
allowedOperators.includes(token.suffix) &&
|
|
||||||
allowedOperators.includes(token.operator))
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (token.suffix && !allowedOperators.includes(token.suffix)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const params: (typeof filter)[0][0] = {
|
const params: (typeof filter)[0][0] = {
|
||||||
comparator: token.comparator,
|
comparator: token.comparator,
|
||||||
@ -297,7 +305,7 @@ export function parseFilter(
|
|||||||
export function addFilter<T>(
|
export function addFilter<T>(
|
||||||
qb: SelectQueryBuilder<T>,
|
qb: SelectQueryBuilder<T>,
|
||||||
query: PaginateQuery,
|
query: PaginateQuery,
|
||||||
filterableColumns?: { [column: string]: (FilterOperator | FilterSuffix)[] }
|
filterableColumns?: { [column: string]: (FilterOperator | FilterSuffix)[] | true }
|
||||||
): SelectQueryBuilder<T> {
|
): SelectQueryBuilder<T> {
|
||||||
const filter = parseFilter(query, filterableColumns)
|
const filter = parseFilter(query, filterableColumns)
|
||||||
return qb.andWhere(
|
return qb.andWhere(
|
||||||
|
@ -2155,4 +2155,41 @@ describe('paginate', () => {
|
|||||||
expect(result.data[0].home).toBeDefined()
|
expect(result.data[0].home).toBeDefined()
|
||||||
expect(result.data[0].home.pillows).toStrictEqual(cat.home.pillows)
|
expect(result.data[0].home.pillows).toStrictEqual(cat.home.pillows)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should allow all filters on a field when passing boolean', async () => {
|
||||||
|
const config: PaginateConfig<CatEntity> = {
|
||||||
|
sortableColumns: ['id'],
|
||||||
|
filterableColumns: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const query: PaginateQuery = {
|
||||||
|
path: '',
|
||||||
|
filter: {
|
||||||
|
id: '$not:$in:1,2,5',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||||
|
|
||||||
|
expect(result.data).toStrictEqual([cats[2], cats[3]])
|
||||||
|
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.id=$not:$in:1,2,5')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should ignore all filters on a field when not passing anything', async () => {
|
||||||
|
const config: PaginateConfig<CatEntity> = {
|
||||||
|
sortableColumns: ['id'],
|
||||||
|
}
|
||||||
|
const query: PaginateQuery = {
|
||||||
|
path: '',
|
||||||
|
filter: {
|
||||||
|
id: '$not:$in:1,2,5',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||||
|
|
||||||
|
expect(result.data).toStrictEqual([cats[0], cats[1], cats[2], cats[3], cats[4]])
|
||||||
|
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.id=$not:$in:1,2,5')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -66,7 +66,7 @@ export interface PaginateConfig<T> {
|
|||||||
defaultLimit?: number
|
defaultLimit?: number
|
||||||
where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]
|
where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]
|
||||||
filterableColumns?: {
|
filterableColumns?: {
|
||||||
[key in Column<T> | string]?: (FilterOperator | FilterSuffix)[]
|
[key in Column<T> | string]?: (FilterOperator | FilterSuffix)[] | true
|
||||||
}
|
}
|
||||||
loadEagerRelations?: boolean
|
loadEagerRelations?: boolean
|
||||||
withDeleted?: boolean
|
withDeleted?: boolean
|
||||||
|
Loading…
Reference in New Issue
Block a user