131 lines
4.7 KiB
Python
131 lines
4.7 KiB
Python
|
|
from typing import Any, Dict, Optional
|
||
|
|
|
||
|
|
import requests
|
||
|
|
from colorama import Fore
|
||
|
|
|
||
|
|
|
||
|
|
class HTTPClient:
|
||
|
|
base_url: str
|
||
|
|
api_key: str
|
||
|
|
version: str
|
||
|
|
|
||
|
|
def __init__(self, base_url: str, api_key: str, version: str):
|
||
|
|
self.base_url = base_url.rstrip("/")
|
||
|
|
self.api_key = api_key
|
||
|
|
self.version = version
|
||
|
|
|
||
|
|
def _headers(self) -> Dict[str, str]:
|
||
|
|
return {
|
||
|
|
"Authorization": f"Bearer {self.api_key}",
|
||
|
|
"X-Traceloop-SDK-Version": self.version,
|
||
|
|
}
|
||
|
|
|
||
|
|
def post(self, path: str, data: Dict[str, Any]) -> Any:
|
||
|
|
"""
|
||
|
|
Make a POST request to the API
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
response = requests.post(
|
||
|
|
f"{self.base_url}/v2/{path.lstrip('/')}",
|
||
|
|
json=data,
|
||
|
|
headers=self._headers(),
|
||
|
|
)
|
||
|
|
response.raise_for_status()
|
||
|
|
return response.json()
|
||
|
|
except requests.exceptions.RequestException as e:
|
||
|
|
error_msg = self._format_error_message(path, e)
|
||
|
|
print(Fore.RED + error_msg + Fore.RESET)
|
||
|
|
return None
|
||
|
|
|
||
|
|
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
|
||
|
|
"""
|
||
|
|
Make a GET request to the API
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
response = requests.get(
|
||
|
|
f"{self.base_url}/v2/{path.lstrip('/')}",
|
||
|
|
params=params,
|
||
|
|
headers=self._headers(),
|
||
|
|
)
|
||
|
|
response.raise_for_status()
|
||
|
|
|
||
|
|
content_type = response.headers.get("content-type", "").lower()
|
||
|
|
if "text/csv" in content_type or "application/x-ndjson" in content_type:
|
||
|
|
return response.text
|
||
|
|
else:
|
||
|
|
return response.json()
|
||
|
|
except requests.exceptions.RequestException as e:
|
||
|
|
error_msg = self._format_error_message(path, e)
|
||
|
|
print(Fore.RED + error_msg + Fore.RESET)
|
||
|
|
return None
|
||
|
|
|
||
|
|
def delete(self, path: str) -> bool:
|
||
|
|
"""
|
||
|
|
Make a DELETE request to the API
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
response = requests.delete(
|
||
|
|
f"{self.base_url}/v2/{path.lstrip('/')}", headers=self._headers()
|
||
|
|
)
|
||
|
|
response.raise_for_status()
|
||
|
|
return response.status_code == 204 or response.status_code == 200
|
||
|
|
except requests.exceptions.RequestException as e:
|
||
|
|
error_msg = self._format_error_message(path, e)
|
||
|
|
print(Fore.RED + error_msg + Fore.RESET)
|
||
|
|
return False
|
||
|
|
|
||
|
|
def put(self, path: str, data: Dict[str, Any]) -> Any:
|
||
|
|
"""
|
||
|
|
Make a PUT request to the API
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
response = requests.put(
|
||
|
|
f"{self.base_url}/v2/{path.lstrip('/')}",
|
||
|
|
json=data,
|
||
|
|
headers=self._headers(),
|
||
|
|
)
|
||
|
|
response.raise_for_status()
|
||
|
|
if response.content:
|
||
|
|
return response.json()
|
||
|
|
else:
|
||
|
|
return {}
|
||
|
|
except requests.exceptions.RequestException as e:
|
||
|
|
error_msg = self._format_error_message(path, e)
|
||
|
|
print(Fore.RED + error_msg + Fore.RESET)
|
||
|
|
return None
|
||
|
|
|
||
|
|
def _format_error_message(self, path: str, exception: requests.exceptions.RequestException) -> str:
|
||
|
|
"""
|
||
|
|
Format a detailed error message including server response if available
|
||
|
|
"""
|
||
|
|
error_parts = [f"Error making request to {path}: {str(exception)}"]
|
||
|
|
|
||
|
|
# Try to extract error details from response
|
||
|
|
if hasattr(exception, 'response') and exception.response is not None:
|
||
|
|
response = exception.response
|
||
|
|
|
||
|
|
# Try to parse JSON error from response
|
||
|
|
try:
|
||
|
|
error_data = response.json()
|
||
|
|
if isinstance(error_data, dict):
|
||
|
|
# Check common error fields
|
||
|
|
if 'error' in error_data:
|
||
|
|
error_parts.append(f"Server error: {error_data['error']}")
|
||
|
|
elif 'message' in error_data:
|
||
|
|
error_parts.append(f"Server message: {error_data['message']}")
|
||
|
|
elif 'msg' in error_data:
|
||
|
|
error_parts.append(f"Server message: {error_data['msg']}")
|
||
|
|
else:
|
||
|
|
# Include the entire JSON if no standard field found
|
||
|
|
error_parts.append(f"Server response: {error_data}")
|
||
|
|
except (ValueError, AttributeError):
|
||
|
|
# Not JSON, try to get text
|
||
|
|
try:
|
||
|
|
error_text = response.text
|
||
|
|
if error_text and len(error_text) < 500: # Only include short error messages
|
||
|
|
error_parts.append(f"Server response: {error_text}")
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
return "\n".join(error_parts)
|