fix: clean up

This commit is contained in:
ppetzold 2023-03-14 22:10:23 +01:00
parent 2a628e89af
commit d7ccd914d4
6 changed files with 105 additions and 111 deletions

View File

@ -1,3 +1,2 @@
export * from './decorator'
export * from './paginate'
export { FilterOperator, FilterComparator } from './operator'

View File

@ -1,65 +0,0 @@
import { values } from 'lodash'
import {
Equal,
FindOperator,
In,
MoreThan,
MoreThanOrEqual,
IsNull,
LessThan,
LessThanOrEqual,
Between,
ILike,
Not,
} from 'typeorm'
export enum FilterOperator {
EQ = '$eq',
GT = '$gt',
GTE = '$gte',
IN = '$in',
NULL = '$null',
LT = '$lt',
LTE = '$lte',
BTW = '$btw',
ILIKE = '$ilike',
SW = '$sw',
}
export function isOperator(value: unknown): value is FilterOperator {
return values(FilterOperator).includes(value as any)
}
export enum FilterSuffix {
NOT = '$not',
}
export function isSuffix(value: unknown): value is FilterSuffix {
return values(FilterSuffix).includes(value as any)
}
export enum FilterComparator {
AND = '$and',
OR = '$or',
}
export function isComparator(value: unknown): value is FilterComparator {
return values(FilterComparator).includes(value as any)
}
export const OperatorSymbolToFunction = new Map<
FilterOperator | FilterSuffix,
(...args: any[]) => FindOperator<string>
>([
[FilterOperator.EQ, Equal],
[FilterOperator.GT, MoreThan],
[FilterOperator.GTE, MoreThanOrEqual],
[FilterOperator.IN, In],
[FilterOperator.NULL, IsNull],
[FilterOperator.LT, LessThan],
[FilterOperator.LTE, LessThanOrEqual],
[FilterOperator.BTW, Between],
[FilterOperator.ILIKE, ILike],
[FilterSuffix.NOT, Not],
[FilterOperator.SW, ILike],
])

View File

