Merge branch 'development' into feature/catalog-drivers
This commit is contained in:
@@ -8,7 +8,9 @@ from typing import Any, Dict
|
||||
from fastapi import Request, status, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from pydantic import ValidationError
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
|
||||
from .config import settings
|
||||
@@ -57,20 +59,69 @@ async def base_exception_handler(
|
||||
return response
|
||||
|
||||
|
||||
# Mapa de campos técnicos a nombres legibles en español
|
||||
_FIELD_LABELS: Dict[str, str] = {
|
||||
"broker_key": "Clave del Agente",
|
||||
"license": "Patente",
|
||||
"tax_id": "RFC",
|
||||
"personal_id": "CURP",
|
||||
"email": "Correo Electrónico",
|
||||
"phone": "Teléfono",
|
||||
"fax": "Fax",
|
||||
"contact": "Nombre de Contacto",
|
||||
"name": "Nombre / Razón Social",
|
||||
"address": "Dirección",
|
||||
"postal_code": "Código Postal",
|
||||
"city": "Ciudad",
|
||||
"state": "Estado",
|
||||
"country": "País",
|
||||
}
|
||||
|
||||
_FIELD_PATTERN_MESSAGES: Dict[str, str] = {
|
||||
"broker_key": "La Clave del Agente solo puede contener letras y números (máx. 5 caracteres).",
|
||||
"license": "La Patente debe ser un número entre 1 y 9999 (no puede ser 0 ni contener letras).",
|
||||
"tax_id": "El RFC no tiene el formato correcto. Ejemplo válido: XAXX010101000.",
|
||||
"personal_id": "La CURP no tiene el formato correcto. Debe tener 18 caracteres alfanuméricos.",
|
||||
"email": "El correo electrónico no tiene un formato válido. Ejemplo: usuario@dominio.com.",
|
||||
"phone": "El teléfono solo puede contener dígitos, espacios y los símbolos: +, -, (, ).",
|
||||
"contact": "El nombre de contacto contiene caracteres no permitidos. Use solo letras, números y puntuación básica.",
|
||||
}
|
||||
|
||||
|
||||
def _friendly_message(field_key: str, error_type: str) -> str:
|
||||
"""Devuelve un mensaje de error legible en español según el campo y tipo de error."""
|
||||
if error_type in ("string_pattern_mismatch", "value_error"):
|
||||
return _FIELD_PATTERN_MESSAGES.get(
|
||||
field_key,
|
||||
f"El campo '{_FIELD_LABELS.get(field_key, field_key)}' contiene un valor con formato inválido.",
|
||||
)
|
||||
if error_type == "string_too_long":
|
||||
return f"El campo '{_FIELD_LABELS.get(field_key, field_key)}' excede la longitud máxima permitida."
|
||||
if error_type == "string_too_short":
|
||||
return f"El campo '{_FIELD_LABELS.get(field_key, field_key)}' es demasiado corto."
|
||||
if error_type in ("missing", "value_error.missing"):
|
||||
return f"El campo '{_FIELD_LABELS.get(field_key, field_key)}' es obligatorio."
|
||||
return f"El campo '{_FIELD_LABELS.get(field_key, field_key)}' contiene un valor inválido."
|
||||
|
||||
|
||||
async def validation_exception_handler(
|
||||
request: Request,
|
||||
exc: RequestValidationError,
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Manejador para errores de validación de Pydantic/FastAPI
|
||||
Manejador para errores de validación de Pydantic/FastAPI.
|
||||
Devuelve mensajes legibles en español.
|
||||
"""
|
||||
errors = []
|
||||
for error in exc.errors():
|
||||
field = ".".join(str(loc) for loc in error["loc"] if loc != "body")
|
||||
loc_parts = [str(loc) for loc in error["loc"] if loc != "body"]
|
||||
field = ".".join(loc_parts)
|
||||
field_key = loc_parts[-1] if loc_parts else ""
|
||||
|
||||
errors.append(
|
||||
{
|
||||
"field": field,
|
||||
"message": error["msg"],
|
||||
"message": _friendly_message(field_key, error["type"]),
|
||||
"type": error["type"],
|
||||
}
|
||||
)
|
||||
@@ -80,11 +131,63 @@ async def validation_exception_handler(
|
||||
extra={"errors": errors},
|
||||
)
|
||||
|
||||
summary = (
|
||||
errors[0]["message"]
|
||||
if len(errors) == 1
|
||||
else f"Hay {len(errors)} errores de validación: " + " | ".join(e["message"] for e in errors)
|
||||
)
|
||||
|
||||
response = JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content={
|
||||
"error": "VALIDATION_ERROR",
|
||||
"message": "Error de validación en los datos recibidos",
|
||||
"message": summary,
|
||||
"status_code": status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
"errors": errors,
|
||||
},
|
||||
)
|
||||
for k, v in _cors_headers(request).items():
|
||||
response.headers[k] = v
|
||||
return response
|
||||
|
||||
|
||||
async def inner_validation_exception_handler(
|
||||
request: Request,
|
||||
exc: ValidationError,
|
||||
) -> JSONResponse:
|
||||
"""
|
||||
Manejador para errores de validación de Pydantic lanzados internamente (como en tenant_crud_routes).
|
||||
"""
|
||||
errors = []
|
||||
for error in exc.errors():
|
||||
loc_parts = [str(loc) for loc in error["loc"] if loc != "body"]
|
||||
field = ".".join(loc_parts)
|
||||
field_key = loc_parts[-1] if loc_parts else ""
|
||||
|
||||
errors.append(
|
||||
{
|
||||
"field": field,
|
||||
"message": _friendly_message(field_key, error["type"]),
|
||||
"type": error["type"],
|
||||
}
|
||||
)
|
||||
|
||||
logger.warning(
|
||||
f"Inner Validation Error en {request.url.path}",
|
||||
extra={"errors": errors},
|
||||
)
|
||||
|
||||
summary = (
|
||||
errors[0]["message"]
|
||||
if len(errors) == 1
|
||||
else f"Hay {len(errors)} errores de validación: " + " | ".join(e["message"] for e in errors)
|
||||
)
|
||||
|
||||
response = JSONResponse(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
content={
|
||||
"error": "VALIDATION_ERROR",
|
||||
"message": summary,
|
||||
"status_code": status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
"errors": errors,
|
||||
},
|
||||
@@ -221,6 +324,7 @@ def register_exception_handlers(app) -> None:
|
||||
app.add_exception_handler(BaseAPIException, base_exception_handler)
|
||||
app.add_exception_handler(HTTPException, http_exception_handler)
|
||||
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||
app.add_exception_handler(ValidationError, inner_validation_exception_handler)
|
||||
app.add_exception_handler(IntegrityError, integrity_error_handler)
|
||||
app.add_exception_handler(SQLAlchemyError, sqlalchemy_error_handler)
|
||||
app.add_exception_handler(Exception, general_exception_handler)
|
||||
|
||||
Reference in New Issue
Block a user