92 lines
3.0 KiB
TypeScript
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;
|