from __future__ import annotations from typing import Any, Optional class AppError(Exception): status_code: int = 500 code: str = "internal_error" def __init__(self, message: str, details: Optional[Any] = None) -> None: super().__init__(message) self.details = details def to_dict(self) -> dict: out: dict = {"error": self.code, "message": str(self)} if self.details is not None: out["details"] = self.details return out class NotFoundError(AppError): status_code = 404 code = "not_found" def __init__(self, resource: str, identifier: Any = None) -> None: msg = f"{resource} not found" if identifier is not None: msg += f": {identifier}" super().__init__(msg) class AuthError(AppError): status_code = 401 code = "unauthorized" class ForbiddenError(AppError): status_code = 403 code = "forbidden" class ValidationError(AppError): status_code = 422 code = "validation_error" def __init__(self, field: str, message: str) -> None: self.field = field super().__init__(f"{field}: {message}") def to_dict(self) -> dict: d = super().to_dict() d["field"] = self.field return d class ConflictError(AppError): status_code = 409 code = "conflict"