Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
213 lines
5.6 KiB
Python
213 lines
5.6 KiB
Python
# 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.
|