Files
office_translator/schemas/errors.py
2026-03-07 11:42:58 +01:00

280 lines
8.6 KiB
Python

"""
Error response models for API documentation
Story 3.6: Documentation OpenAPI (Swagger + ReDoc)
"""
from enum import Enum
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
class ErrorCode(str, Enum):
"""All error codes used in the API"""
# Client errors (4xx)
INVALID_FORMAT = "INVALID_FORMAT"
CORRUPTED_FILE = "CORRUPTED_FILE"
FILE_TOO_LARGE = "FILE_TOO_LARGE"
URL_DOWNLOAD_FAILED = "URL_DOWNLOAD_FAILED"
URL_UNREACHABLE = "URL_UNREACHABLE"
QUOTA_EXCEEDED = "QUOTA_EXCEEDED"
UNAUTHORIZED = "UNAUTHORIZED"
FORBIDDEN = "FORBIDDEN"
INVALID_CREDENTIALS = "INVALID_CREDENTIALS"
USER_NOT_FOUND = "USER_NOT_FOUND"
EMAIL_EXISTS = "EMAIL_EXISTS"
INVALID_EMAIL = "INVALID_EMAIL"
TOKEN_EXPIRED = "TOKEN_EXPIRED"
TOKEN_MISSING = "TOKEN_MISSING"
TOKEN_INVALID = "TOKEN_INVALID"
MISSING_API_KEY = "MISSING_API_KEY"
INVALID_API_KEY = "INVALID_API_KEY"
API_KEY_REVOKED = "API_KEY_REVOKED"
API_KEY_NOT_FOUND = "API_KEY_NOT_FOUND"
API_KEY_LIMIT_REACHED = "API_KEY_LIMIT_REACHED"
PRO_FEATURE_REQUIRED = "PRO_FEATURE_REQUIRED"
GLOSSARY_NOT_FOUND = "GLOSSARY_NOT_FOUND"
PROMPT_NOT_FOUND = "PROMPT_NOT_FOUND"
INVALID_WEBHOOK_URL = "INVALID_WEBHOOK_URL"
FILE_EXPIRED = "FILE_EXPIRED"
NOT_READY = "NOT_READY"
NOT_FOUND = "NOT_FOUND"
INVALID_REQUEST = "INVALID_REQUEST"
INVALID_JOB_ID = "INVALID_JOB_ID"
ACCESS_DENIED = "ACCESS_DENIED"
# Provider errors (5xx but not 500)
PROVIDER_UNAVAILABLE = "PROVIDER_UNAVAILABLE"
PROVIDER_RATE_LIMITED = "PROVIDER_RATE_LIMITED"
ALL_PROVIDERS_FAILED = "ALL_PROVIDERS_FAILED"
WEBHOOK_FAILED = "WEBHOOK_FAILED"
# System errors (5xx)
INTERNAL_ERROR = "INTERNAL_ERROR"
AUTH_HASHING_UNAVAILABLE = "AUTH_HASHING_UNAVAILABLE"
class ErrorResponse(BaseModel):
"""Standard error response format"""
error: ErrorCode = Field(
...,
description="Code d'erreur standardisé",
example="INVALID_FORMAT"
)
message: str = Field(
...,
description="Message d'erreur lisible en français",
example="Format de fichier non supporté. Formats acceptés: .xlsx, .docx, .pptx"
)
details: Optional[Dict[str, Any]] = Field(
None,
description="Détails supplémentaires sur l'erreur"
)
class Config:
json_schema_extra = {
"example": {
"error": "INVALID_FORMAT",
"message": "Format PDF non supporté. Formats acceptés: .xlsx, .docx, .pptx",
"details": {
"accepted_formats": [".xlsx", ".docx", ".pptx"],
"detected_format": ".pdf"
}
}
}
# Pre-defined error examples for OpenAPI documentation
ERROR_EXAMPLES = {
"INVALID_FORMAT": {
"summary": "Format de fichier non supporté",
"value": {
"error": "INVALID_FORMAT",
"message": "Format PDF non supporté. Formats acceptés: .xlsx, .docx, .pptx",
"details": {
"accepted_formats": [".xlsx", ".docx", ".pptx"],
"detected_format": ".pdf"
}
}
},
"CORRUPTED_FILE": {
"summary": "Fichier corrompu",
"value": {
"error": "CORRUPTED_FILE",
"message": "Le fichier n'est pas un document Office valide ou est corrompu.",
"details": {
"reason": "Invalid magic bytes"
}
}
},
"FILE_TOO_LARGE": {
"summary": "Fichier trop volumineux",
"value": {
"error": "FILE_TOO_LARGE",
"message": "Le fichier dépasse la limite de 50 MB.",
"details": {
"max_size_mb": 50,
"actual_size_mb": 65.3
}
}
},
"QUOTA_EXCEEDED": {
"summary": "Limite quotidienne atteinte",
"value": {
"error": "QUOTA_EXCEEDED",
"message": "Limite quotidienne de 5 traductions atteinte. Réessayez après minuit UTC.",
"details": {
"current_usage": 5,
"limit": 5,
"tier": "free",
"reset_at": "2024-01-16T00:00:00Z"
}
}
},
"UNAUTHORIZED": {
"summary": "Authentification requise",
"value": {
"error": "UNAUTHORIZED",
"message": "Authentification requise.",
"details": None
}
},
"FORBIDDEN": {
"summary": "Accès interdit",
"value": {
"error": "FORBIDDEN",
"message": "Vous n'avez pas accès à cette ressource.",
"details": None
}
},
"INVALID_CREDENTIALS": {
"summary": "Identifiants invalides",
"value": {
"error": "INVALID_CREDENTIALS",
"message": "Email ou mot de passe incorrect.",
"details": None
}
},
"EMAIL_EXISTS": {
"summary": "Email déjà utilisé",
"value": {
"error": "EMAIL_EXISTS",
"message": "Un compte existe déjà avec cette adresse email.",
"details": None
}
},
"TOKEN_EXPIRED": {
"summary": "Token expiré",
"value": {
"error": "TOKEN_EXPIRED",
"message": "Token invalide ou expiré.",
"details": None
}
},
"PRO_FEATURE_REQUIRED": {
"summary": "Fonctionnalité Pro requise",
"value": {
"error": "PRO_FEATURE_REQUIRED",
"message": "Cette fonctionnalité nécessite un abonnement Pro.",
"details": {
"feature": "llm_translation",
"current_tier": "free",
"required_tier": "pro"
}
}
},
"API_KEY_NOT_FOUND": {
"summary": "Clé API non trouvée",
"value": {
"error": "API_KEY_NOT_FOUND",
"message": "Clé API non trouvée, n'appartient pas à l'utilisateur ou déjà révoquée.",
"details": None
}
},
"FILE_EXPIRED": {
"summary": "Fichier expiré",
"value": {
"error": "FILE_EXPIRED",
"message": "Le fichier traduit n'est plus disponible ou a expiré.",
"details": {
"job_id": "tr_abc123",
"status": "not_found"
}
}
},
"NOT_READY": {
"summary": "Traduction en cours",
"value": {
"error": "NOT_READY",
"message": "La traduction est encore en cours.",
"details": {
"job_id": "tr_abc123",
"status": "processing",
"progress_percent": 45
}
}
},
"NOT_FOUND": {
"summary": "Ressource non trouvée",
"value": {
"error": "NOT_FOUND",
"message": "Ressource non trouvée.",
"details": None
}
},
"INTERNAL_ERROR": {
"summary": "Erreur interne",
"value": {
"error": "INTERNAL_ERROR",
"message": "Une erreur interne est survenue. Veuillez réessayer.",
"details": None
}
},
"INVALID_WEBHOOK_URL": {
"summary": "URL webhook invalide",
"value": {
"error": "INVALID_WEBHOOK_URL",
"message": "L'URL du webhook doit être une URL HTTP/HTTPS valide.",
"details": {
"field": "webhook_url",
"allowed_schemes": ["http", "https"],
"hint": "L'URL doit commencer par http:// ou https://"
}
}
},
"WEBHOOK_LOCALHOST_BLOCKED": {
"summary": "Localhost non autorisé",
"value": {
"error": "INVALID_WEBHOOK_URL",
"message": "Les URLs localhost ne sont pas autorisées.",
"details": {
"field": "webhook_url",
"reason": "localhost_blocked"
}
}
},
"WEBHOOK_PRIVATE_IP_BLOCKED": {
"summary": "IP privée non autorisée",
"value": {
"error": "INVALID_WEBHOOK_URL",
"message": "Les adresses IP privées ne sont pas autorisées.",
"details": {
"field": "webhook_url",
"reason": "private_ip_blocked"
}
}
},
"WEBHOOK_CREDENTIALS_IN_URL": {
"summary": "Credentials dans l'URL",
"value": {
"error": "INVALID_WEBHOOK_URL",
"message": "L'URL ne doit pas contenir d'identifiants (credentials).",
"details": {
"field": "webhook_url",
"reason": "credentials_in_url"
}
}
}
}