agAdvisor / src /api_clients /base_client.py
tirtho149's picture
Deploy AgAdvisor
b30f068 verified
Raw
History Blame Contribute Delete
4.8 kB
"""
Base API Client
Abstract base class for all API clients
"""
from abc import ABC, abstractmethod
import requests
from typing import Dict, Any, Optional
import time
class BaseAPIClient(ABC):
"""
Abstract base class for API clients
Provides common functionality:
- HTTP request handling
- Error handling
- Rate limiting
- Response validation
"""
def __init__(self):
"""Initialize base client"""
self.session = requests.Session()
self.timeout = 30 # Default timeout in seconds
self.last_request_time = 0
self.min_request_interval = 0.1 # 100ms between requests
def _wait_for_rate_limit(self):
"""Implement simple rate limiting"""
elapsed = time.time() - self.last_request_time
if elapsed < self.min_request_interval:
time.sleep(self.min_request_interval - elapsed)
self.last_request_time = time.time()
def _make_request(
self,
method: str,
url: str,
params: Optional[Dict] = None,
headers: Optional[Dict] = None,
data: Optional[Dict] = None,
json: Optional[Dict] = None
) -> requests.Response:
"""
Make HTTP request with error handling
Args:
method: HTTP method (GET, POST, etc.)
url: Request URL
params: Query parameters
headers: Request headers
data: Form data
json: JSON data
Returns:
Response object
Raises:
requests.exceptions.RequestException: On request failure
"""
self._wait_for_rate_limit()
try:
response = self.session.request(
method=method,
url=url,
params=params,
headers=headers,
data=data,
json=json,
timeout=self.timeout
)
response.raise_for_status()
return response
except requests.exceptions.Timeout:
raise requests.exceptions.RequestException(
f"Request to {url} timed out after {self.timeout}s"
)
except requests.exceptions.ConnectionError:
raise requests.exceptions.RequestException(
f"Failed to connect to {url}. Check your internet connection."
)
except requests.exceptions.HTTPError as e:
status_code = e.response.status_code
if status_code == 401:
raise requests.exceptions.RequestException(
"Authentication failed. Check your API key."
)
elif status_code == 429:
raise requests.exceptions.RequestException(
"Rate limit exceeded. Please wait before making more requests."
)
elif status_code == 404:
raise requests.exceptions.RequestException(
"Resource not found. Check your request parameters."
)
else:
raise requests.exceptions.RequestException(
f"HTTP {status_code} error: {str(e)}"
)
def get(self, url: str, params: Optional[Dict] = None,
headers: Optional[Dict] = None) -> Dict[str, Any]:
"""
Make GET request
Args:
url: Request URL
params: Query parameters
headers: Request headers
Returns:
JSON response as dict
"""
response = self._make_request("GET", url, params=params, headers=headers)
return response.json()
def post(self, url: str, data: Optional[Dict] = None,
json: Optional[Dict] = None, headers: Optional[Dict] = None) -> Dict[str, Any]:
"""
Make POST request
Args:
url: Request URL
data: Form data
json: JSON data
headers: Request headers
Returns:
JSON response as dict
"""
response = self._make_request("POST", url, data=data, json=json, headers=headers)
return response.json()
@abstractmethod
def _validate_params(self, **kwargs) -> bool:
"""
Validate request parameters
Should be implemented by subclasses
Args:
**kwargs: Parameters to validate
Returns:
True if valid
Raises:
ValueError: If parameters are invalid
"""
pass
def __del__(self):
"""Cleanup: close session"""
if hasattr(self, 'session'):
self.session.close()