63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from urllib.error import HTTPError, URLError
|
|
from urllib.parse import urlencode
|
|
from urllib.request import Request, urlopen
|
|
|
|
from src.core.config import load_settings
|
|
from src.integrations.exchange.exceptions import (
|
|
ExchangeConnectionError,
|
|
ExchangeResponseError,
|
|
)
|
|
|
|
|
|
class ExchangeRestClient:
|
|
def __init__(self) -> None:
|
|
self.settings = load_settings()
|
|
if not self.settings.exchange_base_url:
|
|
raise ExchangeConnectionError("EXCHANGE_BASE_URL is empty.")
|
|
self.base_url = self.settings.exchange_base_url.rstrip("/")
|
|
self.timeout = self.settings.exchange_timeout_sec
|
|
|
|
def get_json(self, path: str, params: dict[str, str] | None = None) -> dict:
|
|
query = f"?{urlencode(params)}" if params else ""
|
|
url = f"{self.base_url}{path}{query}"
|
|
|
|
request = Request(
|
|
url=url,
|
|
method="GET",
|
|
headers={
|
|
"Accept": "application/json",
|
|
"User-Agent": "dzentra-bot/2.0.0",
|
|
},
|
|
)
|
|
|
|
try:
|
|
with urlopen(request, timeout=self.timeout) as response:
|
|
status = getattr(response, "status", 200)
|
|
body = response.read().decode("utf-8")
|
|
except HTTPError as exc:
|
|
raise ExchangeResponseError(
|
|
f"HTTP {exc.code} from exchange: {exc.reason}"
|
|
) from exc
|
|
except URLError as exc:
|
|
raise ExchangeConnectionError(
|
|
f"Network error while calling exchange: {exc.reason}"
|
|
) from exc
|
|
except TimeoutError as exc:
|
|
raise ExchangeConnectionError("Timeout while calling exchange.") from exc
|
|
|
|
if status != 200:
|
|
raise ExchangeResponseError(f"Unexpected HTTP status: {status}")
|
|
|
|
try:
|
|
payload = json.loads(body)
|
|
except json.JSONDecodeError as exc:
|
|
raise ExchangeResponseError("Exchange returned non-JSON response.") from exc
|
|
|
|
if not isinstance(payload, dict):
|
|
raise ExchangeResponseError("Exchange response is not a JSON object.")
|
|
|
|
return payload
|