71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
from __future__ import annotations
|
||
|
||
import logging
|
||
import time
|
||
from typing import Any
|
||
|
||
import httpx
|
||
|
||
|
||
logger = logging.getLogger("connecthub.integrations")
|
||
|
||
|
||
class BaseClient:
|
||
"""
|
||
统一的外部系统访问 SDK 基类。
|
||
业务 Job 禁止直接写 HTTP,只能调用 integrations 下的 Client。
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
*,
|
||
base_url: str,
|
||
timeout_s: float = 10.0,
|
||
retries: int = 2,
|
||
retry_backoff_s: float = 0.5,
|
||
headers: dict[str, str] | None = None,
|
||
) -> None:
|
||
self.base_url = base_url.rstrip("/")
|
||
self.timeout_s = timeout_s
|
||
self.retries = retries
|
||
self.retry_backoff_s = retry_backoff_s
|
||
self.headers = headers or {}
|
||
|
||
self._client = httpx.Client(
|
||
base_url=self.base_url,
|
||
timeout=httpx.Timeout(self.timeout_s),
|
||
headers=self.headers,
|
||
)
|
||
|
||
def close(self) -> None:
|
||
self._client.close()
|
||
|
||
def request(self, method: str, path: str, **kwargs: Any) -> httpx.Response:
|
||
url = path if path.startswith("/") else f"/{path}"
|
||
last_exc: Exception | None = None
|
||
for attempt in range(self.retries + 1):
|
||
try:
|
||
start = time.time()
|
||
resp = self._client.request(method=method, url=url, **kwargs)
|
||
elapsed_ms = int((time.time() - start) * 1000)
|
||
logger.info("HTTP %s %s -> %s (%sms)", method, url, resp.status_code, elapsed_ms)
|
||
resp.raise_for_status()
|
||
return resp
|
||
except Exception as e: # noqa: BLE001 (framework-wide)
|
||
last_exc = e
|
||
logger.warning("HTTP failed (%s %s) attempt=%s err=%r", method, url, attempt + 1, e)
|
||
if attempt < self.retries:
|
||
time.sleep(self.retry_backoff_s * (2**attempt))
|
||
continue
|
||
raise
|
||
assert last_exc is not None
|
||
raise last_exc
|
||
|
||
def get_json(self, path: str, **kwargs: Any) -> Any:
|
||
return self.request("GET", path, **kwargs).json()
|
||
|
||
def post_json(self, path: str, json: Any = None, **kwargs: Any) -> Any:
|
||
return self.request("POST", path, json=json, **kwargs).json()
|
||
|
||
|