Files
plantillas-proyectos/backend/api/v1/modules/sitar/common/base_service.py
2026-04-24 09:58:22 -06:00

163 lines
5.4 KiB
Python

"""
SITAR API Base Service
Base service class for SITAR API authentication and HTTP requests.
All specific resource services inherit from this.
"""
import os
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
import httpx
class SitarAPIBaseService:
"""Base service for SITAR API integration with authentication"""
_token: Optional[str] = None
_token_expires: Optional[datetime] = None
def __init__(self):
"""Initialize base service with API credentials"""
self.base_url = os.getenv("SITAR_API_URL")
self.username = os.getenv("SITAR_API_USER")
self.password = os.getenv("SITAR_API_PASSWORD")
self.timeout = 30.0
if not all([self.base_url, self.username, self.password]):
raise ValueError(
"Missing SITAR API configuration. "
"Set SITAR_API_URL, SITAR_API_USER, and SITAR_API_PASSWORD environment variables."
)
async def _get_token(self) -> str:
"""
Get authentication token, refreshing if necessary
Returns:
str: Bearer token for API authentication
Raises:
httpx.HTTPError: If authentication fails
"""
# Return cached token if still valid
if self._token and self._token_expires and datetime.now() < self._token_expires:
return self._token
# Authenticate and get new token
login_url = f"{self.base_url}/fractions/api/v1/auth/login"
payload = {"username": self.username, "password": self.password}
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.post(login_url, json=payload)
response.raise_for_status()
data = response.json()
self._token = data.get("access_token") or data.get("token")
if not self._token:
raise ValueError("No token received from SITAR API")
# Set token expiration (assume 1 hour if not specified)
self._token_expires = datetime.now() + timedelta(hours=1)
return self._token
async def _make_request(
self,
method: str,
endpoint: str,
params: Optional[Dict[str, Any]] = None,
json_data: Optional[Dict[str, Any]] = None,
) -> Any:
"""
Make authenticated request to SITAR API
Args:
method: HTTP method (GET, POST, etc.)
endpoint: API endpoint path
params: Query parameters
json_data: JSON body data
Returns:
JSON response data
Raises:
httpx.HTTPError: If request fails
"""
token = await self._get_token()
# Ensure no double slash between fractures and endpoint
endpoint = endpoint.lstrip("/")
url = f"{self.base_url}/fractions/{endpoint}"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.request(
method=method,
url=url,
params=params,
json=json_data,
headers=headers,
)
response.raise_for_status()
# DEBUG LOGGING for SITAR inspection
if "fracciones" in url:
import logging
logger = logging.getLogger(__name__)
try:
data = response.json()
return data
except Exception:
logger.error(f"Error parsing SITAR API Response Body: {response.text}")
pass
return response.json()
def _get_token_sync(self) -> str:
"""Same token cache as async path; safe for sync validators (no running asyncio loop)."""
if self._token and self._token_expires and datetime.now() < self._token_expires:
return self._token
login_url = f"{self.base_url}/fractions/api/v1/auth/login"
payload = {"username": self.username, "password": self.password}
with httpx.Client(timeout=self.timeout) as client:
response = client.post(login_url, json=payload)
response.raise_for_status()
data = response.json()
token = data.get("access_token") or data.get("token")
if not token:
raise ValueError("No token received from SITAR API")
self._token = token
self._token_expires = datetime.now() + timedelta(hours=1)
return token
def _make_request_sync(
self,
method: str,
endpoint: str,
params: Optional[Dict[str, Any]] = None,
json_data: Optional[Dict[str, Any]] = None,
) -> Any:
token = self._get_token_sync()
endpoint = endpoint.lstrip("/")
url = f"{self.base_url}/fractions/{endpoint}"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
with httpx.Client(timeout=self.timeout) as client:
response = client.request(
method=method,
url=url,
params=params,
json=json_data,
headers=headers,
)
response.raise_for_status()
return response.json()