chore(semantic): remediate backend core contracts

This commit is contained in:
2026-03-15 21:23:44 +03:00
parent 84a2cd5429
commit 0bf55885a8
7 changed files with 166 additions and 49 deletions

View File

@@ -26,15 +26,21 @@ from .network import (
# [DEF:AsyncAPIClient:Class]
# @TIER: STANDARD
# @PURPOSE: Async Superset API client backed by httpx.AsyncClient with shared auth cache.
# @RELATION: [DEPENDS_ON] ->[backend.src.core.utils.network.SupersetAuthCache]
# @RELATION: [CALLS] ->[backend.src.core.utils.network.SupersetAuthCache.get]
# @RELATION: [CALLS] ->[backend.src.core.utils.network.SupersetAuthCache.set]
class AsyncAPIClient:
DEFAULT_TIMEOUT = 30
_auth_locks: Dict[tuple[str, str, bool], asyncio.Lock] = {}
# [DEF:__init__:Function]
# @TIER: STANDARD
# @PURPOSE: Initialize async API client for one environment.
# @PRE: config contains base_url and auth payload.
# @POST: Client is ready for async request/authentication flow.
# @DATA_CONTRACT: Input[config: Dict[str, Any]] -> self._auth_cache_key[str]
def __init__(self, config: Dict[str, Any], verify_ssl: bool = True, timeout: int = DEFAULT_TIMEOUT):
self.base_url: str = self._normalize_base_url(config.get("base_url", ""))
self.api_base_url: str = f"{self.base_url}/api/v1"
@@ -56,6 +62,7 @@ class AsyncAPIClient:
# [/DEF:__init__:Function]
# [DEF:_normalize_base_url:Function]
# @TIER: TRIVIAL
# @PURPOSE: Normalize base URL for Superset API root construction.
# @POST: Returns canonical base URL without trailing slash and duplicate /api/v1 suffix.
def _normalize_base_url(self, raw_url: str) -> str:
@@ -66,6 +73,7 @@ class AsyncAPIClient:
# [/DEF:_normalize_base_url:Function]
# [DEF:_build_api_url:Function]
# @TIER: TRIVIAL
# @PURPOSE: Build full API URL from relative Superset endpoint.
# @POST: Returns absolute URL for upstream request.
def _build_api_url(self, endpoint: str) -> str:
@@ -80,6 +88,7 @@ class AsyncAPIClient:
# [/DEF:_build_api_url:Function]
# [DEF:_get_auth_lock:Function]
# @TIER: TRIVIAL
# @PURPOSE: Return per-cache-key async lock to serialize fresh login attempts.
# @POST: Returns stable asyncio.Lock instance.
@classmethod
@@ -93,8 +102,11 @@ class AsyncAPIClient:
# [/DEF:_get_auth_lock:Function]
# [DEF:authenticate:Function]
# @TIER: STANDARD
# @PURPOSE: Authenticate against Superset and cache access/csrf tokens.
# @POST: Client tokens are populated and reusable across requests.
# @SIDE_EFFECT: Performs network requests to Superset authentication endpoints.
# @DATA_CONTRACT: None -> Output[Dict[str, str]]
async def authenticate(self) -> Dict[str, str]:
cached_tokens = SupersetAuthCache.get(self._auth_cache_key)
if cached_tokens and cached_tokens.get("access_token") and cached_tokens.get("csrf_token"):
@@ -150,8 +162,10 @@ class AsyncAPIClient:
# [/DEF:authenticate:Function]
# [DEF:get_headers:Function]
# @TIER: STANDARD
# @PURPOSE: Return authenticated Superset headers for async requests.
# @POST: Headers include Authorization and CSRF tokens.
# @RELATION: CALLS -> self.authenticate
async def get_headers(self) -> Dict[str, str]:
if not self._authenticated:
await self.authenticate()
@@ -164,8 +178,13 @@ class AsyncAPIClient:
# [/DEF:get_headers:Function]
# [DEF:request:Function]
# @TIER: STANDARD
# @PURPOSE: Perform one authenticated async Superset API request.
# @POST: Returns JSON payload or raw httpx.Response when raw_response=true.
# @SIDE_EFFECT: Performs network I/O.
# @RELATION: [CALLS] ->[self.get_headers]
# @RELATION: [CALLS] ->[self._handle_http_error]
# @RELATION: [CALLS] ->[self._handle_network_error]
async def request(
self,
method: str,
@@ -196,8 +215,10 @@ class AsyncAPIClient:
# [/DEF:request:Function]
# [DEF:_handle_http_error:Function]
# @TIER: STANDARD
# @PURPOSE: Translate upstream HTTP errors into stable domain exceptions.
# @POST: Raises domain-specific exception for caller flow control.
# @DATA_CONTRACT: Input[httpx.HTTPStatusError] -> Exception
def _handle_http_error(self, exc: httpx.HTTPStatusError, endpoint: str) -> None:
with belief_scope("AsyncAPIClient._handle_http_error"):
status_code = exc.response.status_code
@@ -213,8 +234,10 @@ class AsyncAPIClient:
# [/DEF:_handle_http_error:Function]
# [DEF:_handle_network_error:Function]
# @TIER: STANDARD
# @PURPOSE: Translate generic httpx errors into NetworkError.
# @POST: Raises NetworkError with URL context.
# @DATA_CONTRACT: Input[httpx.HTTPError] -> NetworkError
def _handle_network_error(self, exc: httpx.HTTPError, url: str) -> None:
with belief_scope("AsyncAPIClient._handle_network_error"):
if isinstance(exc, httpx.TimeoutException):
@@ -227,8 +250,10 @@ class AsyncAPIClient:
# [/DEF:_handle_network_error:Function]
# [DEF:aclose:Function]
# @TIER: STANDARD
# @PURPOSE: Close underlying httpx client.
# @POST: Client resources are released.
# @SIDE_EFFECT: Closes network connections.
async def aclose(self) -> None:
await self._client.aclose()
# [/DEF:aclose:Function]

View File

@@ -145,7 +145,10 @@ class SupersetAuthCache:
# [/DEF:SupersetAuthCache:Class]
# [DEF:APIClient:Class]
# @PURPOSE: Инкапсулирует HTTP-логику для работы с API, включая сессии, аутентификацию, и обработку запросов.
# @TIER: STANDARD
# @PURPOSE: Synchronous Superset API client with process-local auth token caching.
# @RELATION: DEPENDS_ON -> backend.src.core.utils.network.SupersetAuthCache
# @RELATION: DEPENDS_ON -> backend.src.core.logger.logger
class APIClient:
DEFAULT_TIMEOUT = 30