import requests
import time
from typing import Optional, Dict, Any
class DripAPIError(Exception):
def __init__(self, status: int, error: str, message: str, details: Optional[Dict] = None, request_id: Optional[str] = None):
super().__init__(message)
self.status = status
self.error = error
self.message = message
self.details = details or {}
self.request_id = request_id
class DripClient:
def __init__(self, api_key: str, realm_id: str):
self.api_key = api_key
self.realm_id = realm_id
self.base_url = 'https://api.drip.re/api/v1'
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Dict[Any, Any]:
try:
response = self.session.request(method, f"{self.base_url}{endpoint}", json=data)
if not response.ok:
try:
error_data = response.json()
except ValueError:
error_data = {'error': 'UnknownError', 'message': 'Unknown error occurred'}
raise DripAPIError(
status=response.status_code,
error=error_data.get('error', 'UnknownError'),
message=error_data.get('message', 'Unknown error'),
details=error_data.get('details'),
request_id=error_data.get('requestId')
)
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"Network error: {str(e)}")
def safe_request(self, method: str, endpoint: str, data: Optional[Dict] = None, retries: int = 3, retry_delay: int = 1) -> Dict[Any, Any]:
for attempt in range(retries + 1):
try:
return self.request(method, endpoint, data)
except DripAPIError as e:
# Don't retry client errors except rate limits
if 400 <= e.status < 500 and e.status != 429:
raise e
# Handle rate limits
if e.status == 429:
retry_after = e.details.get('retryAfter', 60)
if attempt < retries:
time.sleep(retry_after)
continue
# Retry server errors
if e.status >= 500 and attempt < retries:
time.sleep(retry_delay * (2 ** attempt))
continue
# Final attempt
if attempt == retries:
raise e
except Exception as e:
if attempt == retries:
raise e
time.sleep(retry_delay * (2 ** attempt))