# services/forecasting/app/clients/inventory_client.py """ Simple client for inventory service integration Used when product names are not available locally """ import aiohttp import structlog from typing import Optional, Dict, Any import os logger = structlog.get_logger() class InventoryServiceClient: """Simple client for inventory service interactions""" def __init__(self, base_url: str = None): self.base_url = base_url or os.getenv("INVENTORY_SERVICE_URL", "http://inventory-service:8000") self.timeout = aiohttp.ClientTimeout(total=5) # 5 second timeout async def get_product_name(self, tenant_id: str, inventory_product_id: str) -> Optional[str]: """ Get product name from inventory service Returns None if service is unavailable or product not found """ try: async with aiohttp.ClientSession(timeout=self.timeout) as session: url = f"{self.base_url}/api/v1/products/{inventory_product_id}" headers = {"X-Tenant-ID": tenant_id} async with session.get(url, headers=headers) as response: if response.status == 200: data = await response.json() return data.get("name", f"Product-{inventory_product_id}") else: logger.debug("Product not found in inventory service", inventory_product_id=inventory_product_id, status=response.status) return None except Exception as e: logger.debug("Failed to get product name from inventory service", inventory_product_id=inventory_product_id, error=str(e)) return None async def get_multiple_product_names(self, tenant_id: str, product_ids: list) -> Dict[str, str]: """ Get multiple product names efficiently Returns a mapping of product_id -> product_name """ try: async with aiohttp.ClientSession(timeout=self.timeout) as session: url = f"{self.base_url}/api/v1/products/batch" headers = {"X-Tenant-ID": tenant_id} payload = {"product_ids": product_ids} async with session.post(url, json=payload, headers=headers) as response: if response.status == 200: data = await response.json() return {item["id"]: item["name"] for item in data.get("products", [])} else: logger.debug("Batch product lookup failed", product_count=len(product_ids), status=response.status) return {} except Exception as e: logger.debug("Failed to get product names from inventory service", product_count=len(product_ids), error=str(e)) return {} # Global client instance _inventory_client = None def get_inventory_client() -> InventoryServiceClient: """Get the global inventory client instance""" global _inventory_client if _inventory_client is None: _inventory_client = InventoryServiceClient() return _inventory_client