016 add creating order to adapter
This commit is contained in:
@@ -10,7 +10,13 @@ import httpx
|
||||
from app.adapters.delivery_providers.base import DeliveryProvider, ProviderRequestError
|
||||
from app.adapters.delivery_providers.cdek.auth import CDEKAuthClient
|
||||
from app.adapters.delivery_providers.cdek.mapper import map_cdek_response
|
||||
from app.adapters.delivery_providers.cdek.order_mapper import (
|
||||
CDEKOrderMappingError,
|
||||
map_cdek_order_request,
|
||||
map_cdek_order_response,
|
||||
)
|
||||
from app.config import AdapterConfig
|
||||
from app.schemas.order import OrderCreateRequest, OrderCreateResponse
|
||||
from app.schemas.request import DeliveryRequest
|
||||
from app.schemas.response import DeliveryPrice
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -41,6 +47,7 @@ class CDEKClient:
|
||||
normalized_base_url = base_url.rstrip("/")
|
||||
self._city_lookup_url = f"{normalized_base_url}/location/suggest/cities"
|
||||
self._tariff_url = f"{normalized_base_url}/calculator/tarifflist"
|
||||
self._orders_url = f"{normalized_base_url}/orders"
|
||||
self._timeout_seconds = timeout_seconds
|
||||
self._retry_attempts = retry_attempts
|
||||
self._retry_backoff_seconds = retry_backoff_seconds
|
||||
@@ -86,6 +93,62 @@ class CDEKClient:
|
||||
|
||||
raise CDEKClientError("CDEK tariff request failed unexpectedly.")
|
||||
|
||||
async def register_order(self, request: OrderCreateRequest) -> OrderCreateResponse:
|
||||
payload = map_cdek_order_request(request)
|
||||
for attempt in range(self._retry_attempts + 1):
|
||||
try:
|
||||
token = await self._auth_client.get_access_token()
|
||||
response = await self._http_client.post(
|
||||
self._orders_url,
|
||||
json=payload,
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=self._timeout_seconds,
|
||||
)
|
||||
except (httpx.TimeoutException, httpx.TransportError) as exc:
|
||||
if attempt < self._retry_attempts:
|
||||
await self._sleep(self._retry_delay(attempt))
|
||||
continue
|
||||
raise CDEKClientError(
|
||||
"CDEK order registration failed after retry attempts."
|
||||
) from exc
|
||||
|
||||
if self._should_retry(response.status_code):
|
||||
if attempt < self._retry_attempts:
|
||||
await self._sleep(self._retry_delay(attempt))
|
||||
continue
|
||||
raise CDEKClientError(
|
||||
"CDEK order registration failed with retriable status "
|
||||
f"{response.status_code}."
|
||||
)
|
||||
|
||||
if 400 <= response.status_code < 500:
|
||||
raise CDEKRequestError(
|
||||
"CDEK order registration request was rejected with status "
|
||||
f"{response.status_code}."
|
||||
)
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
raw_payload = response.json()
|
||||
except (httpx.HTTPError, TypeError, ValueError) as exc:
|
||||
raise CDEKClientError(
|
||||
"CDEK order registration returned invalid payload."
|
||||
) from exc
|
||||
|
||||
if not isinstance(raw_payload, dict):
|
||||
raise CDEKClientError(
|
||||
"CDEK order registration payload must be a JSON object."
|
||||
)
|
||||
|
||||
try:
|
||||
return map_cdek_order_response(raw_payload)
|
||||
except CDEKOrderMappingError as exc:
|
||||
raise CDEKClientError(
|
||||
"CDEK order registration response payload is invalid."
|
||||
) from exc
|
||||
|
||||
raise CDEKClientError("CDEK order registration failed unexpectedly.")
|
||||
|
||||
def _retry_delay(self, attempt: int) -> float:
|
||||
return self._retry_backoff_seconds * (attempt + 1)
|
||||
|
||||
@@ -221,3 +284,6 @@ class CDEKProvider(DeliveryProvider):
|
||||
async def get_price(self, request: DeliveryRequest) -> DeliveryPrice:
|
||||
raw_payload = await self._client.get_raw_price(request)
|
||||
return map_cdek_response(raw_payload)
|
||||
|
||||
async def register_order(self, request: OrderCreateRequest) -> OrderCreateResponse:
|
||||
return await self._client.register_order(request)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"""CDEK order registration payload mappers."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from app.schemas.order import OrderCreateRequest, OrderCreateResponse
|
||||
|
||||
|
||||
class CDEKOrderMappingError(ValueError):
|
||||
"""Raised when CDEK order payload cannot be mapped."""
|
||||
|
||||
|
||||
def map_cdek_order_request(request: OrderCreateRequest) -> dict[str, Any]:
|
||||
return request.model_dump(mode="python", exclude_none=True)
|
||||
|
||||
|
||||
def map_cdek_order_response(payload: dict[str, Any]) -> OrderCreateResponse:
|
||||
entity = payload.get("entity")
|
||||
if not isinstance(entity, dict):
|
||||
raise CDEKOrderMappingError("CDEK order response must include entity object.")
|
||||
|
||||
order_uuid = entity.get("uuid")
|
||||
if not isinstance(order_uuid, str) or not order_uuid:
|
||||
raise CDEKOrderMappingError("CDEK order response must include entity.uuid.")
|
||||
|
||||
return OrderCreateResponse(provider="cdek", order_uuid=order_uuid)
|
||||
Reference in New Issue
Block a user