"""
Core HTTP client for interacting with the iMednet REST API.
This module defines the `Client` class which handles:
- Authentication headers (API key and security key).
- Configuration of base URL, timeouts, and retry logic.
- Making HTTP GET and POST requests.
- Error mapping to custom errors.
- Context-manager support for automatic cleanup.
"""
from __future__ import annotations
import logging
from types import TracebackType
from typing import Any, Dict, Optional
import httpx
from imednet.core.http.executor import SyncRequestExecutor
from imednet.core.retry import RetryPolicy
from .http_client_base import HTTPClientBase
logger = logging.getLogger(__name__)
[docs]class Client(HTTPClientBase[httpx.Client, SyncRequestExecutor]):
"""
Core HTTP client for the iMednet API.
Attributes:
base_url: Base URL for API requests.
timeout: Default timeout for requests.
retries: Number of retry attempts for transient errors.
backoff_factor: Multiplier for exponential backoff.
"""
def _get_client_class(self) -> type[httpx.Client]:
return httpx.Client
def _create_executor(
self, client: httpx.Client, retry_policy: Optional[RetryPolicy] = None
) -> SyncRequestExecutor:
# Use wrapper to allow mocking client.request after initialization
def send_wrapper(method: str, url: str, **kwargs: Any) -> httpx.Response:
return client.request(method, url, **kwargs)
return SyncRequestExecutor(
send=send_wrapper,
retries=self.retries,
backoff_factor=self.backoff_factor,
tracer=self._tracer,
retry_policy=retry_policy,
)
def __enter__(self) -> Client:
return self
def __exit__(
self,
exc_type: Optional[type[BaseException]],
exc: Optional[BaseException],
tb: Optional[TracebackType],
) -> None:
self.close()
[docs] def close(self) -> None:
"""Close the underlying HTTP client."""
self._client.close()
def _request(self, method: str, path: str, **kwargs: Any) -> httpx.Response:
return self._executor(method, path, **kwargs)
[docs] def get(
self,
path: str,
params: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> httpx.Response:
"""
Make a GET request.
Args:
path: URL path or full URL.
params: Query parameters.
"""
return self._request("GET", path, params=params, **kwargs)
[docs] def post(
self,
path: str,
json: Optional[Any] = None,
**kwargs: Any,
) -> httpx.Response:
"""
Make a POST request.
Args:
path: URL path or full URL.
json: JSON body for the request.
"""
return self._request("POST", path, json=json, **kwargs)