FactuGES_ERP/packages/rdx-ddd/src/errors/validation-error-collection.ts

92 lines
3.0 KiB
TypeScript
Raw Normal View History

2025-06-24 18:38:57 +00:00
/**
* 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);
*
*/
2025-08-25 17:42:56 +00:00
import { DomainError } from "./domain-error";
2025-10-04 15:40:41 +00:00
import { DomainValidationError } from "./domain-validation-error";
2025-08-25 17:42:56 +00:00
2025-06-24 18:38:57 +00:00
export interface ValidationErrorDetail {
2025-10-04 15:40:41 +00:00
/** 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;
2025-06-24 18:38:57 +00:00
}
2025-08-25 17:42:56 +00:00
/**
2025-10-04 15:40:41 +00:00
* 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);
2025-08-25 17:42:56 +00:00
*/
export class ValidationErrorCollection extends DomainError {
2025-10-04 15:40:41 +00:00
public readonly kind = "VALIDATION" as const;
public readonly code = "MULTIPLE_VALIDATION_ERRORS" as const;
2025-06-24 18:38:57 +00:00
public readonly details: ValidationErrorDetail[];
2025-10-04 15:40:41 +00:00
constructor(
details: ValidationErrorDetail[],
options?: ErrorOptions & { metadata?: Record<string, unknown> }
) {
super("Multiple validation errors", "MULTIPLE_VALIDATION_ERRORS", {
...options,
metadata: { ...(options?.metadata ?? {}), errors: details },
});
2025-06-24 18:38:57 +00:00
this.name = "ValidationErrorCollection";
this.details = details;
2025-10-04 15:40:41 +00:00
Object.setPrototypeOf(this, new.target.prototype);
2025-08-25 17:42:56 +00:00
Object.freeze(this);
2025-06-24 18:38:57 +00:00
}
2025-10-04 15:40:41 +00:00
/** 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,
})),
};
}
2025-06-24 18:38:57 +00:00
}
2025-08-25 17:42:56 +00:00
2025-10-04 15:40:41 +00:00
/** Type guard */
2025-08-25 17:42:56 +00:00
export const isValidationErrorCollection = (e: unknown): e is ValidationErrorCollection =>
e instanceof ValidationErrorCollection;