87 lines
3.5 KiB
Python
87 lines
3.5 KiB
Python
"""Controller for provider payload optimization and forwarding."""
|
|
|
|
import logging
|
|
import hashlib
|
|
import json
|
|
from typing import Dict, Any
|
|
import httpx
|
|
from fastapi import HTTPException
|
|
|
|
from app.core.exceptions import ValidationError, APIException
|
|
from app.services.routing.route_optimizer import RouteOptimizer
|
|
from app.services import cache
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class RouteController:
|
|
"""Controller for optimizing provider payloads and forwarding upstream."""
|
|
|
|
def __init__(self):
|
|
self.route_optimizer = RouteOptimizer()
|
|
|
|
def _hash_key(self, prefix: str, payload: Dict[str, Any]) -> str:
|
|
"""Create a stable cache key from a dict payload."""
|
|
# ensure deterministic json by sorting keys
|
|
serialized = json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
|
digest = hashlib.sha256(serialized.encode("utf-8")).hexdigest()
|
|
return f"routes:{prefix}:{digest}"
|
|
|
|
async def optimize_and_forward_provider_payload(self, orders: list[dict], forward_url: str) -> dict:
|
|
"""Optimize provider payload and return it (forwarding paused).
|
|
|
|
- Input: list of provider orders (dicts)
|
|
- Output: {code, details, message, status} where details is the optimized array
|
|
"""
|
|
try:
|
|
if not isinstance(orders, list) or not orders:
|
|
raise ValidationError("Orders array is required", field="body")
|
|
|
|
optimized = await self.route_optimizer.optimize_provider_payload(orders)
|
|
|
|
# Debug sample of optimized payload (first 3 items, select keys)
|
|
try:
|
|
sample = [
|
|
{
|
|
k: item.get(k)
|
|
for k in ("orderheaderid", "orderid", "deliverycustomerid", "step", "previouskms", "cumulativekms", "eta")
|
|
}
|
|
for item in optimized[:3]
|
|
]
|
|
logger.debug(f"Optimized payload sample: {sample}")
|
|
trace = [
|
|
{
|
|
"orderid": item.get("orderid"),
|
|
"step": item.get("step"),
|
|
"prev": item.get("previouskms"),
|
|
"cum": item.get("cumulativekms"),
|
|
}
|
|
for item in optimized
|
|
]
|
|
logger.debug(f"Optimized order trace: {trace}")
|
|
except Exception:
|
|
logger.debug("Optimized payload sample logging failed")
|
|
|
|
# Forwarding paused: return optimized payload directly
|
|
return {
|
|
"code": 200,
|
|
"details": optimized,
|
|
"message": "Success",
|
|
"status": True,
|
|
}
|
|
except ValidationError:
|
|
raise
|
|
except httpx.HTTPStatusError as e:
|
|
status_code = e.response.status_code
|
|
body_text = e.response.text
|
|
logger.error(f"Forwarding failed: {status_code} - {body_text}")
|
|
# Surface upstream details to the client for faster debugging
|
|
raise APIException(
|
|
status_code=502,
|
|
message=f"Upstream service error (status {status_code}): {body_text}",
|
|
code="UPSTREAM_ERROR"
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error optimizing/forwarding provider payload: {e}", exc_info=True)
|
|
raise APIException(status_code=500, message="Internal server error", code="INTERNAL_ERROR")
|
|
# Batch routes removed - use single-route optimization for each pickup location |