016 add creating order to adapter

This commit is contained in:
Раис Юсупалиев
2026-03-14 03:54:55 +03:00
parent 66beab4682
commit 15a17be4ed
12 changed files with 607 additions and 12 deletions
@@ -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)