import requests import json import time from typing import Optional, Dict, Any from dataclasses import dataclass from datetime import datetime, timedelta @dataclass class SigmaConfig: """Configuration for Sigma API client""" client_id: str client_secret: str base_url: str class SigmaAPIClient: """ Sigma Computing REST API client for admin tasks """ def __init__(self, config: SigmaConfig): self.config = config self.access_token: Optional[str] = None self.token_expires_at: Optional[datetime] = None self.session = requests.Session() def _get_token_url(self) -> str: """Get the token endpoint URL""" return f"{self.config.base_url}/v2/auth/token" def _is_token_expired(self) -> bool: """Check if the current token is expired or about to expire""" if not self.access_token or not self.token_expires_at: return True # Refresh token 5 minutes before expiry return datetime.now() >= (self.token_expires_at - timedelta(minutes=5)) def authenticate(self) -> bool: """ Authenticate with Sigma API and get access token Returns True if successful, False otherwise """ if not self._is_token_expired(): return True token_url = self._get_token_url() headers = { 'Content-Type': 'application/x-www-form-urlencoded' } data = { 'grant_type': 'client_credentials', 'client_id': self.config.client_id, 'client_secret': self.config.client_secret } try: response = self.session.post(token_url, headers=headers, data=data) response.raise_for_status() token_data = response.json() self.access_token = token_data['access_token'] expires_in = token_data.get('expires_in', 3600) # Default 1 hour self.token_expires_at = datetime.now() + timedelta(seconds=expires_in) # Update session headers self.session.headers.update({ 'Authorization': f'Bearer {self.access_token}', 'Content-Type': 'application/json' }) return True except requests.exceptions.RequestException as e: print(f"Authentication failed: {e}") return False def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response: """ Make authenticated request to Sigma API """ if not self.authenticate(): raise Exception("Failed to authenticate with Sigma API") url = f"{self.config.base_url}{endpoint}" # Rate limiting: 1 request per second for token endpoint if 'auth/token' in endpoint: time.sleep(1) response = self.session.request(method, url, **kwargs) response.raise_for_status() return response def get(self, endpoint: str, **kwargs) -> Dict[Any, Any]: """Make GET request""" response = self._make_request('GET', endpoint, **kwargs) return response.json() def post(self, endpoint: str, data: Optional[Dict] = None, **kwargs) -> Dict[Any, Any]: """Make POST request""" if data: kwargs['json'] = data response = self._make_request('POST', endpoint, **kwargs) return response.json() def patch(self, endpoint: str, data: Optional[Dict] = None, **kwargs) -> Dict[Any, Any]: """Make PATCH request""" if data: kwargs['json'] = data response = self._make_request('PATCH', endpoint, **kwargs) return response.json() def delete(self, endpoint: str, **kwargs) -> bool: """Make DELETE request""" response = self._make_request('DELETE', endpoint, **kwargs) return response.status_code == 204