fix country code

This commit is contained in:
Раис Юсупалиев
2026-03-09 15:24:10 +03:00
parent 2d68aff34d
commit da301d4bc4
8 changed files with 155 additions and 45 deletions
+30 -11
View File
@@ -48,8 +48,8 @@ class CDEKClient:
self._http_client = http_client
self._auth_client = auth_client
normalized_base_url = base_url.rstrip("/")
self._city_lookup_url = f"{normalized_base_url}/location/cities"
self._tariff_url = f"{base_url.rstrip('/')}/calculator/tarifflist"
self._city_lookup_url = f"{normalized_base_url}/location/suggest/cities"
self._tariff_url = f"{normalized_base_url}/calculator/tarifflist"
self._timeout_seconds = timeout_seconds
self._retry_attempts = retry_attempts
self._retry_backoff_seconds = retry_backoff_seconds
@@ -103,8 +103,14 @@ class CDEKClient:
return status_code == 429 or status_code >= 500
async def _build_payload(self, request: DeliveryRequest) -> dict[str, Any]:
from_city_code = await self._resolve_city_code(request.from_city)
to_city_code = await self._resolve_city_code(request.to_city)
from_city_code = await self._resolve_city_code(
request.from_city,
country_code=request.country_code,
)
to_city_code = await self._resolve_city_code(
request.to_city,
country_code=request.country_code,
)
return {
"from_location": {"code": from_city_code},
"to_location": {"code": to_city_code},
@@ -118,21 +124,29 @@ class CDEKClient:
],
}
async def _resolve_city_code(self, city: str) -> int:
async def _resolve_city_code(self, city: str, *, country_code: str | None) -> int:
normalized_city = city.strip().casefold()
cached_code = self._city_code_cache.get(normalized_city)
normalized_country_code = (
country_code.strip().upper() if country_code is not None else ""
)
cache_key = f"{normalized_country_code}:{normalized_city}"
cached_code = self._city_code_cache.get(cache_key)
if cached_code is not None:
return cached_code
for attempt in range(self._retry_attempts + 1):
try:
token = await self._auth_client.get_access_token()
params = {"name": city}
if normalized_country_code:
params["country_code"] = normalized_country_code
response = await self._http_client.get(
self._city_lookup_url,
params={"city": city, "country_codes": "RU", "size": 1},
params=params,
headers={"Authorization": f"Bearer {token}"},
timeout=self._timeout_seconds,
)
log.info(response.json())
except (httpx.TimeoutException, httpx.TransportError) as exc:
if attempt < self._retry_attempts:
await self._sleep(self._retry_delay(attempt))
@@ -148,7 +162,6 @@ class CDEKClient:
raise CDEKClientError(
f"CDEK city lookup failed with status {response.status_code}."
)
log.info(response.json())
try:
response.raise_for_status()
body = response.json()
@@ -158,11 +171,17 @@ class CDEKClient:
else:
raise CDEKClientError("CDEK city lookup failed unexpectedly.")
if not isinstance(body, list) or not body:
if isinstance(body, dict):
first_item = body
elif isinstance(body, list) and body:
first_item = body[0]
elif isinstance(body, list):
raise CDEKRequestError(
f"CDEK city lookup returned no matches for '{city}'."
)
first_item = body[0]
else:
raise CDEKClientError("CDEK city lookup response must be an object or array.")
if not isinstance(first_item, dict):
raise CDEKClientError("CDEK city lookup response entry must be an object.")
raw_city_code = first_item.get("code")
@@ -173,7 +192,7 @@ class CDEKClient:
except (TypeError, ValueError) as exc:
raise CDEKClientError("CDEK city lookup response city code is invalid.") from exc
self._city_code_cache[normalized_city] = city_code
self._city_code_cache[cache_key] = city_code
return city_code
+12
View File
@@ -18,6 +18,7 @@ class RequestLike(Protocol):
entity: object
from_city: object
to_city: object
country_code: object
weight_kg: object
length_cm: object
width_cm: object
@@ -29,6 +30,7 @@ class NormalizedDeliveryRequest:
entity: str
from_city: str
to_city: str
country_code: str | None
weight_kg: Decimal
length_cm: Decimal
width_cm: Decimal
@@ -58,6 +60,7 @@ def normalize_delivery_request(
entity=str(_get_required_attr(request, "entity")),
from_city=_normalize_text(_get_required_attr(request, "from_city")),
to_city=_normalize_text(_get_required_attr(request, "to_city")),
country_code=_normalize_country_code(_get_optional_attr(request, "country_code")),
weight_kg=_normalize_weight(
_to_decimal(_get_required_attr(request, "weight_kg")),
normalized_weight_scale,
@@ -167,6 +170,15 @@ def _normalize_text(value: object) -> str:
return str(value).strip()
def _normalize_country_code(value: object) -> str | None:
if value is _MISSING or value is None:
return None
normalized = str(value).strip().upper()
if not normalized:
return None
return normalized
def _clamp_scale(scale: int) -> int:
if scale < 0:
return 0
+1
View File
@@ -14,6 +14,7 @@ class DeliveryRequest(BaseModel):
entity: DeliveryEntity
from_city: str = Field(min_length=1)
to_city: str = Field(min_length=1)
country_code: str | None = None
weight_kg: float = Field(gt=0)
length_cm: float = Field(gt=0)
width_cm: float = Field(gt=0)
+2
View File
@@ -142,6 +142,7 @@ class AggregatorService:
entity=request.entity,
from_city=request.from_city,
to_city=request.to_city,
country_code=request.country_code,
weight_kg=request.weight_kg,
length_cm=request.length_cm,
width_cm=request.width_cm,
@@ -155,6 +156,7 @@ class AggregatorService:
"entity": request.entity,
"from_city": request.from_city,
"to_city": request.to_city,
"country_code": request.country_code,
"weight_kg": str(request.weight_kg),
"length_cm": str(request.length_cm),
"width_cm": str(request.width_cm),