Files
homelab-infra/apps/dashboard/backend/app/clients/base.py
T

112 lines
3.1 KiB
Python

from __future__ import annotations
import logging
from typing import Any
import httpx
from app.config import Settings
logger = logging.getLogger(__name__)
class BaseHTTPClient:
def __init__(self, settings: Settings, name: str, base_url: str | None) -> None:
self.settings = settings
self.name = name
self.base_url = str(base_url).rstrip("/") if base_url else None
async def _request_json(
self,
method: str,
path: str,
*,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
auth: tuple[str, str] | None = None,
json: Any | None = None,
) -> Any | None:
response = await self._request(
method,
path,
headers=headers,
params=params,
auth=auth,
json=json,
)
if response is None:
return None
try:
return response.json()
except ValueError:
logger.warning("%s returned non-JSON payload for %s", self.name, path)
return None
async def _request_text(
self,
method: str,
path: str,
*,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
auth: tuple[str, str] | None = None,
) -> str | None:
response = await self._request(
method,
path,
headers=headers,
params=params,
auth=auth,
)
if response is None:
return None
return response.text
async def _request(
self,
method: str,
path: str,
*,
headers: dict[str, str] | None = None,
params: dict[str, Any] | None = None,
auth: tuple[str, str] | None = None,
json: Any | None = None,
) -> httpx.Response | None:
if not self.base_url:
logger.info("%s client skipped because base URL is not configured", self.name)
return None
url = f"{self.base_url}/{path.lstrip('/')}"
try:
async with httpx.AsyncClient(
timeout=self.settings.request_timeout_seconds,
trust_env=False,
) as client:
response = await client.request(
method,
url,
headers=headers,
params=params,
auth=auth,
json=json,
)
response.raise_for_status()
return response
except httpx.TimeoutException:
logger.warning("%s request timed out: %s %s", self.name, method, url)
except httpx.HTTPStatusError as exc:
logger.warning(
"%s request failed with status %s for %s %s",
self.name,
exc.response.status_code,
method,
url,
)
except httpx.HTTPError as exc:
logger.warning("%s request error for %s %s: %s", self.name, method, url, exc)
return None