diff options
| author | Christian Cleberg <[email protected]> | 2026-04-09 19:45:45 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-09 19:45:45 -0500 |
| commit | acbff854f2da96bddcaede1385e7fefeba0fb34b (patch) | |
| tree | 48d4707cd3276d2825370f0793008a6ffcfff936 /src/srht_contrib/services/srht_client.py | |
| download | hutch-stats-acbff854f2da96bddcaede1385e7fefeba0fb34b.tar.gz hutch-stats-acbff854f2da96bddcaede1385e7fefeba0fb34b.tar.bz2 hutch-stats-acbff854f2da96bddcaede1385e7fefeba0fb34b.zip | |
initial commit
Diffstat (limited to 'src/srht_contrib/services/srht_client.py')
| -rw-r--r-- | src/srht_contrib/services/srht_client.py | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/srht_contrib/services/srht_client.py b/src/srht_contrib/services/srht_client.py new file mode 100644 index 0000000..c8e2649 --- /dev/null +++ b/src/srht_contrib/services/srht_client.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import logging +from typing import Any + +import httpx + + +logger = logging.getLogger(__name__) + + +class SourceHutClientError(RuntimeError): + """Raised when a SourceHut GraphQL request fails.""" + + +class SourceHutGraphQLClient: + def __init__( + self, + endpoint: str, + token: str, + *, + timeout: float = 15.0, + max_retries: int = 2, + transport: httpx.BaseTransport | None = None, + ) -> None: + self.endpoint = endpoint + self.timeout = timeout + self.max_retries = max_retries + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + } + self._client = httpx.Client(headers=headers, timeout=timeout, transport=transport) + + def execute(self, query: str, variables: dict[str, Any] | None = None) -> dict[str, Any]: + payload = {"query": query, "variables": variables or {}} + attempts = self.max_retries + 1 + + for attempt in range(1, attempts + 1): + try: + response = self._client.post(self.endpoint, json=payload) + response.raise_for_status() + body = response.json() + except httpx.HTTPStatusError as exc: + response_text = exc.response.text[:500] + logger.warning( + "SourceHut HTTP failure from %s on attempt %s/%s: %s %s", + self.endpoint, + attempt, + attempts, + exc.response.status_code, + response_text, + ) + if exc.response.status_code >= 500 and attempt < attempts: + continue + raise SourceHutClientError( + f"HTTP error from SourceHut: {exc.response.status_code} {response_text}".strip() + ) from exc + except httpx.HTTPError as exc: + logger.warning( + "SourceHut network failure from %s on attempt %s/%s", + self.endpoint, + attempt, + attempts, + ) + if attempt < attempts: + continue + raise SourceHutClientError("Network error while contacting SourceHut") from exc + + if "errors" in body: + raise SourceHutClientError(f"GraphQL errors returned by SourceHut: {body['errors']}") + return body.get("data", {}) + + raise SourceHutClientError("SourceHut request exhausted retries") + + def close(self) -> None: + self._client.close() |
