chore(semantic): remediate backend core contracts
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user