@ -1,6 +1,21 @@
import { Brackets, FindOperator, SelectQueryBuilder } from 'typeorm'
import { values } from 'lodash'
import {
Brackets,
Equal,
FindOperator,
In,
MoreThan,
MoreThanOrEqual,
IsNull,
LessThan,
LessThanOrEqual,
Between,
ILike,
Not,
SelectQueryBuilder,
} from 'typeorm'
import { WherePredicateOperator } from 'typeorm/query-builder/WhereClause'
import { PaginateQuery } from './decorator'
import { PaginateQuery } from '../decorator'
import {
checkIsEmbedded,
checkIsRelation,
@ -8,16 +23,57 @@ import {
fixColumnAlias,
getPropertiesByColumnName,
} from './helper'
import {
FilterComparator,
FilterOperator,
FilterSuffix,
isComparator,
isOperator,
isSuffix,
OperatorSymbolToFunction,
} from './operator'
import { PaginateConfig } from './paginate'
export enum FilterOperator {
EQ = '$eq',
GT = '$gt',
GTE = '$gte',
IN = '$in',
NULL = '$null',
LT = '$lt',
LTE = '$lte',
BTW = '$btw',
ILIKE = '$ilike',
SW = '$sw',
}
export function isOperator(value: unknown): value is FilterOperator {
return values(FilterOperator).includes(value as any)
}
export enum FilterSuffix {
NOT = '$not',
}
export function isSuffix(value: unknown): value is FilterSuffix {
return values(FilterSuffix).includes(value as any)
}
export enum FilterComparator {
AND = '$and',
OR = '$or',
}
export function isComparator(value: unknown): value is FilterComparator {
return values(FilterComparator).includes(value as any)
}
export const OperatorSymbolToFunction = new Map<
FilterOperator | FilterSuffix,
(...args: any[]) => FindOperator<string>
>([
[FilterOperator.EQ, Equal],
[FilterOperator.GT, MoreThan],
[FilterOperator.GTE, MoreThanOrEqual],
[FilterOperator.IN, In],
[FilterOperator.NULL, IsNull],
[FilterOperator.LT, LessThan],
[FilterOperator.LTE, LessThanOrEqual],
[FilterOperator.BTW, Between],
[FilterOperator.ILIKE, ILike],
[FilterSuffix.NOT, Not],
[FilterOperator.SW, ILike],
])
type Filter = { comparator: FilterComparator; findOperator: FindOperator<string> }
type ColumnsFilters = { [columnName: string]: Filter[] }
@ -168,7 +224,7 @@ export function getFilterTokens(raw?: string): FilterToken | null {
export function parseFilter<T>(
query: PaginateQuery,
filterableColumns?: PaginateConfig<T>['filterableColumns']
filterableColumns?: { [column: string]: (FilterOperator | FilterSuffix)[] }
): ColumnsFilters {
const filter: ColumnsFilters = {}
if (!filterableColumns || !query.filter) {
@ -237,7 +293,7 @@ export function parseFilter<T>(
export function addFilter<T>(
qb: SelectQueryBuilder<T>,
query: PaginateQuery,
filterableColumns?: PaginateConfig<T>['filterableColumns']
filterableColumns?: { [column: string]: (FilterOperator | FilterSuffix)[] }
): SelectQueryBuilder<T> {
const filter = parseFilter(query, filterableColumns)
return qb.andWhere(

View File

@ -34,6 +34,10 @@ export type RelationColumn<T> = Extract<
export type Order<T> = [Column<T>, 'ASC' | 'DESC']
export type SortBy<T> = Order<T>[]
export function isEntityKey<T>(entityColumns: Column<T>[], column: string): column is Column<T> {
return !!entityColumns.find((c) => c === column)
}
export const positiveNumberOrDefault = (value: number | undefined, defaultValue: number, minValue: 0 | 1 = 0) =>
value === undefined || value < minValue ? defaultValue : value

View File

@ -1,21 +1,21 @@
import { Repository, In, DataSource, TypeORMError } from 'typeorm'
import { Paginated, paginate, PaginateConfig, NO_PAGINATION } from './paginate'
import { PaginateQuery } from './decorator'
import { Paginated, paginate, PaginateConfig, NO_PAGINATION } from '.'
import { PaginateQuery } from '../decorator'
import { HttpException } from '@nestjs/common'
import { CatEntity } from './__tests__/cat.entity'
import { CatToyEntity } from './__tests__/cat-toy.entity'
import { CatHomeEntity } from './__tests__/cat-home.entity'
import { CatHomePillowEntity } from './__tests__/cat-home-pillow.entity'
import { CatEntity } from '../__tests__/cat.entity'
import { CatToyEntity } from '../__tests__/cat-toy.entity'
import { CatHomeEntity } from '../__tests__/cat-home.entity'
import { CatHomePillowEntity } from '../__tests__/cat-home-pillow.entity'
import { clone } from 'lodash'
import {
getFilterTokens,
FilterComparator,
FilterOperator,
FilterSuffix,
isOperator,
isSuffix,
OperatorSymbolToFunction,
} from './operator'
import { getFilterTokens } from './filter'
} from './filter'
describe('paginate', () => {
let dataSource: DataSource

View File

@ -7,7 +7,7 @@ import {
ObjectLiteral,
FindOptionsUtils,
} from 'typeorm'
import { PaginateQuery } from './decorator'
import { PaginateQuery } from '../decorator'
import { ServiceUnavailableException, Logger } from '@nestjs/common'
import { mapKeys } from 'lodash'
import { stringify } from 'querystring'
@ -25,12 +25,14 @@ import {
SortBy,
hasColumnWithPropertyPath,
includesAllPrimaryKeyColumns,
isEntityKey,
} from './helper'
import { FilterOperator, FilterSuffix } from './operator'
import { addFilter } from './filter'
import { addFilter, FilterOperator, FilterSuffix } from './filter'
const logger: Logger = new Logger('nestjs-paginate')
export { FilterOperator, FilterSuffix }
export class Paginated<T> {
data: T[]
meta: {
@ -121,10 +123,6 @@ export async function paginate<T extends ObjectLiteral>(
path = queryOrigin + queryPath
}
function isEntityKey(entityColumns: Column<T>[], column: string): column is Column<T> {
return !!entityColumns.find((c) => c === column)
}
if (config.sortableColumns.length < 1) {
logger.debug("Missing required 'sortableColumns' config.")
throw new ServiceUnavailableException()
@ -142,18 +140,6 @@ export async function paginate<T extends ObjectLiteral>(
sortBy.push(...(config.defaultSortBy || [[config.sortableColumns[0], 'ASC']]))
}
if (config.searchableColumns) {
if (query.searchBy) {
for (const column of query.searchBy) {
if (isEntityKey(config.searchableColumns, column)) {
searchBy.push(column)
}
}
} else {
searchBy.push(...config.searchableColumns)
}
}
let [items, totalItems]: [T[], number] = [[], 0]
const queryBuilder = repo instanceof Repository ? repo.createQueryBuilder('__root') : repo
@ -165,10 +151,12 @@ export async function paginate<T extends ObjectLiteral>(
}
if (isPaginated) {
// Switch from take and skip to limit and offset
// due to this problem https://github.com/typeorm/typeorm/issues/5670
// (anyway this creates more clean query without double distinct)
// queryBuilder.limit(limit).offset((page - 1) * limit)
// Allow user to choose between limit/offset and take/skip.
// However, using limit/offset can return unexpected results.
// For more information see:
// [#477](https://github.com/ppetzold/nestjs-paginate/issues/477)
// [#4742](https://github.com/typeorm/typeorm/issues/4742)
// [#5670](https://github.com/typeorm/typeorm/issues/5670)
if (paginationType === PaginationType.LIMIT_AND_OFFSET) {
queryBuilder.limit(limit).offset((page - 1) * limit)
} else {
@ -248,6 +236,18 @@ export async function paginate<T extends ObjectLiteral>(
queryBuilder.withDeleted()
}
if (config.searchableColumns) {
if (query.searchBy) {
for (const column of query.searchBy) {
if (isEntityKey(config.searchableColumns, column)) {
searchBy.push(column)
}
}
} else {
searchBy.push(...config.searchableColumns)
}
}
if (query.search && searchBy.length) {
queryBuilder.andWhere(
new Brackets((qb: SelectQueryBuilder<T>) => {