@@ -1,7 +1,9 @@
|
||||
"""Base interface for delivery providers."""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
from app.schemas.payment import InitPaymentRequest
|
||||
from app.schemas.request import DeliveryCalculationRequest
|
||||
from app.schemas.response import DeliveryPrice
|
||||
|
||||
@@ -18,6 +20,27 @@ class DeliveryProvider(ABC):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class PaymentPriceValidationProvider(Protocol):
|
||||
name: str
|
||||
|
||||
async def get_payment_price(
|
||||
self,
|
||||
request: InitPaymentRequest,
|
||||
) -> DeliveryPrice | None: ...
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class OrderRegistrationProvider(Protocol):
|
||||
name: str
|
||||
|
||||
async def register_order(
|
||||
self,
|
||||
request: InitPaymentRequest,
|
||||
order_uuid: str,
|
||||
) -> object: ...
|
||||
|
||||
|
||||
class ProviderClientError(RuntimeError):
|
||||
"""Raised when a provider call fails for temporary or provider-side reasons."""
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ from app.adapters.delivery_providers.cdek.order_mapper import (
|
||||
resolve_cdek_city_code,
|
||||
)
|
||||
from app.cities import cities_map
|
||||
from app.config import AdapterConfig
|
||||
from app.config import CDEKDeliveryProviderConfig
|
||||
from app.schemas.payment import InitPaymentRequest
|
||||
from app.schemas.request import DeliveryCalculationRequest
|
||||
from app.schemas.response import DeliveryPrice
|
||||
@@ -452,28 +452,28 @@ class CDEKProvider(DeliveryProvider):
|
||||
self.cache_ttl_seconds = cache_ttl_seconds
|
||||
|
||||
@classmethod
|
||||
def from_adapter_config(
|
||||
def from_config(
|
||||
cls,
|
||||
*,
|
||||
http_client: httpx.AsyncClient,
|
||||
adapter_config: AdapterConfig,
|
||||
config: CDEKDeliveryProviderConfig,
|
||||
) -> "CDEKProvider":
|
||||
auth_client = CDEKAuthClient(
|
||||
http_client=http_client,
|
||||
base_url=adapter_config.cdek_base_url,
|
||||
client_id=adapter_config.cdek_client_id,
|
||||
client_secret=adapter_config.cdek_client_secret,
|
||||
timeout_seconds=adapter_config.cdek_timeout_seconds,
|
||||
base_url=config.base_url,
|
||||
client_id=config.client_id,
|
||||
client_secret=config.client_secret,
|
||||
timeout_seconds=config.timeout_seconds,
|
||||
)
|
||||
client = CDEKClient(
|
||||
http_client=http_client,
|
||||
auth_client=auth_client,
|
||||
base_url=adapter_config.cdek_base_url,
|
||||
timeout_seconds=adapter_config.cdek_timeout_seconds,
|
||||
retry_attempts=adapter_config.cdek_retry_attempts,
|
||||
retry_backoff_seconds=adapter_config.cdek_retry_backoff_seconds,
|
||||
base_url=config.base_url,
|
||||
timeout_seconds=config.timeout_seconds,
|
||||
retry_attempts=config.retry_attempts,
|
||||
retry_backoff_seconds=config.retry_backoff_seconds,
|
||||
)
|
||||
return cls(client=client, cache_ttl_seconds=adapter_config.cdek_cache_ttl_seconds)
|
||||
return cls(client=client, cache_ttl_seconds=config.cache_ttl_seconds)
|
||||
|
||||
async def get_prices(
|
||||
self, request: DeliveryCalculationRequest
|
||||
|
||||
@@ -35,7 +35,7 @@ from app.adapters.delivery_providers.cse.soap import (
|
||||
make_field,
|
||||
parse_response,
|
||||
)
|
||||
from app.config import AdapterConfig
|
||||
from app.config import CSEDeliveryProviderConfig
|
||||
from app.schemas.payment import InitPaymentRequest
|
||||
from app.schemas.request import DeliveryCalculationRequest
|
||||
from app.schemas.response import DeliveryPrice
|
||||
@@ -212,27 +212,27 @@ class CSEProvider(DeliveryProvider):
|
||||
self._delivery_types: list[tuple[str, str]] | None = None
|
||||
|
||||
@classmethod
|
||||
def from_adapter_config(
|
||||
def from_config(
|
||||
cls,
|
||||
*,
|
||||
http_client: httpx.AsyncClient,
|
||||
adapter_config: AdapterConfig,
|
||||
config: CSEDeliveryProviderConfig,
|
||||
) -> "CSEProvider":
|
||||
client = CSEClient(
|
||||
http_client=http_client,
|
||||
base_url=adapter_config.cse_base_url,
|
||||
login=adapter_config.cse_login,
|
||||
password=adapter_config.cse_password,
|
||||
base_url=config.base_url,
|
||||
login=config.login,
|
||||
password=config.password,
|
||||
registration_params=CSEOrderRegistrationParams(
|
||||
payer=adapter_config.cse_payer,
|
||||
payment_method=adapter_config.cse_payment_method,
|
||||
shipping_method=adapter_config.cse_shipping_method,
|
||||
payer=config.payer,
|
||||
payment_method=config.payment_method,
|
||||
shipping_method=config.shipping_method,
|
||||
),
|
||||
timeout_seconds=adapter_config.cse_timeout_seconds,
|
||||
retry_attempts=adapter_config.cse_retry_attempts,
|
||||
retry_backoff_seconds=adapter_config.cse_retry_backoff_seconds,
|
||||
timeout_seconds=config.timeout_seconds,
|
||||
retry_attempts=config.retry_attempts,
|
||||
retry_backoff_seconds=config.retry_backoff_seconds,
|
||||
)
|
||||
return cls(client=client, cache_ttl_seconds=adapter_config.cse_cache_ttl_seconds)
|
||||
return cls(client=client, cache_ttl_seconds=config.cache_ttl_seconds)
|
||||
|
||||
async def get_prices(
|
||||
self, request: DeliveryCalculationRequest
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Delivery provider registry and wiring."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Mapping, Sequence
|
||||
|
||||
import httpx
|
||||
|
||||
from app.adapters.delivery_providers.base import (
|
||||
DeliveryProvider,
|
||||
OrderRegistrationProvider,
|
||||
PaymentPriceValidationProvider,
|
||||
)
|
||||
from app.adapters.delivery_providers.cdek import CDEKProvider
|
||||
from app.adapters.delivery_providers.cse import CSEProvider
|
||||
from app.config import DeliveryProvidersConfig
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class DeliveryProviderRegistry:
|
||||
providers: Sequence[DeliveryProvider]
|
||||
payment_price_validation_adapters: Mapping[str, PaymentPriceValidationProvider]
|
||||
order_registration_adapters: Mapping[str, OrderRegistrationProvider]
|
||||
|
||||
|
||||
def resolve_delivery_provider_timeout_seconds(
|
||||
config: DeliveryProvidersConfig,
|
||||
) -> float:
|
||||
timeouts = [
|
||||
provider_config.timeout_seconds
|
||||
for provider_config in (config.cdek, config.cse)
|
||||
if provider_config.enabled
|
||||
]
|
||||
return max(timeouts, default=10.0)
|
||||
|
||||
|
||||
def build_delivery_provider_registry(
|
||||
*,
|
||||
http_client: httpx.AsyncClient,
|
||||
config: DeliveryProvidersConfig,
|
||||
) -> DeliveryProviderRegistry:
|
||||
providers: list[DeliveryProvider] = []
|
||||
payment_price_validation_adapters: dict[str, PaymentPriceValidationProvider] = {}
|
||||
order_registration_adapters: dict[str, OrderRegistrationProvider] = {}
|
||||
|
||||
if config.cdek.enabled:
|
||||
cdek_provider = CDEKProvider.from_config(
|
||||
http_client=http_client,
|
||||
config=config.cdek,
|
||||
)
|
||||
providers.append(cdek_provider)
|
||||
payment_price_validation_adapters[cdek_provider.name] = cdek_provider
|
||||
order_registration_adapters[cdek_provider.name] = cdek_provider
|
||||
|
||||
if config.cse.enabled:
|
||||
cse_provider = CSEProvider.from_config(
|
||||
http_client=http_client,
|
||||
config=config.cse,
|
||||
)
|
||||
providers.append(cse_provider)
|
||||
payment_price_validation_adapters[cse_provider.name] = cse_provider
|
||||
order_registration_adapters[cse_provider.name] = cse_provider
|
||||
|
||||
return DeliveryProviderRegistry(
|
||||
providers=tuple(providers),
|
||||
payment_price_validation_adapters=payment_price_validation_adapters,
|
||||
order_registration_adapters=order_registration_adapters,
|
||||
)
|
||||
Reference in New Issue
Block a user