# app/exceptions/base.py """ Base exception classes for the application. This module provides classes and functions for: - Base exception class with consistent error formatting - Common exception types for different error categories - Standardized error response structure """ from typing import Any class OrionException(Exception): """Base exception class for all custom exceptions.""" def __init__( self, message: str, error_code: str, status_code: int = 400, details: dict[str, Any] | None = None, ): self.message = message self.error_code = error_code self.status_code = status_code self.details = details or {} super().__init__(self.message) def to_dict(self) -> dict[str, Any]: """Convert exception to dictionary for JSON response.""" result = { "error_code": self.error_code, "message": self.message, "status_code": self.status_code, } if self.details: result["details"] = self.details return result class ValidationException(OrionException): """Raised when request validation fails.""" def __init__( self, message: str, field: str | None = None, details: dict[str, Any] | None = None, ): validation_details = details or {} if field: validation_details["field"] = field super().__init__( message=message, error_code="VALIDATION_ERROR", status_code=422, details=validation_details, ) class AuthenticationException(OrionException): """Raised when authentication fails.""" def __init__( self, message: str = "Authentication failed", error_code: str = "AUTHENTICATION_FAILED", details: dict[str, Any] | None = None, ): super().__init__( message=message, error_code=error_code, status_code=401, details=details, ) class AuthorizationException(OrionException): """Raised when user lacks permission for an operation.""" def __init__( self, message: str = "Access denied", error_code: str = "AUTHORIZATION_FAILED", details: dict[str, Any] | None = None, ): super().__init__( message=message, error_code=error_code, status_code=403, details=details, ) class ResourceNotFoundException(OrionException): """Raised when a requested resource is not found.""" def __init__( self, resource_type: str, identifier: str, message: str | None = None, error_code: str | None = None, ): if not message: message = f"{resource_type} with identifier '{identifier}' not found" if not error_code: error_code = f"{resource_type.upper()}_NOT_FOUND" super().__init__( message=message, error_code=error_code, status_code=404, details={ "resource_type": resource_type, "identifier": identifier, }, ) class ConflictException(OrionException): """Raised when a resource conflict occurs.""" def __init__( self, message: str, error_code: str = "RESOURCE_CONFLICT", details: dict[str, Any] | None = None, ): super().__init__( message=message, error_code=error_code, status_code=409, details=details, ) class BusinessLogicException(OrionException): """Raised when business logic rules are violated.""" def __init__( self, message: str, error_code: str, details: dict[str, Any] | None = None, ): super().__init__( message=message, error_code=error_code, status_code=400, details=details, ) class ExternalServiceException(OrionException): """Raised when an external service fails.""" def __init__( self, message: str, service_name: str, error_code: str = "EXTERNAL_SERVICE_ERROR", details: dict[str, Any] | None = None, ): service_details = details or {} service_details["service_name"] = service_name super().__init__( message=message, error_code=error_code, status_code=502, details=service_details, ) class RateLimitException(OrionException): """Raised when rate limit is exceeded.""" def __init__( self, message: str = "Rate limit exceeded", retry_after: int | None = None, details: dict[str, Any] | None = None, ): rate_limit_details = details or {} if retry_after: rate_limit_details["retry_after"] = retry_after super().__init__( message=message, error_code="RATE_LIMIT_EXCEEDED", status_code=429, details=rate_limit_details, ) class ServiceUnavailableException(OrionException): """Raised when service is unavailable.""" def __init__(self, message: str = "Service temporarily unavailable"): super().__init__( message=message, error_code="SERVICE_UNAVAILABLE", status_code=503, ) # Note: Domain-specific exceptions like StoreNotFoundException, UserNotFoundException, etc. # are defined in their respective domain modules (store.py, admin.py, etc.) # to keep domain-specific logic separate from base exceptions.