Enhance pagination and ordering capabilities in API responses
- Updated pagination-response and read-write-controllers documentation to include `orderBy` and `orderType` parameters for sorting results. - Introduced new `order-clause` module to handle ordering logic, including validation for order types and columns. - Enhanced `PaginationQueryDto` to support ordering fields in API requests. - Updated various repository and service classes to implement ordering in database queries. - Added unit tests for new ordering functionality and ensured existing tests cover the updated behavior. - Refactored related DTOs to include user and code relations for better data representation in responses.
This commit is contained in:
@@ -5,6 +5,8 @@ import {
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { and, asc, count, eq, ilike, inArray, or, SQL } from 'drizzle-orm';
|
||||
import { toOrderClauses } from '../../common/http/response';
|
||||
import { attachAuditUsers } from '../../database/load-user-refs';
|
||||
import { DateTime } from '../../common/value-objects/date-time/date-time';
|
||||
import { Status } from '../../common/value-objects/status/status';
|
||||
import { DRIZZLE, type DrizzleDB } from '../../database/database.module';
|
||||
@@ -30,6 +32,22 @@ import type {
|
||||
UpdatePrivilegeInput,
|
||||
} from './privilege';
|
||||
|
||||
const PRIVILEGE_ORDER_COLUMNS = {
|
||||
id: privileges.id,
|
||||
name: privileges.name,
|
||||
code: privileges.code,
|
||||
status: privileges.status,
|
||||
createdAt: privileges.createdAt,
|
||||
updatedAt: privileges.updatedAt,
|
||||
};
|
||||
|
||||
const PRIVILEGE_KEY_ORDER_COLUMNS = {
|
||||
id: privilegeKeys.id,
|
||||
code: privilegeKeys.code,
|
||||
label: privilegeKeys.label,
|
||||
sortOrder: privilegeKeys.sortOrder,
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class PrivilegesRepository {
|
||||
constructor(@Inject(DRIZZLE) private readonly db: DrizzleDB) {}
|
||||
@@ -47,12 +65,16 @@ export class PrivilegesRepository {
|
||||
qb = this.extendListQuery(qb, filters);
|
||||
const rows = await qb
|
||||
.where(where)
|
||||
.orderBy(asc(privileges.code))
|
||||
.orderBy(
|
||||
...toOrderClauses(PRIVILEGE_ORDER_COLUMNS, filters, [
|
||||
{ column: 'code', type: 'ASC' },
|
||||
]),
|
||||
)
|
||||
.limit(filters.limit)
|
||||
.offset(filters.offset);
|
||||
|
||||
return {
|
||||
data: rows.map((row) => this.toDomain(row)),
|
||||
data: await this.hydrate(rows),
|
||||
total: Number(totalRow?.total ?? 0),
|
||||
};
|
||||
}
|
||||
@@ -75,7 +97,8 @@ export class PrivilegesRepository {
|
||||
return null;
|
||||
}
|
||||
const details = await this.loadDetails(id);
|
||||
return { ...this.toDomain(row), details };
|
||||
const [base] = await this.hydrate([row]);
|
||||
return { ...base, details };
|
||||
}
|
||||
|
||||
async findByCode(code: string): Promise<Privilege | null> {
|
||||
@@ -84,7 +107,7 @@ export class PrivilegesRepository {
|
||||
.from(privileges)
|
||||
.where(eq(privileges.code, code))
|
||||
.limit(1);
|
||||
return row ? this.toDomain(row) : null;
|
||||
return row ? this.hydrateOne(row) : null;
|
||||
}
|
||||
|
||||
async create(input: CreatePrivilegeInput): Promise<PrivilegeWithDetails> {
|
||||
@@ -117,7 +140,8 @@ export class PrivilegesRepository {
|
||||
}
|
||||
|
||||
const details = await this.loadDetails(row.id, tx);
|
||||
return { ...this.toDomain(row), details };
|
||||
const [base] = await this.hydrate([row]);
|
||||
return { ...base, details };
|
||||
});
|
||||
} catch (error) {
|
||||
this.rethrowUniqueViolation(error);
|
||||
@@ -190,7 +214,8 @@ export class PrivilegesRepository {
|
||||
}
|
||||
|
||||
const details = await this.loadDetails(id, tx);
|
||||
return { ...this.toDomain(row), details };
|
||||
const [base] = await this.hydrate([row]);
|
||||
return { ...base, details };
|
||||
});
|
||||
} catch (error) {
|
||||
this.rethrowUniqueViolation(error);
|
||||
@@ -215,7 +240,7 @@ export class PrivilegesRepository {
|
||||
if (!row) {
|
||||
throw new NotFoundException('Privilege not found');
|
||||
}
|
||||
return this.toDomain(row);
|
||||
return this.hydrateOne(row);
|
||||
}
|
||||
|
||||
async bulkUpdateStatus(
|
||||
@@ -297,7 +322,12 @@ export class PrivilegesRepository {
|
||||
.select()
|
||||
.from(privilegeKeys)
|
||||
.where(where)
|
||||
.orderBy(asc(privilegeKeys.sortOrder), asc(privilegeKeys.code))
|
||||
.orderBy(
|
||||
...toOrderClauses(PRIVILEGE_KEY_ORDER_COLUMNS, filters, [
|
||||
{ column: 'sortOrder', type: 'ASC' },
|
||||
{ column: 'code', type: 'ASC' },
|
||||
]),
|
||||
)
|
||||
.limit(filters.limit)
|
||||
.offset(filters.offset);
|
||||
|
||||
@@ -451,7 +481,7 @@ export class PrivilegesRepository {
|
||||
}));
|
||||
}
|
||||
|
||||
private toDomain(row: PrivilegeRow): Privilege {
|
||||
private toDomain(row: PrivilegeRow) {
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
@@ -464,6 +494,18 @@ export class PrivilegesRepository {
|
||||
};
|
||||
}
|
||||
|
||||
private async hydrate(rows: PrivilegeRow[]): Promise<Privilege[]> {
|
||||
return attachAuditUsers(
|
||||
this.db,
|
||||
rows.map((row) => this.toDomain(row)),
|
||||
);
|
||||
}
|
||||
|
||||
private async hydrateOne(row: PrivilegeRow): Promise<Privilege> {
|
||||
const [item] = await this.hydrate([row]);
|
||||
return item;
|
||||
}
|
||||
|
||||
private toKeyDomain(row: PrivilegeKeyRow): PrivilegeKey {
|
||||
return {
|
||||
id: row.id,
|
||||
|
||||
Reference in New Issue
Block a user