# 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, Dict, Optional class LetzShopException(Exception): """Base exception class for all custom exceptions.""" def __init__( self, message: str, error_code: str, status_code: int = 400, details: Optional[Dict[str, Any]] = 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(LetzShopException): """Raised when request validation fails.""" def __init__( self, message: str, field: Optional[str] = None, details: Optional[Dict[str, Any]] = 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(LetzShopException): """Raised when authentication fails.""" def __init__( self, message: str = "Authentication failed", error_code: str = "AUTHENTICATION_FAILED", details: Optional[Dict[str, Any]] = None, ): super().__init__( message=message, error_code=error_code, status_code=401, details=details, ) class AuthorizationException(LetzShopException): """Raised when user lacks permission for an operation.""" def __init__( self, message: str = "Access denied", error_code: str = "AUTHORIZATION_FAILED", details: Optional[Dict[str, Any]] = None, ): super().__init__( message=message, error_code=error_code, status_code=403, details=details, ) class ResourceNotFoundException(LetzShopException): """Raised when a requested resource is not found.""" def __init__( self, resource_type: str, identifier: str, message: Optional[str] = None, error_code: Optional[str] = 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(LetzShopException): """Raised when a resource conflict occurs.""" def __init__( self, message: str, error_code: str = "RESOURCE_CONFLICT", details: Optional[Dict[str, Any]] = None, ): super().__init__( message=message, error_code=error_code, status_code=409, details=details, ) class BusinessLogicException(LetzShopException): """Raised when business logic rules are violated.""" def __init__( self, message: str, error_code: str, details: Optional[Dict[str, Any]] = None, ): super().__init__( message=message, error_code=error_code, status_code=400, details=details, ) class ExternalServiceException(LetzShopException): """Raised when an external service fails.""" def __init__( self, message: str, service_name: str, error_code: str = "EXTERNAL_SERVICE_ERROR", details: Optional[Dict[str, Any]] = 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(LetzShopException): """Raised when rate limit is exceeded.""" def __init__( self, message: str = "Rate limit exceeded", retry_after: Optional[int] = None, details: Optional[Dict[str, Any]] = 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(LetzShopException): """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 VendorNotFoundException, UserNotFoundException, etc. # are defined in their respective domain modules (vendor.py, admin.py, etc.) # to keep domain-specific logic separate from base exceptions.