fix: add check on primary key columns to avoid select exception (#507)
This commit is contained in:
parent
6193ea28df
commit
c2c6eaf44e
@ -194,6 +194,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
|
|||||||
* Type: TypeORM partial selection
|
* Type: TypeORM partial selection
|
||||||
* Default: None
|
* Default: None
|
||||||
* https://typeorm.io/select-query-builder#partial-selection
|
* https://typeorm.io/select-query-builder#partial-selection
|
||||||
|
* Note: You must include the primary key in the selection.
|
||||||
*/
|
*/
|
||||||
select: ['name', 'color'],
|
select: ['name', 'color'],
|
||||||
|
|
||||||
@ -266,7 +267,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
|
|||||||
Eager loading should work with typeorm's eager property out the box. Like so
|
Eager loading should work with typeorm's eager property out the box. Like so
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Entity, OneToMany } from 'typeorm';
|
import { Entity, OneToMany } from 'typeorm'
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class CatEntity {
|
export class CatEntity {
|
||||||
@ -295,7 +296,7 @@ class CatService {
|
|||||||
public findAll(query: PaginateQuery): Promise<Paginated<CatEntity>> {
|
public findAll(query: PaginateQuery): Promise<Paginated<CatEntity>> {
|
||||||
return paginate(query, this.catsRepository, {
|
return paginate(query, this.catsRepository, {
|
||||||
sortableColumns: ['id', 'name', 'color', 'age'],
|
sortableColumns: ['id', 'name', 'color', 'age'],
|
||||||
loadEagerRelations: true // set this property as true to enable the eager loading
|
loadEagerRelations: true, // set this property as true to enable the eager loading
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,25 @@ export function extractVirtualProperty(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function includesAllPrimaryKeyColumns(qb: SelectQueryBuilder<unknown>, propertyPath: string[]): boolean {
|
||||||
|
if (!qb || !propertyPath) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return qb.expressionMap.mainAlias?.metadata?.primaryColumns
|
||||||
|
.map((column) => column.propertyPath)
|
||||||
|
.every((column) => propertyPath.includes(column))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasColumnWithPropertyPath(
|
||||||
|
qb: SelectQueryBuilder<unknown>,
|
||||||
|
columnProperties: ColumnProperties
|
||||||
|
): boolean {
|
||||||
|
if (!qb || !columnProperties) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !!qb.expressionMap.mainAlias?.metadata?.hasColumnWithPropertyPath(columnProperties.propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
export function checkIsRelation(qb: SelectQueryBuilder<unknown>, propertyPath: string): boolean {
|
export function checkIsRelation(qb: SelectQueryBuilder<unknown>, propertyPath: string): boolean {
|
||||||
if (!qb || !propertyPath) {
|
if (!qb || !propertyPath) {
|
||||||
return false
|
return false
|
||||||
|
@ -1871,6 +1871,20 @@ describe('paginate', () => {
|
|||||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=countCat:ASC&filter.countCat=$gt:0')
|
expect(result.links.current).toBe('?page=1&limit=20&sortBy=countCat:ASC&filter.countCat=$gt:0')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should return all columns if select doesn't contain all primary columns", async () => {
|
||||||
|
const config: PaginateConfig<CatEntity> = {
|
||||||
|
sortableColumns: ['id', 'name'],
|
||||||
|
select: ['name'],
|
||||||
|
}
|
||||||
|
const query: PaginateQuery = {
|
||||||
|
path: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||||
|
|
||||||
|
expect(result.data).toStrictEqual(cats)
|
||||||
|
})
|
||||||
|
|
||||||
it('should return all items even if deleted', async () => {
|
it('should return all items even if deleted', async () => {
|
||||||
const config: PaginateConfig<CatEntity> = {
|
const config: PaginateConfig<CatEntity> = {
|
||||||
sortableColumns: ['id'],
|
sortableColumns: ['id'],
|
||||||
|
@ -23,6 +23,8 @@ import {
|
|||||||
positiveNumberOrDefault,
|
positiveNumberOrDefault,
|
||||||
RelationColumn,
|
RelationColumn,
|
||||||
SortBy,
|
SortBy,
|
||||||
|
hasColumnWithPropertyPath,
|
||||||
|
includesAllPrimaryKeyColumns,
|
||||||
} from './helper'
|
} from './helper'
|
||||||
import { FilterOperator, FilterSuffix } from './operator'
|
import { FilterOperator, FilterSuffix } from './operator'
|
||||||
import { addFilter } from './filter'
|
import { addFilter } from './filter'
|
||||||
@ -208,16 +210,19 @@ export async function paginate<T extends ObjectLiteral>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When we partial select the columns (main or relation) we must add the primary key column otherwise
|
// 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 TODO: write it in the docs
|
// typeorm will not be able to map the result.
|
||||||
const selectParams = config.select || query.select
|
const selectParams = config.select || query.select
|
||||||
if (selectParams?.length > 0) {
|
if (selectParams?.length > 0 && includesAllPrimaryKeyColumns(queryBuilder, selectParams)) {
|
||||||
const cols: string[] = selectParams.reduce((cols, currentCol) => {
|
const cols: string[] = selectParams.reduce((cols, currentCol) => {
|
||||||
if (query.select?.includes(currentCol) ?? true) {
|
if (query.select?.includes(currentCol) ?? true) {
|
||||||
const columnProperties = getPropertiesByColumnName(currentCol)
|
const columnProperties = getPropertiesByColumnName(currentCol)
|
||||||
const isRelation = checkIsRelation(queryBuilder, columnProperties.propertyPath)
|
const isRelation = checkIsRelation(queryBuilder, columnProperties.propertyPath)
|
||||||
|
const { isVirtualProperty } = extractVirtualProperty(queryBuilder, columnProperties)
|
||||||
|
if (hasColumnWithPropertyPath(queryBuilder, columnProperties) || isVirtualProperty) {
|
||||||
// here we can avoid to manually fix and add the query of virtual columns
|
// here we can avoid to manually fix and add the query of virtual columns
|
||||||
cols.push(fixColumnAlias(columnProperties, queryBuilder.alias, isRelation))
|
cols.push(fixColumnAlias(columnProperties, queryBuilder.alias, isRelation))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return cols
|
return cols
|
||||||
}, [])
|
}, [])
|
||||||
queryBuilder.select(cols)
|
queryBuilder.select(cols)
|
||||||
|
Loading…
Reference in New Issue
Block a user