feat: Add config options to ignore select
and searchBy
query parameters (#790)
This commit is contained in:
parent
040e8c1a68
commit
3945809ef8
17
README.md
17
README.md
@ -280,6 +280,22 @@ const paginateConfig: PaginateConfig<CatEntity> {
|
||||
* Description: Overrides the origin of absolute resource links if set.
|
||||
*/
|
||||
origin: 'http://cats.example',
|
||||
|
||||
/**
|
||||
* Required: false
|
||||
* Type: boolean
|
||||
* Default: false
|
||||
* Description: Prevent `searchBy` query param from limiting search scope further. Search will depend upon `searchableColumns` config option only
|
||||
*/
|
||||
ignoreSearchByInQueryParam: true,
|
||||
|
||||
/**
|
||||
* Required: false
|
||||
* Type: boolean
|
||||
* Default: false
|
||||
* Description: Prevent `select` query param from limiting selection further. Partial selection will depend upon `select` config option only
|
||||
*/
|
||||
ignoreSelectInQueryParam: true,
|
||||
}
|
||||
```
|
||||
|
||||
@ -466,7 +482,6 @@ You can use two default decorators @ApiOkResponsePaginated and @ApiPagination to
|
||||
|
||||
`@ApiPaginationQuery` is for query params
|
||||
|
||||
|
||||
```typescript
|
||||
@Get()
|
||||
@ApiOkPaginatedResponse(
|
||||
|
@ -2512,6 +2512,110 @@ describe('paginate', () => {
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.id=$not:$in:1,2,5')
|
||||
})
|
||||
|
||||
it('should use searchBy in query param when ignoreSearchByInQueryParam is not defined', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
searchableColumns: ['name', 'color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
search: 'Milo',
|
||||
searchBy: ['color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
expect(result.data.length).toEqual(0)
|
||||
expect(result.meta.searchBy).toStrictEqual(['color'])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&search=Milo&searchBy=color')
|
||||
})
|
||||
|
||||
it('should use searchBy in query param when ignoreSearchByInQueryParam is set to false', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
ignoreSearchByInQueryParam: false,
|
||||
searchableColumns: ['name', 'color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
search: 'Milo',
|
||||
searchBy: ['color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
expect(result.data.length).toEqual(0)
|
||||
expect(result.meta.searchBy).toStrictEqual(['color'])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&search=Milo&searchBy=color')
|
||||
})
|
||||
|
||||
it('should ignore searchBy in query param when ignoreSearchByInQueryParam is set to true', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
ignoreSearchByInQueryParam: true,
|
||||
searchableColumns: ['name', 'color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
search: 'Milo',
|
||||
searchBy: ['color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
expect(result.data.length).toEqual(1)
|
||||
expect(result.data).toStrictEqual([cats[0]])
|
||||
expect(result.meta.searchBy).toStrictEqual(['name', 'color'])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&search=Milo')
|
||||
})
|
||||
|
||||
it('should use select in query param when ignoreSelectInQueryParam is not defined', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
select: ['id', 'name', 'color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
select: ['id', 'color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
expect(result.data[0]).toEqual({ id: cats[0].id, color: cats[0].color })
|
||||
expect(result.meta.select).toStrictEqual(['id', 'color'])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&select=id,color')
|
||||
})
|
||||
|
||||
it('should use select in query param when ignoreSelectInQueryParam is set to false', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
ignoreSelectInQueryParam: false,
|
||||
select: ['id', 'name', 'color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
select: ['id', 'color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
expect(result.data[0]).toEqual({ id: cats[0].id, color: cats[0].color })
|
||||
expect(result.meta.select).toStrictEqual(['id', 'color'])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&select=id,color')
|
||||
})
|
||||
|
||||
it('should ignore select in query param when ignoreSelectInQueryParam is set to true', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
ignoreSelectInQueryParam: true,
|
||||
select: ['id', 'name', 'color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
select: ['id', 'color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
expect(result.data[0]).toEqual({ id: cats[0].id, color: cats[0].color, name: cats[0].name })
|
||||
expect(result.meta.select).toEqual(undefined)
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC')
|
||||
})
|
||||
|
||||
describe('should return result based on date column filter', () => {
|
||||
it('with $not and $null operators', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
|
@ -83,6 +83,8 @@ export interface PaginateConfig<T> {
|
||||
paginationType?: PaginationType
|
||||
relativePath?: boolean
|
||||
origin?: string
|
||||
ignoreSearchByInQueryParam?: boolean
|
||||
ignoreSelectInQueryParam?: boolean
|
||||
}
|
||||
|
||||
export const DEFAULT_MAX_LIMIT = 100
|
||||
@ -272,7 +274,9 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
// When we partial select the columns (main or relation) we must add the primary key column otherwise
|
||||
// typeorm will not be able to map the result.
|
||||
const selectParams =
|
||||
config.select && query.select ? config.select.filter((column) => query.select.includes(column)) : config.select
|
||||
config.select && query.select && !config.ignoreSelectInQueryParam
|
||||
? config.select.filter((column) => query.select.includes(column))
|
||||
: config.select
|
||||
if (selectParams?.length > 0 && includesAllPrimaryKeyColumns(queryBuilder, selectParams)) {
|
||||
const cols: string[] = selectParams.reduce((cols, currentCol) => {
|
||||
const columnProperties = getPropertiesByColumnName(currentCol)
|
||||
@ -293,7 +297,7 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
}
|
||||
|
||||
if (config.searchableColumns) {
|
||||
if (query.searchBy) {
|
||||
if (query.searchBy && !config.ignoreSearchByInQueryParam) {
|
||||
for (const column of query.searchBy) {
|
||||
if (isEntityKey(config.searchableColumns, column)) {
|
||||
searchBy.push(column)
|
||||
@ -362,7 +366,9 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
const searchQuery = query.search ? `&search=${query.search}` : ''
|
||||
|
||||
const searchByQuery =
|
||||
query.searchBy && searchBy.length ? searchBy.map((column) => `&searchBy=${column}`).join('') : ''
|
||||
query.searchBy && searchBy.length && !config.ignoreSearchByInQueryParam
|
||||
? searchBy.map((column) => `&searchBy=${column}`).join('')
|
||||
: ''
|
||||
|
||||
// Only expose select in meta data if query select differs from config select
|
||||
const isQuerySelected = selectParams?.length !== config.select?.length
|
||||
|
Loading…
Reference in New Issue
Block a user