feat: mejorar la búsqueda de auditoría al agregar soporte para filtros y ordenamiento
This commit is contained in:
parent
ce9f9029e3
commit
fb7d3f7043
@ -1,5 +1,5 @@
|
|||||||
import type { AuditValue } from "@repo/rdx-audit";
|
import type { AuditValue } from "@repo/rdx-audit";
|
||||||
import type { Criteria } from "@repo/rdx-criteria/server";
|
import type { Criteria, FiltersPrimitives } from "@repo/rdx-criteria/server";
|
||||||
import { Op, Sequelize } from "sequelize";
|
import { Op, Sequelize } from "sequelize";
|
||||||
|
|
||||||
import { AuditChangeModel, AuditEventModel } from "./sequelize";
|
import { AuditChangeModel, AuditEventModel } from "./sequelize";
|
||||||
@ -59,6 +59,7 @@ export interface IAuditReader {
|
|||||||
search(query: AuditSearchQuery, access: AuditReadAccess): Promise<AuditSearchResult>;
|
search(query: AuditSearchQuery, access: AuditReadAccess): Promise<AuditSearchResult>;
|
||||||
findById(id: string, access: AuditReadAccess): Promise<AuditDetail | null>;
|
findById(id: string, access: AuditReadAccess): Promise<AuditDetail | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIELD_MAP: Record<string, string> = {
|
const FIELD_MAP: Record<string, string> = {
|
||||||
occurredAt: "occurred_at",
|
occurredAt: "occurred_at",
|
||||||
kind: "kind",
|
kind: "kind",
|
||||||
@ -80,31 +81,10 @@ export class SequelizeAuditReader implements IAuditReader {
|
|||||||
async search(query: AuditSearchQuery, access: AuditReadAccess): Promise<AuditSearchResult> {
|
async search(query: AuditSearchQuery, access: AuditReadAccess): Promise<AuditSearchResult> {
|
||||||
const primitives = query.criteria.toPrimitives();
|
const primitives = query.criteria.toPrimitives();
|
||||||
const filters = primitives.filters ?? [];
|
const filters = primitives.filters ?? [];
|
||||||
|
this.assertSupportedFilters(filters);
|
||||||
|
|
||||||
for (const filter of filters) {
|
const orderBy = this.resolveOrderBy(primitives.orderBy);
|
||||||
if (!FIELD_MAP[filter.field]) throw new Error(`Unsupported audit filter: ${filter.field}`);
|
const where = this.buildWhere(access, filters, query.quickSearch);
|
||||||
}
|
|
||||||
|
|
||||||
const orderBy = primitives.orderBy ?? "occurredAt";
|
|
||||||
if (!SORTABLE.has(orderBy)) throw new Error(`Unsupported audit order: ${orderBy}`);
|
|
||||||
|
|
||||||
const where: Record<PropertyKey, unknown> = { ...this.accessWhere(access) };
|
|
||||||
|
|
||||||
for (const filter of filters) {
|
|
||||||
const column = FIELD_MAP[filter.field];
|
|
||||||
if (filter.operator === ">=" || filter.operator === "<") {
|
|
||||||
where[column] = {
|
|
||||||
...(typeof where[column] === "object" && where[column] !== null ? where[column] : {}),
|
|
||||||
[filter.operator === ">=" ? Op.gte : Op.lt]: filter.value,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
where[column] = filter.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query.quickSearch) {
|
|
||||||
where[Op.and] = [{ [Op.or]: this.quickSearch(query.quickSearch) }];
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageSize = primitives.pageSize ?? 50;
|
const pageSize = primitives.pageSize ?? 50;
|
||||||
const pageNumber = primitives.pageNumber ?? 0;
|
const pageNumber = primitives.pageNumber ?? 0;
|
||||||
@ -136,6 +116,51 @@ export class SequelizeAuditReader implements IAuditReader {
|
|||||||
pageSize,
|
pageSize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private assertSupportedFilters(filters: readonly FiltersPrimitives[]): void {
|
||||||
|
for (const filter of filters) {
|
||||||
|
if (!FIELD_MAP[filter.field]) {
|
||||||
|
throw new Error(`Unsupported audit filter: ${filter.field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveOrderBy(orderBy?: string): string {
|
||||||
|
const resolvedOrderBy = orderBy ?? "occurredAt";
|
||||||
|
if (!SORTABLE.has(resolvedOrderBy)) {
|
||||||
|
throw new Error(`Unsupported audit order: ${resolvedOrderBy}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedOrderBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildWhere(
|
||||||
|
access: AuditReadAccess,
|
||||||
|
filters: readonly FiltersPrimitives[],
|
||||||
|
quickSearch: string | null
|
||||||
|
): Record<PropertyKey, unknown> {
|
||||||
|
const where: Record<PropertyKey, unknown> = { ...this.accessWhere(access) };
|
||||||
|
|
||||||
|
for (const filter of filters) {
|
||||||
|
const column = FIELD_MAP[filter.field];
|
||||||
|
if (filter.operator === ">=" || filter.operator === "<") {
|
||||||
|
where[column] = {
|
||||||
|
...(typeof where[column] === "object" && where[column] !== null ? where[column] : {}),
|
||||||
|
[filter.operator === ">=" ? Op.gte : Op.lt]: filter.value,
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
where[column] = filter.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quickSearch) {
|
||||||
|
where[Op.and] = [{ [Op.or]: this.quickSearch(quickSearch) }];
|
||||||
|
}
|
||||||
|
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
|
||||||
async findById(id: string, access: AuditReadAccess): Promise<AuditDetail | null> {
|
async findById(id: string, access: AuditReadAccess): Promise<AuditDetail | null> {
|
||||||
const row = await AuditEventModel.findOne({ where: { id, ...this.accessWhere(access) } });
|
const row = await AuditEventModel.findOne({ where: { id, ...this.accessWhere(access) } });
|
||||||
if (!row) {
|
if (!row) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user