FactuGES_ERP/packages/rdx-ddd/src/errors/validation-error-collection.ts
2025-10-04 17:40:41 +02:00

92 lines
3.0 KiB
TypeScript

/**
* ValidationErrorCollection
*
* Esta clase representa un error de validación que agrega múltiples detalles de errores
* en un formato estructurado. Es útil para manejar múltiples errores de validación
* en una sola respuesta, permitiendo a los clientes entender qué campos fallaron y por qué.
*
* Ejemplo de uso:
*
* const errors: ValidationErrorDetail[] = [
* { path: "lines[1].unitPrice.amount", message: "Amount must be a positive number" },
* { path: "lines[1].unitPrice.scale", message: "Scale must be a non-negative integer" },
* ];
* const validationError = new ValidationErrorCollection(errors);
*
*/
import { DomainError } from "./domain-error";
import { DomainValidationError } from "./domain-validation-error";
export interface ValidationErrorDetail {
/** Path del campo inválido, ej. "lines[1].unitPrice.amount" */
path?: string;
/** Mensaje legible del fallo */
message: string;
/** Valor inválido (no se debe exponer directamente en API pública si es sensible) */
value?: unknown;
}
/**
* Error de validación múltiple de dominio.
* Permite agrupar varios fallos de validación en una única instancia.
*
* Ejemplo:
* const errors: ValidationErrorDetail[] = [
* { path: "lines[1].unitPrice.amount", message: "Amount must be positive" },
* { path: "lines[1].unitPrice.scale", message: "Scale must be non-negative" },
* ];
* throw new ValidationErrorCollection(errors);
*/
export class ValidationErrorCollection extends DomainError {
public readonly kind = "VALIDATION" as const;
public readonly code = "MULTIPLE_VALIDATION_ERRORS" as const;
public readonly details: ValidationErrorDetail[];
constructor(
details: ValidationErrorDetail[],
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
super("Multiple validation errors", "MULTIPLE_VALIDATION_ERRORS", {
...options,
metadata: { ...(options?.metadata ?? {}), errors: details },
});
this.name = "ValidationErrorCollection";
this.details = details;
Object.setPrototypeOf(this, new.target.prototype);
Object.freeze(this);
}
/** Crear a partir de varios DomainValidationError */
static fromErrors(
errors: DomainValidationError[],
options?: ErrorOptions & { metadata?: Record<string, unknown> }
): ValidationErrorCollection {
const details: ValidationErrorDetail[] = errors.map((e) => ({
path: e.field,
message: e.detail,
value: (e as any).cause, // opcional: valor que provocó el error
}));
return new ValidationErrorCollection(details, options);
}
/** Serialización para Problem+JSON / logs */
override toJSON() {
const base = super.toJSON();
return {
...base,
kind: this.kind,
details: this.details.map((d) => ({
path: d.path,
message: d.message,
})),
};
}
}
/** Type guard */
export const isValidationErrorCollection = (e: unknown): e is ValidationErrorCollection =>
e instanceof ValidationErrorCollection;