Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"""Custom exceptions for VoIdea application."""
|
||||
|
||||
|
||||
class AppError(Exception):
|
||||
"""Base application exception.
|
||||
|
||||
All business errors inherit from this class.
|
||||
Does not contain HTTP status codes (those are in API layer).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
code: str = "GENERAL_ERROR",
|
||||
details: dict | None = None,
|
||||
) -> None:
|
||||
self.message = message
|
||||
self.code = code
|
||||
self.details = details or {}
|
||||
super().__init__(message)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert exception to dictionary."""
|
||||
return {
|
||||
"error": self.code,
|
||||
"message": self.message,
|
||||
"details": self.details,
|
||||
}
|
||||
|
||||
|
||||
class NotFoundError(AppError):
|
||||
"""Resource not found error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Resource not found",
|
||||
code: str = "NOT_FOUND",
|
||||
resource: str | None = None,
|
||||
resource_id: str | None = None,
|
||||
) -> None:
|
||||
details = {}
|
||||
if resource:
|
||||
details["resource"] = resource
|
||||
if resource_id:
|
||||
details["resource_id"] = resource_id
|
||||
super().__init__(message, code, details)
|
||||
|
||||
|
||||
class PermissionError(AppError):
|
||||
"""Permission denied error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Permission denied",
|
||||
code: str = "PERMISSION_DENIED",
|
||||
) -> None:
|
||||
super().__init__(message, code)
|
||||
|
||||
|
||||
class ValidationError(AppError):
|
||||
"""Validation error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Validation error",
|
||||
code: str = "VALIDATION_ERROR",
|
||||
field: str | None = None,
|
||||
value: Any = None,
|
||||
) -> None:
|
||||
details = {}
|
||||
if field:
|
||||
details["field"] = field
|
||||
if value is not None:
|
||||
details["value"] = str(value)
|
||||
super().__init__(message, code, details)
|
||||
|
||||
|
||||
class AuthenticationError(AppError):
|
||||
"""Authentication error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Authentication failed",
|
||||
code: str = "AUTH_FAILED",
|
||||
) -> None:
|
||||
super().__init__(message, code)
|
||||
|
||||
|
||||
class QuotaExceededError(AppError):
|
||||
"""Quota exceeded error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Quota exceeded",
|
||||
code: str = "QUOTA_EXCEEDED",
|
||||
limit: int | None = None,
|
||||
used: int | None = None,
|
||||
) -> None:
|
||||
details = {}
|
||||
if limit is not None:
|
||||
details["limit"] = limit
|
||||
if used is not None:
|
||||
details["used"] = used
|
||||
super().__init__(message, code, details)
|
||||
|
||||
|
||||
class ExternalServiceError(AppError):
|
||||
"""External service error (AI, OAuth, etc.)."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "External service error",
|
||||
code: str = "EXTERNAL_SERVICE_ERROR",
|
||||
service: str | None = None,
|
||||
) -> None:
|
||||
details = {}
|
||||
if service:
|
||||
details["service"] = service
|
||||
super().__init__(message, code, details)
|
||||
|
||||
|
||||
class RateLimitError(AppError):
|
||||
"""Rate limit exceeded error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "Rate limit exceeded",
|
||||
code: str = "RATE_LIMIT_EXCEEDED",
|
||||
retry_after: int | None = None,
|
||||
) -> None:
|
||||
details = {}
|
||||
if retry_after:
|
||||
details["retry_after"] = retry_after
|
||||
super().__init__(message, code, details)
|
||||
|
||||
|
||||
# Import for type hints
|
||||
from typing import Any
|
||||
Reference in New Issue
Block a user