Fix bugs issues

This commit is contained in:
Urtzi Alfaro
2025-07-18 14:18:52 +02:00
parent 57ac772c6c
commit a469f0c01d
6 changed files with 288 additions and 305 deletions

View File

@@ -1,168 +1,42 @@
# ================================================================
# services/data/app/services/messaging.py
# ================================================================
"""Message queue service for data events"""
import json
import asyncio
from typing import Dict, Any, Optional
import structlog
try:
from aio_pika import connect_robust, Message, ExchangeType
AIO_PIKA_AVAILABLE = True
except ImportError:
AIO_PIKA_AVAILABLE = False
from shared.messaging.rabbitmq import RabbitMQClient
from app.core.config import settings
import logging
logger = structlog.get_logger()
logger = logging.getLogger(__name__)
class DataEventPublisher:
"""
Event publisher for data service events.
Falls back gracefully if RabbitMQ is not available.
"""
def __init__(self):
self.connection = None
self.channel = None
self.exchange = None
self.connected = False
async def connect(self):
"""Connect to RabbitMQ"""
if not AIO_PIKA_AVAILABLE:
logger.warning("aio-pika not available, messaging disabled")
return
try:
self.connection = await connect_robust(settings.RABBITMQ_URL)
self.channel = await self.connection.channel()
# Declare exchange for data events
self.exchange = await self.channel.declare_exchange(
"data.events",
ExchangeType.TOPIC,
durable=True
)
self.connected = True
logger.info("Connected to RabbitMQ for data events")
except Exception as e:
logger.warning("Failed to connect to RabbitMQ", error=str(e))
self.connected = False
async def disconnect(self):
"""Disconnect from RabbitMQ"""
if self.connection and not self.connection.is_closed:
await self.connection.close()
self.connected = False
logger.info("Disconnected from RabbitMQ")
async def publish_data_imported(self, event_data: Dict[str, Any]):
"""Publish data imported event"""
await self._publish_event("data.imported", event_data)
async def publish_weather_updated(self, event_data: Dict[str, Any]):
"""Publish weather data updated event"""
await self._publish_event("weather.updated", event_data)
async def publish_traffic_updated(self, event_data: Dict[str, Any]):
"""Publish traffic data updated event"""
await self._publish_event("traffic.updated", event_data)
async def publish_sales_created(self, event_data: Dict[str, Any]):
"""Publish sales record created event"""
await self._publish_event("sales.created", event_data)
async def publish_import_completed(self, event_data: Dict[str, Any]):
"""Publish import process completed event"""
await self._publish_event("import.completed", event_data)
async def _publish_event(self, routing_key: str, data: Dict[str, Any]):
"""Publish event to exchange"""
try:
# If not connected, try to connect
if not self.connected:
await self.connect()
# If still not connected, log and return
if not self.connected:
logger.debug("Message not sent - RabbitMQ unavailable", routing_key=routing_key)
return
# Prepare message
message_body = json.dumps(data, default=str)
message = Message(
message_body.encode(),
content_type="application/json",
delivery_mode=2 # Persistent
)
# Publish to exchange
await self.exchange.publish(
message,
routing_key=routing_key
)
logger.debug("Event published", routing_key=routing_key, data_size=len(message_body))
except Exception as e:
logger.error("Failed to publish event",
routing_key=routing_key,
error=str(e))
# Reset connection on error
self.connected = False
# Single global instance
data_publisher = RabbitMQClient(settings.RABBITMQ_URL, "data-service")
class MockDataEventPublisher:
"""
Mock publisher for development/testing when RabbitMQ is not available
"""
async def connect(self):
logger.info("Mock publisher - connect called")
async def disconnect(self):
logger.info("Mock publisher - disconnect called")
async def publish_data_imported(self, event_data: Dict[str, Any]):
logger.debug("Mock publish - data imported", event_data=event_data)
async def publish_weather_updated(self, event_data: Dict[str, Any]):
logger.debug("Mock publish - weather updated", event_data=event_data)
async def publish_traffic_updated(self, event_data: Dict[str, Any]):
logger.debug("Mock publish - traffic updated", event_data=event_data)
async def publish_sales_created(self, event_data: Dict[str, Any]):
logger.debug("Mock publish - sales created", event_data=event_data)
async def publish_import_completed(self, event_data: Dict[str, Any]):
logger.debug("Mock publish - import completed", event_data=event_data)
async def setup_messaging():
"""Initialize messaging for data service"""
success = await data_publisher.connect()
if success:
logger.info("Data service messaging initialized")
else:
logger.warning("Data service messaging failed to initialize")
# Global publisher instance
# Use mock if RabbitMQ is not available or in development mode
if AIO_PIKA_AVAILABLE and hasattr(settings, 'RABBITMQ_URL') and settings.RABBITMQ_URL:
data_publisher = DataEventPublisher()
else:
logger.info("Using mock data publisher")
data_publisher = MockDataEventPublisher()
# Ensure connection is established
async def init_messaging():
"""Initialize messaging connection"""
try:
await data_publisher.connect()
except Exception as e:
logger.warning("Failed to initialize messaging", error=str(e))
# Cleanup function
async def cleanup_messaging():
"""Cleanup messaging connection"""
try:
if hasattr(data_publisher, 'disconnect'):
await data_publisher.disconnect()
except Exception as e:
logger.warning("Failed to cleanup messaging", error=str(e))
"""Cleanup messaging for data service"""
await data_publisher.disconnect()
logger.info("Data service messaging cleaned up")
# Convenience functions for data-specific events
async def publish_data_imported(data: dict) -> bool:
"""Publish data imported event"""
return await data_publisher.publish_data_event("imported", data)
async def publish_weather_updated(data: dict) -> bool:
"""Publish weather updated event"""
return await data_publisher.publish_data_event("weather.updated", data)
async def publish_traffic_updated(data: dict) -> bool:
"""Publish traffic updated event"""
return await data_publisher.publish_data_event("traffic.updated", data)
async def publish_sales_created(data: dict) -> bool:
"""Publish sales created event"""
return await data_publisher.publish_data_event("sales.created", data)