fix: add check on primary key columns to avoid select exception (#507)

This commit is contained in:
xMase 2023-03-01 12:23:17 +01:00 committed by GitHub
parent 6193ea28df
commit c2c6eaf44e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 6 deletions

View File

@ -194,6 +194,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
* Type: TypeORM partial selection
* Default: None
* https://typeorm.io/select-query-builder#partial-selection
* Note: You must include the primary key in the selection.
*/
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
```typescript
import { Entity, OneToMany } from 'typeorm';
import { Entity, OneToMany } from 'typeorm'
@Entity()
export class CatEntity {
@ -295,7 +296,7 @@ class CatService {
public findAll(query: PaginateQuery): Promise<Paginated<CatEntity>> {
return paginate(query, this.catsRepository, {
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
})
}
}

View File

@ -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 {
if (!qb || !propertyPath) {
return false

View File

@ -1871,6 +1871,20 @@ describe('paginate', () => {
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 () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],

View File

@ -23,6 +23,8 @@ import {
positiveNumberOrDefault,
RelationColumn,
SortBy,
hasColumnWithPropertyPath,
includesAllPrimaryKeyColumns,
} from './helper'
import { FilterOperator, FilterSuffix } from './operator'
import { addFilter } from './filter'
@ -208,15 +210,18 @@ 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 TODO: write it in the docs
// typeorm will not be able to map the result.
const selectParams = config.select || query.select
if (selectParams?.length > 0) {
if (selectParams?.length > 0 && includesAllPrimaryKeyColumns(queryBuilder, selectParams)) {
const cols: string[] = selectParams.reduce((cols, currentCol) => {
if (query.select?.includes(currentCol) ?? true) {
const columnProperties = getPropertiesByColumnName(currentCol)
const isRelation = checkIsRelation(queryBuilder, columnProperties.propertyPath)
// here we can avoid to manually fix and add the query of virtual columns
cols.push(fixColumnAlias(columnProperties, queryBuilder.alias, isRelation))
const { isVirtualProperty } = extractVirtualProperty(queryBuilder, columnProperties)
if (hasColumnWithPropertyPath(queryBuilder, columnProperties) || isVirtualProperty) {
// here we can avoid to manually fix and add the query of virtual columns
cols.push(fixColumnAlias(columnProperties, queryBuilder.alias, isRelation))
}
}
return cols
}, [])