Initial commit
Dieser Commit ist enthalten in:
356
v2_adminpanel/core/exceptions.py
Normale Datei
356
v2_adminpanel/core/exceptions.py
Normale Datei
@ -0,0 +1,356 @@
|
||||
import uuid
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class BaseApplicationException(Exception):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
code: str,
|
||||
status_code: int = 500,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
user_message: Optional[str] = None
|
||||
):
|
||||
super().__init__(message)
|
||||
self.message = message
|
||||
self.code = code
|
||||
self.status_code = status_code
|
||||
self.details = details or {}
|
||||
self.user_message = user_message or message
|
||||
self.timestamp = datetime.utcnow()
|
||||
self.request_id = str(uuid.uuid4())
|
||||
|
||||
def to_dict(self, include_details: bool = False) -> Dict[str, Any]:
|
||||
result = {
|
||||
'error': {
|
||||
'code': self.code,
|
||||
'message': self.user_message,
|
||||
'timestamp': self.timestamp.isoformat(),
|
||||
'request_id': self.request_id
|
||||
}
|
||||
}
|
||||
|
||||
if include_details and self.details:
|
||||
result['error']['details'] = self.details
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class ValidationException(BaseApplicationException):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
field: Optional[str] = None,
|
||||
value: Any = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
user_message: Optional[str] = None
|
||||
):
|
||||
details = details or {}
|
||||
if field:
|
||||
details['field'] = field
|
||||
if value is not None:
|
||||
details['value'] = str(value)
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
code='VALIDATION_ERROR',
|
||||
status_code=400,
|
||||
details=details,
|
||||
user_message=user_message or "Ungültige Eingabe"
|
||||
)
|
||||
|
||||
|
||||
class InputValidationError(ValidationException):
|
||||
def __init__(
|
||||
self,
|
||||
field: str,
|
||||
message: str,
|
||||
value: Any = None,
|
||||
expected_type: Optional[str] = None
|
||||
):
|
||||
details = {'expected_type': expected_type} if expected_type else None
|
||||
super().__init__(
|
||||
message=f"Invalid input for field '{field}': {message}",
|
||||
field=field,
|
||||
value=value,
|
||||
details=details,
|
||||
user_message=f"Ungültiger Wert für Feld '{field}'"
|
||||
)
|
||||
|
||||
|
||||
class BusinessRuleViolation(ValidationException):
|
||||
def __init__(
|
||||
self,
|
||||
rule: str,
|
||||
message: str,
|
||||
context: Optional[Dict[str, Any]] = None
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
details={'rule': rule, 'context': context or {}},
|
||||
user_message="Geschäftsregel verletzt"
|
||||
)
|
||||
|
||||
|
||||
class DataIntegrityError(ValidationException):
|
||||
def __init__(
|
||||
self,
|
||||
entity: str,
|
||||
constraint: str,
|
||||
message: str,
|
||||
details: Optional[Dict[str, Any]] = None
|
||||
):
|
||||
details = details or {}
|
||||
details.update({'entity': entity, 'constraint': constraint})
|
||||
super().__init__(
|
||||
message=message,
|
||||
details=details,
|
||||
user_message="Datenintegritätsfehler"
|
||||
)
|
||||
|
||||
|
||||
class AuthenticationException(BaseApplicationException):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
user_message: Optional[str] = None
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
code='AUTHENTICATION_ERROR',
|
||||
status_code=401,
|
||||
details=details,
|
||||
user_message=user_message or "Authentifizierung fehlgeschlagen"
|
||||
)
|
||||
|
||||
|
||||
class InvalidCredentialsError(AuthenticationException):
|
||||
def __init__(self, username: Optional[str] = None):
|
||||
details = {'username': username} if username else None
|
||||
super().__init__(
|
||||
message="Invalid username or password",
|
||||
details=details,
|
||||
user_message="Ungültiger Benutzername oder Passwort"
|
||||
)
|
||||
|
||||
|
||||
class SessionExpiredError(AuthenticationException):
|
||||
def __init__(self, session_id: Optional[str] = None):
|
||||
details = {'session_id': session_id} if session_id else None
|
||||
super().__init__(
|
||||
message="Session has expired",
|
||||
details=details,
|
||||
user_message="Ihre Sitzung ist abgelaufen"
|
||||
)
|
||||
|
||||
|
||||
class InsufficientPermissionsError(AuthenticationException):
|
||||
def __init__(
|
||||
self,
|
||||
required_permission: str,
|
||||
user_permissions: Optional[list] = None
|
||||
):
|
||||
super().__init__(
|
||||
message=f"User lacks required permission: {required_permission}",
|
||||
details={
|
||||
'required': required_permission,
|
||||
'user_permissions': user_permissions or []
|
||||
},
|
||||
user_message="Unzureichende Berechtigungen für diese Aktion"
|
||||
)
|
||||
self.status_code = 403
|
||||
|
||||
|
||||
class DatabaseException(BaseApplicationException):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
query: Optional[str] = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
user_message: Optional[str] = None
|
||||
):
|
||||
details = details or {}
|
||||
if query:
|
||||
details['query_hash'] = str(hash(query))
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
code='DATABASE_ERROR',
|
||||
status_code=500,
|
||||
details=details,
|
||||
user_message=user_message or "Datenbankfehler aufgetreten"
|
||||
)
|
||||
|
||||
|
||||
class ConnectionError(DatabaseException):
|
||||
def __init__(self, message: str, host: Optional[str] = None):
|
||||
details = {'host': host} if host else None
|
||||
super().__init__(
|
||||
message=message,
|
||||
details=details,
|
||||
user_message="Datenbankverbindung fehlgeschlagen"
|
||||
)
|
||||
|
||||
|
||||
class QueryError(DatabaseException):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
query: str,
|
||||
error_code: Optional[str] = None
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
query=query,
|
||||
details={'error_code': error_code} if error_code else None,
|
||||
user_message="Datenbankabfrage fehlgeschlagen"
|
||||
)
|
||||
|
||||
|
||||
class TransactionError(DatabaseException):
|
||||
def __init__(self, message: str, operation: str):
|
||||
super().__init__(
|
||||
message=message,
|
||||
details={'operation': operation},
|
||||
user_message="Transaktion fehlgeschlagen"
|
||||
)
|
||||
|
||||
|
||||
class ExternalServiceException(BaseApplicationException):
|
||||
def __init__(
|
||||
self,
|
||||
service_name: str,
|
||||
message: str,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
user_message: Optional[str] = None
|
||||
):
|
||||
details = details or {}
|
||||
details['service'] = service_name
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
code='EXTERNAL_SERVICE_ERROR',
|
||||
status_code=502,
|
||||
details=details,
|
||||
user_message=user_message or f"Fehler beim Zugriff auf {service_name}"
|
||||
)
|
||||
|
||||
|
||||
class APIError(ExternalServiceException):
|
||||
def __init__(
|
||||
self,
|
||||
service_name: str,
|
||||
endpoint: str,
|
||||
status_code: int,
|
||||
message: str
|
||||
):
|
||||
super().__init__(
|
||||
service_name=service_name,
|
||||
message=message,
|
||||
details={
|
||||
'endpoint': endpoint,
|
||||
'response_status': status_code
|
||||
},
|
||||
user_message=f"API-Fehler bei {service_name}"
|
||||
)
|
||||
|
||||
|
||||
class TimeoutError(ExternalServiceException):
|
||||
def __init__(
|
||||
self,
|
||||
service_name: str,
|
||||
timeout_seconds: int,
|
||||
operation: str
|
||||
):
|
||||
super().__init__(
|
||||
service_name=service_name,
|
||||
message=f"Timeout after {timeout_seconds}s while {operation}",
|
||||
details={
|
||||
'timeout_seconds': timeout_seconds,
|
||||
'operation': operation
|
||||
},
|
||||
user_message=f"Zeitüberschreitung bei {service_name}"
|
||||
)
|
||||
|
||||
|
||||
class ResourceException(BaseApplicationException):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
resource_type: str,
|
||||
resource_id: Any = None,
|
||||
details: Optional[Dict[str, Any]] = None,
|
||||
user_message: Optional[str] = None
|
||||
):
|
||||
details = details or {}
|
||||
details.update({
|
||||
'resource_type': resource_type,
|
||||
'resource_id': str(resource_id) if resource_id else None
|
||||
})
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
code='RESOURCE_ERROR',
|
||||
status_code=404,
|
||||
details=details,
|
||||
user_message=user_message or "Ressourcenfehler"
|
||||
)
|
||||
|
||||
|
||||
class ResourceNotFoundError(ResourceException):
|
||||
def __init__(
|
||||
self,
|
||||
resource_type: str,
|
||||
resource_id: Any = None,
|
||||
search_criteria: Optional[Dict[str, Any]] = None
|
||||
):
|
||||
details = {'search_criteria': search_criteria} if search_criteria else None
|
||||
super().__init__(
|
||||
message=f"{resource_type} not found",
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
details=details,
|
||||
user_message=f"{resource_type} nicht gefunden"
|
||||
)
|
||||
self.status_code = 404
|
||||
|
||||
|
||||
class ResourceConflictError(ResourceException):
|
||||
def __init__(
|
||||
self,
|
||||
resource_type: str,
|
||||
resource_id: Any,
|
||||
conflict_reason: str
|
||||
):
|
||||
super().__init__(
|
||||
message=f"Conflict with {resource_type}: {conflict_reason}",
|
||||
resource_type=resource_type,
|
||||
resource_id=resource_id,
|
||||
details={'conflict_reason': conflict_reason},
|
||||
user_message=f"Konflikt mit {resource_type}"
|
||||
)
|
||||
self.status_code = 409
|
||||
|
||||
|
||||
class ResourceLimitExceeded(ResourceException):
|
||||
def __init__(
|
||||
self,
|
||||
resource_type: str,
|
||||
limit: int,
|
||||
current: int,
|
||||
requested: Optional[int] = None
|
||||
):
|
||||
details = {
|
||||
'limit': limit,
|
||||
'current': current,
|
||||
'requested': requested
|
||||
}
|
||||
super().__init__(
|
||||
message=f"{resource_type} limit exceeded: {current}/{limit}",
|
||||
resource_type=resource_type,
|
||||
details=details,
|
||||
user_message=f"Limit für {resource_type} überschritten"
|
||||
)
|
||||
self.status_code = 429
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren