Initial commit
Dieser Commit ist enthalten in:
229
domain/value_objects/operation_result.py
Normale Datei
229
domain/value_objects/operation_result.py
Normale Datei
@ -0,0 +1,229 @@
|
||||
"""
|
||||
Operation Result Value Object - Standardisierte Ergebnisstruktur
|
||||
Backward-compatible Wrapper für konsistente Fehlerbehandlung
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Any, Dict, Union
|
||||
from datetime import datetime
|
||||
import traceback
|
||||
|
||||
|
||||
@dataclass
|
||||
class OperationResult:
|
||||
"""
|
||||
Standardisierte Ergebnisstruktur für alle Operationen.
|
||||
Kompatibel mit bestehenden boolean und dict returns.
|
||||
"""
|
||||
success: bool
|
||||
data: Optional[Any] = None
|
||||
error_message: Optional[str] = None
|
||||
error_code: Optional[str] = None
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
timestamp: Optional[datetime] = None
|
||||
legacy_result: Optional[Any] = None # Für backward compatibility
|
||||
|
||||
def __post_init__(self):
|
||||
if self.timestamp is None:
|
||||
self.timestamp = datetime.now()
|
||||
|
||||
@classmethod
|
||||
def success_result(cls, data: Any = None, metadata: Dict[str, Any] = None, legacy_result: Any = None):
|
||||
"""Erstellt ein Erfolgsergebnis"""
|
||||
return cls(
|
||||
success=True,
|
||||
data=data,
|
||||
metadata=metadata or {},
|
||||
legacy_result=legacy_result if legacy_result is not None else data
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def error_result(cls, message: str, code: str = None,
|
||||
metadata: Dict[str, Any] = None, legacy_result: Any = None):
|
||||
"""Erstellt ein Fehlerergebnis"""
|
||||
return cls(
|
||||
success=False,
|
||||
error_message=message,
|
||||
error_code=code,
|
||||
metadata=metadata or {},
|
||||
legacy_result=legacy_result if legacy_result is not None else False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_exception(cls, exception: Exception, code: str = None,
|
||||
metadata: Dict[str, Any] = None):
|
||||
"""Erstellt Fehlerergebnis aus Exception"""
|
||||
metadata = metadata or {}
|
||||
metadata.update({
|
||||
'exception_type': type(exception).__name__,
|
||||
'traceback': traceback.format_exc()
|
||||
})
|
||||
|
||||
return cls(
|
||||
success=False,
|
||||
error_message=str(exception),
|
||||
error_code=code or type(exception).__name__,
|
||||
metadata=metadata,
|
||||
legacy_result=False
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_legacy_boolean(cls, result: bool, success_data: Any = None, error_message: str = None):
|
||||
"""Konvertiert legacy boolean zu OperationResult"""
|
||||
if result:
|
||||
return cls.success_result(data=success_data, legacy_result=result)
|
||||
else:
|
||||
return cls.error_result(
|
||||
message=error_message or "Operation failed",
|
||||
legacy_result=result
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_legacy_dict(cls, result: Dict[str, Any]):
|
||||
"""Konvertiert legacy dict zu OperationResult"""
|
||||
success = result.get('success', False)
|
||||
|
||||
if success:
|
||||
return cls.success_result(
|
||||
data=result.get('data'),
|
||||
metadata=result.get('metadata', {}),
|
||||
legacy_result=result
|
||||
)
|
||||
else:
|
||||
return cls.error_result(
|
||||
message=result.get('error', 'Operation failed'),
|
||||
code=result.get('error_code'),
|
||||
metadata=result.get('metadata', {}),
|
||||
legacy_result=result
|
||||
)
|
||||
|
||||
def is_success(self) -> bool:
|
||||
"""Prüft ob Operation erfolgreich war"""
|
||||
return self.success
|
||||
|
||||
def is_error(self) -> bool:
|
||||
"""Prüft ob Operation fehlgeschlagen ist"""
|
||||
return not self.success
|
||||
|
||||
def get_legacy_result(self) -> Any:
|
||||
"""Gibt das ursprüngliche Result-Format zurück für backward compatibility"""
|
||||
return self.legacy_result
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Konvertiert zu Dictionary für API/JSON Serialisierung"""
|
||||
result = {
|
||||
'success': self.success,
|
||||
'timestamp': self.timestamp.isoformat() if self.timestamp else None
|
||||
}
|
||||
|
||||
if self.data is not None:
|
||||
result['data'] = self.data
|
||||
|
||||
if self.error_message:
|
||||
result['error'] = self.error_message
|
||||
|
||||
if self.error_code:
|
||||
result['error_code'] = self.error_code
|
||||
|
||||
if self.metadata:
|
||||
result['metadata'] = self.metadata
|
||||
|
||||
return result
|
||||
|
||||
def to_legacy_dict(self) -> Dict[str, Any]:
|
||||
"""Konvertiert zu legacy dict format"""
|
||||
return {
|
||||
'success': self.success,
|
||||
'data': self.data,
|
||||
'error': self.error_message,
|
||||
'error_code': self.error_code,
|
||||
'metadata': self.metadata
|
||||
}
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""Ermöglicht if result: syntax"""
|
||||
return self.success
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.success:
|
||||
return f"Success: {self.data}"
|
||||
else:
|
||||
return f"Error: {self.error_message} ({self.error_code})"
|
||||
|
||||
|
||||
class ResultWrapper:
|
||||
"""
|
||||
Utility-Klasse für backward-compatible Wrapping von bestehenden Methoden
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def wrap_boolean_method(method, *args, **kwargs) -> OperationResult:
|
||||
"""Wrapper für bestehende boolean-Methoden"""
|
||||
try:
|
||||
success = method(*args, **kwargs)
|
||||
return OperationResult.from_legacy_boolean(
|
||||
result=success,
|
||||
success_data=success,
|
||||
error_message="Operation failed" if not success else None
|
||||
)
|
||||
except Exception as e:
|
||||
return OperationResult.from_exception(e)
|
||||
|
||||
@staticmethod
|
||||
def wrap_dict_method(method, *args, **kwargs) -> OperationResult:
|
||||
"""Wrapper für bestehende dict-Methoden"""
|
||||
try:
|
||||
result = method(*args, **kwargs)
|
||||
if isinstance(result, dict):
|
||||
return OperationResult.from_legacy_dict(result)
|
||||
else:
|
||||
return OperationResult.success_result(data=result, legacy_result=result)
|
||||
except Exception as e:
|
||||
return OperationResult.from_exception(e)
|
||||
|
||||
@staticmethod
|
||||
def wrap_any_method(method, *args, **kwargs) -> OperationResult:
|
||||
"""Universal wrapper für beliebige Methoden"""
|
||||
try:
|
||||
result = method(*args, **kwargs)
|
||||
|
||||
if isinstance(result, bool):
|
||||
return OperationResult.from_legacy_boolean(result)
|
||||
elif isinstance(result, dict) and 'success' in result:
|
||||
return OperationResult.from_legacy_dict(result)
|
||||
elif result is None:
|
||||
return OperationResult.error_result("Method returned None", legacy_result=result)
|
||||
else:
|
||||
return OperationResult.success_result(data=result, legacy_result=result)
|
||||
|
||||
except Exception as e:
|
||||
return OperationResult.from_exception(e)
|
||||
|
||||
|
||||
# Error Codes für häufige Fehlertypen
|
||||
class CommonErrorCodes:
|
||||
"""Häufig verwendete Fehlercodes"""
|
||||
|
||||
# Instagram spezifisch
|
||||
CAPTCHA_REQUIRED = "CAPTCHA_REQUIRED"
|
||||
EMAIL_TIMEOUT = "EMAIL_TIMEOUT"
|
||||
SMS_NOT_IMPLEMENTED = "SMS_NOT_IMPLEMENTED"
|
||||
USERNAME_TAKEN = "USERNAME_TAKEN"
|
||||
SELECTOR_NOT_FOUND = "SELECTOR_NOT_FOUND"
|
||||
BIRTHDAY_SELECTOR_FAILED = "BIRTHDAY_SELECTOR_FAILED"
|
||||
|
||||
# Allgemein
|
||||
PROXY_ERROR = "PROXY_ERROR"
|
||||
RATE_LIMITED = "RATE_LIMITED"
|
||||
NETWORK_TIMEOUT = "NETWORK_TIMEOUT"
|
||||
BROWSER_ERROR = "BROWSER_ERROR"
|
||||
|
||||
# Fingerprint spezifisch
|
||||
FINGERPRINT_GENERATION_FAILED = "FINGERPRINT_GENERATION_FAILED"
|
||||
FINGERPRINT_RACE_CONDITION = "FINGERPRINT_RACE_CONDITION"
|
||||
FINGERPRINT_NOT_FOUND = "FINGERPRINT_NOT_FOUND"
|
||||
|
||||
# Session spezifisch
|
||||
SESSION_EXPIRED = "SESSION_EXPIRED"
|
||||
SESSION_INVALID = "SESSION_INVALID"
|
||||
SESSION_SAVE_FAILED = "SESSION_SAVE_FAILED"
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren