"""NAS realestate 내부 계약 — X-Internal-Key + retry.""" from __future__ import annotations import asyncio import logging import httpx logger = logging.getLogger(__name__) _MAX_ATTEMPTS = 3 _RETRY_STATUSES = {429, 500, 502, 503, 504} class NASClient: def __init__(self, base_url: str, api_key: str, timeout: float = 15.0): self._base_url = base_url.rstrip("/") self._api_key = api_key self._client = httpx.AsyncClient(timeout=timeout) async def close(self) -> None: await self._client.aclose() async def get_targets(self) -> dict: return await self._request("GET", "/api/internal/realestate/targets") async def post_ingest(self, fetched_at: str, batches: list[dict]) -> dict: return await self._request( "POST", "/api/internal/realestate/listings-ingest", json={"fetched_at": fetched_at, "batches": batches}) async def _request(self, method: str, path: str, **kwargs) -> dict: url = f"{self._base_url}{path}" headers = {"X-Internal-Key": self._api_key} for attempt in range(_MAX_ATTEMPTS): try: resp = await self._client.request(method, url, headers=headers, **kwargs) if resp.status_code in _RETRY_STATUSES and attempt < _MAX_ATTEMPTS - 1: await asyncio.sleep(2 ** attempt) continue resp.raise_for_status() return resp.json() except httpx.TimeoutException: if attempt < _MAX_ATTEMPTS - 1: await asyncio.sleep(2 ** attempt) continue raise raise RuntimeError("retry exhausted")