semantic
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
# [DEF:network:Module]
|
||||
# [DEF:NetworkModule:Module]
|
||||
#
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: network, http, client, api, requests, session, authentication
|
||||
# @PURPOSE: Инкапсулирует низкоуровневую HTTP-логику для взаимодействия с Superset API, включая аутентификацию, управление сессией, retry-логику и обработку ошибок.
|
||||
# @LAYER: Infra
|
||||
# @RELATION: DEPENDS_ON -> backend.src.core.logger
|
||||
# @RELATION: DEPENDS_ON -> requests
|
||||
# @RELATION: [DEPENDS_ON] ->[LoggerModule]
|
||||
# @PUBLIC_API: APIClient
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
@@ -82,7 +81,7 @@ class DashboardNotFoundError(SupersetAPIError):
|
||||
# [DEF:NetworkError:Class]
|
||||
# @PURPOSE: Exception raised when a network level error occurs.
|
||||
class NetworkError(Exception):
|
||||
# [DEF:network.APIClient.__init__:Function]
|
||||
# [DEF:NetworkError.__init__:Function]
|
||||
# @PURPOSE: Initializes the network error.
|
||||
# @PRE: message is a string.
|
||||
# @POST: NetworkError is initialized.
|
||||
@@ -90,11 +89,11 @@ class NetworkError(Exception):
|
||||
with belief_scope("NetworkError.__init__"):
|
||||
self.context = context
|
||||
super().__init__(f"[NETWORK_FAILURE] {message} | Context: {self.context}")
|
||||
# [/DEF:__init__:Function]
|
||||
# [/DEF:NetworkError.__init__:Function]
|
||||
# [/DEF:NetworkError:Class]
|
||||
|
||||
|
||||
# [DEF:network.SupersetAuthCache:Class]
|
||||
# [DEF:SupersetAuthCache:Class]
|
||||
# @PURPOSE: Process-local cache for Superset access/csrf tokens keyed by environment credentials.
|
||||
# @PRE: base_url and username are stable strings.
|
||||
# @POST: Cached entries expire automatically by TTL and can be reused across requests.
|
||||
@@ -152,8 +151,8 @@ class SupersetAuthCache:
|
||||
# [DEF:APIClient:Class]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Synchronous Superset API client with process-local auth token caching.
|
||||
# @RELATION: DEPENDS_ON -> network.SupersetAuthCache
|
||||
# @RELATION: DEPENDS_ON -> logger
|
||||
# @RELATION: [DEPENDS_ON] ->[SupersetAuthCache]
|
||||
# @RELATION: [DEPENDS_ON] ->[LoggerModule]
|
||||
class APIClient:
|
||||
DEFAULT_TIMEOUT = 30
|
||||
|
||||
@@ -256,7 +255,7 @@ class APIClient:
|
||||
return f"{self.api_base_url}{normalized_endpoint}"
|
||||
# [/DEF:_build_api_url:Function]
|
||||
|
||||
# [DEF:authenticate:Function]
|
||||
# [DEF:APIClient.authenticate:Function]
|
||||
# @PURPOSE: Выполняет аутентификацию в Superset API и получает access и CSRF токены.
|
||||
# @PRE: self.auth and self.base_url must be valid.
|
||||
# @POST: `self._tokens` заполнен, `self._authenticated` установлен в `True`.
|
||||
@@ -364,7 +363,14 @@ class APIClient:
|
||||
if status_code == 502 or status_code == 503 or status_code == 504:
|
||||
raise NetworkError(f"Environment unavailable (Status {status_code})", status_code=status_code) from e
|
||||
if status_code == 404:
|
||||
raise DashboardNotFoundError(endpoint) from e
|
||||
if self._is_dashboard_endpoint(endpoint):
|
||||
raise DashboardNotFoundError(endpoint) from e
|
||||
raise SupersetAPIError(
|
||||
f"API resource not found at endpoint '{endpoint}'",
|
||||
status_code=status_code,
|
||||
endpoint=endpoint,
|
||||
subtype="not_found",
|
||||
) from e
|
||||
if status_code == 403:
|
||||
raise PermissionDeniedError() from e
|
||||
if status_code == 401:
|
||||
@@ -372,6 +378,24 @@ class APIClient:
|
||||
raise SupersetAPIError(f"API Error {status_code}: {e.response.text}") from e
|
||||
# [/DEF:_handle_http_error:Function]
|
||||
|
||||
# [DEF:_is_dashboard_endpoint:Function]
|
||||
# @PURPOSE: Determine whether an API endpoint represents a dashboard resource for 404 translation.
|
||||
# @PRE: endpoint may be relative or absolute.
|
||||
# @POST: Returns true only for dashboard-specific endpoints.
|
||||
def _is_dashboard_endpoint(self, endpoint: str) -> bool:
|
||||
normalized_endpoint = str(endpoint or "").strip().lower()
|
||||
if not normalized_endpoint:
|
||||
return False
|
||||
if normalized_endpoint.startswith("http://") or normalized_endpoint.startswith("https://"):
|
||||
try:
|
||||
normalized_endpoint = "/" + normalized_endpoint.split("/api/v1", 1)[1].lstrip("/")
|
||||
except IndexError:
|
||||
return False
|
||||
if normalized_endpoint.startswith("/api/v1/"):
|
||||
normalized_endpoint = normalized_endpoint[len("/api/v1"):]
|
||||
return normalized_endpoint.startswith("/dashboard/") or normalized_endpoint == "/dashboard"
|
||||
# [/DEF:_is_dashboard_endpoint:Function]
|
||||
|
||||
# [DEF:_handle_network_error:Function]
|
||||
# @PURPOSE: (Helper) Преобразует сетевые ошибки в `NetworkError`.
|
||||
# @PARAM: e (requests.exceptions.RequestException) - Ошибка.
|
||||
@@ -505,4 +529,4 @@ class APIClient:
|
||||
|
||||
# [/DEF:APIClient:Class]
|
||||
|
||||
# [/DEF:backend.core.utils.network:Module]
|
||||
# [/DEF:NetworkModule:Module]
|
||||
|
||||
Reference in New Issue
Block a user