229 Zeilen
7.7 KiB
Python
229 Zeilen
7.7 KiB
Python
"""
|
|
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" |