017 fix returning all tariffs
This commit is contained in:
@@ -10,8 +10,8 @@ class DeliveryProvider(ABC):
|
||||
name: str
|
||||
|
||||
@abstractmethod
|
||||
async def get_price(self, request: DeliveryRequest) -> DeliveryPrice:
|
||||
"""Fetch one quote from an external provider."""
|
||||
async def get_prices(self, request: DeliveryRequest) -> list[DeliveryPrice]:
|
||||
"""Fetch provider tariffs for a delivery request."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ class CDEKClient:
|
||||
|
||||
if not isinstance(raw_payload, dict):
|
||||
raise CDEKClientError("CDEK tariff payload must be a JSON object.")
|
||||
log.info(f"Founded {len(response.json())} tarrifs")
|
||||
return raw_payload
|
||||
|
||||
raise CDEKClientError("CDEK tariff request failed unexpectedly.")
|
||||
@@ -201,7 +202,7 @@ class CDEKClient:
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=self._timeout_seconds,
|
||||
)
|
||||
log.info(response.json())
|
||||
log.info(f"Founded cities: {response.json()}")
|
||||
except (httpx.TimeoutException, httpx.TransportError) as exc:
|
||||
if attempt < self._retry_attempts:
|
||||
await self._sleep(self._retry_delay(attempt))
|
||||
@@ -282,7 +283,7 @@ class CDEKProvider(DeliveryProvider):
|
||||
)
|
||||
return cls(client=client, cache_ttl_seconds=adapter_config.cdek_cache_ttl_seconds)
|
||||
|
||||
async def get_price(self, request: DeliveryRequest) -> DeliveryPrice:
|
||||
async def get_prices(self, request: DeliveryRequest) -> list[DeliveryPrice]:
|
||||
raw_payload = await self._client.get_raw_price(request)
|
||||
return map_cdek_response(raw_payload)
|
||||
|
||||
|
||||
@@ -10,9 +10,20 @@ class CDEKMappingError(ValueError):
|
||||
"""Raised when CDEK response cannot be mapped."""
|
||||
|
||||
|
||||
def map_cdek_response(payload: dict[str, Any]) -> DeliveryPrice:
|
||||
tariff = _get_first_tariff(payload)
|
||||
def map_cdek_response(payload: dict[str, Any]) -> list[DeliveryPrice]:
|
||||
tariffs = _get_tariffs(payload)
|
||||
payload_currency = payload.get("currency")
|
||||
return [
|
||||
_map_tariff(tariff, payload_currency=payload_currency)
|
||||
for tariff in tariffs
|
||||
]
|
||||
|
||||
|
||||
def _map_tariff(
|
||||
tariff: dict[str, Any],
|
||||
*,
|
||||
payload_currency: object,
|
||||
) -> DeliveryPrice:
|
||||
service_name = tariff.get("tariff_name") or tariff.get("tariff_code")
|
||||
if service_name is None:
|
||||
raise CDEKMappingError("CDEK tariff_name is missing.")
|
||||
@@ -26,7 +37,7 @@ def map_cdek_response(payload: dict[str, Any]) -> DeliveryPrice:
|
||||
raise CDEKMappingError("CDEK period_min is missing.")
|
||||
period_max = tariff.get("period_max", period_min)
|
||||
|
||||
raw_currency = tariff.get("currency") or payload.get("currency") or "RUB"
|
||||
raw_currency = tariff.get("currency") or payload_currency or "RUB"
|
||||
|
||||
try:
|
||||
return DeliveryPrice(
|
||||
@@ -41,11 +52,13 @@ def map_cdek_response(payload: dict[str, Any]) -> DeliveryPrice:
|
||||
raise CDEKMappingError("CDEK response fields have invalid values.") from exc
|
||||
|
||||
|
||||
def _get_first_tariff(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
def _get_tariffs(payload: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
tariff_codes = payload.get("tariff_codes")
|
||||
if not isinstance(tariff_codes, list) or not tariff_codes:
|
||||
raise CDEKMappingError("CDEK response must include non-empty tariff_codes.")
|
||||
tariff = tariff_codes[0]
|
||||
if not isinstance(tariff, dict):
|
||||
raise CDEKMappingError("CDEK tariff entry must be an object.")
|
||||
return tariff
|
||||
tariffs: list[dict[str, Any]] = []
|
||||
for tariff in tariff_codes:
|
||||
if not isinstance(tariff, dict):
|
||||
raise CDEKMappingError("CDEK tariff entry must be an object.")
|
||||
tariffs.append(tariff)
|
||||
return tariffs
|
||||
|
||||
+23
-16
@@ -83,7 +83,7 @@ class AggregatorService:
|
||||
|
||||
provider_results = await asyncio.gather(
|
||||
*(
|
||||
self._get_provider_price(
|
||||
self._get_provider_prices(
|
||||
provider=provider,
|
||||
request=provider_request,
|
||||
cache_key=self._build_cache_key(
|
||||
@@ -98,8 +98,9 @@ class AggregatorService:
|
||||
|
||||
successful_results = [
|
||||
price
|
||||
for price in provider_results
|
||||
if isinstance(price, DeliveryPrice)
|
||||
for provider_prices in provider_results
|
||||
if isinstance(provider_prices, list)
|
||||
for price in provider_prices
|
||||
]
|
||||
provider_errors = [
|
||||
error
|
||||
@@ -142,26 +143,26 @@ class AggregatorService:
|
||||
from_attributes=True,
|
||||
)
|
||||
|
||||
async def _get_provider_price(
|
||||
async def _get_provider_prices(
|
||||
self,
|
||||
*,
|
||||
provider: DeliveryProvider,
|
||||
request: DeliveryRequest,
|
||||
cache_key: str,
|
||||
) -> DeliveryPrice:
|
||||
cached_price = await self._get_cached_price(cache_key)
|
||||
if cached_price is not None:
|
||||
return cached_price
|
||||
) -> list[DeliveryPrice]:
|
||||
cached_prices = await self._get_cached_prices(cache_key)
|
||||
if cached_prices is not None:
|
||||
return cached_prices
|
||||
|
||||
fresh_price = await provider.get_price(request)
|
||||
await self._set_cached_price(
|
||||
fresh_prices = await provider.get_prices(request)
|
||||
await self._set_cached_prices(
|
||||
cache_key,
|
||||
fresh_price,
|
||||
fresh_prices,
|
||||
ttl=getattr(provider, "cache_ttl_seconds", None),
|
||||
)
|
||||
return fresh_price
|
||||
return fresh_prices
|
||||
|
||||
async def _get_cached_price(self, cache_key: str) -> DeliveryPrice | None:
|
||||
async def _get_cached_prices(self, cache_key: str) -> list[DeliveryPrice] | None:
|
||||
if self._cache is None:
|
||||
return None
|
||||
try:
|
||||
@@ -171,14 +172,14 @@ class AggregatorService:
|
||||
if payload is None:
|
||||
return None
|
||||
try:
|
||||
return self._coerce_delivery_price(payload)
|
||||
return self._coerce_delivery_prices(payload)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
async def _set_cached_price(
|
||||
async def _set_cached_prices(
|
||||
self,
|
||||
cache_key: str,
|
||||
payload: DeliveryPrice,
|
||||
payload: list[DeliveryPrice],
|
||||
*,
|
||||
ttl: int | None,
|
||||
) -> None:
|
||||
@@ -227,3 +228,9 @@ class AggregatorService:
|
||||
@staticmethod
|
||||
def _coerce_delivery_price(value: object) -> DeliveryPrice:
|
||||
return DeliveryPrice.model_validate(value, from_attributes=True)
|
||||
|
||||
@classmethod
|
||||
def _coerce_delivery_prices(cls, value: object) -> list[DeliveryPrice]:
|
||||
if not isinstance(value, list):
|
||||
raise TypeError("Cached delivery prices must be a list.")
|
||||
return [cls._coerce_delivery_price(item) for item in value]
|
||||
|
||||
Reference in New Issue
Block a user