Fix some issues

This commit is contained in:
2026-01-25 20:07:37 +01:00
parent e0be1b22f9
commit 6c6a9fc58c
32 changed files with 1719 additions and 226 deletions

View File

@@ -0,0 +1,545 @@
# ================================================================
# services/notification/app/api/public_contact.py
# ================================================================
"""
Public contact form API endpoints
Handles contact, feedback, and prelaunch email submissions
These endpoints are public (no authentication required)
"""
import structlog
from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, Literal
from datetime import datetime
from app.services.email_service import EmailService
from app.core.config import settings
logger = structlog.get_logger()
router = APIRouter(prefix="/api/v1/public", tags=["public-contact"])
# ================================================================
# PYDANTIC MODELS
# ================================================================
class ContactFormRequest(BaseModel):
"""Contact form submission request"""
name: str = Field(..., min_length=2, max_length=100)
email: EmailStr
phone: Optional[str] = Field(None, max_length=20)
bakery_name: Optional[str] = Field(None, max_length=100)
type: Literal["general", "technical", "sales", "feedback"] = "general"
subject: str = Field(..., min_length=5, max_length=200)
message: str = Field(..., min_length=10, max_length=5000)
class FeedbackFormRequest(BaseModel):
"""Feedback form submission request"""
name: str = Field(..., min_length=2, max_length=100)
email: EmailStr
category: Literal["suggestion", "bug", "feature", "praise", "complaint"]
title: str = Field(..., min_length=5, max_length=200)
description: str = Field(..., min_length=10, max_length=5000)
rating: Optional[int] = Field(None, ge=1, le=5)
class PrelaunchEmailRequest(BaseModel):
"""Prelaunch email subscription request"""
email: EmailStr
class ContactFormResponse(BaseModel):
"""Response for form submissions"""
success: bool
message: str
# ================================================================
# EMAIL TEMPLATES
# ================================================================
CONTACT_EMAIL_TEMPLATE_TEXT = """
Nuevo mensaje de contacto recibido
==============================================
DATOS DEL CONTACTO
==============================================
Nombre: {name}
Email: {email}
Teléfono: {phone}
Panadería: {bakery_name}
Tipo de consulta: {type_label}
==============================================
ASUNTO
==============================================
{subject}
==============================================
MENSAJE
==============================================
{message}
==============================================
Recibido: {timestamp}
"""
CONTACT_EMAIL_TEMPLATE_HTML = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; background-color: #f5f5f5; }}
.container {{ max-width: 600px; margin: 0 auto; background-color: #ffffff; }}
.header {{ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); padding: 30px; text-align: center; }}
.header h1 {{ color: white; margin: 0; font-size: 24px; }}
.content {{ padding: 30px; }}
.info-box {{ background-color: #fef3c7; border-left: 4px solid #f59e0b; padding: 15px; margin: 20px 0; }}
.info-row {{ display: flex; margin-bottom: 10px; }}
.info-label {{ font-weight: bold; color: #92400e; min-width: 120px; }}
.info-value {{ color: #374151; }}
.message-box {{ background-color: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; margin: 20px 0; }}
.message-box h3 {{ color: #1f2937; margin-top: 0; }}
.footer {{ background-color: #f3f4f6; padding: 20px; text-align: center; font-size: 12px; color: #6b7280; }}
.type-badge {{ display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; }}
.type-general {{ background-color: #dbeafe; color: #1e40af; }}
.type-technical {{ background-color: #fce7f3; color: #9d174d; }}
.type-sales {{ background-color: #d1fae5; color: #065f46; }}
.type-feedback {{ background-color: #e0e7ff; color: #3730a3; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Nuevo Mensaje de Contacto</h1>
</div>
<div class="content">
<div class="info-box">
<div class="info-row">
<span class="info-label">Nombre:</span>
<span class="info-value">{name}</span>
</div>
<div class="info-row">
<span class="info-label">Email:</span>
<span class="info-value"><a href="mailto:{email}">{email}</a></span>
</div>
<div class="info-row">
<span class="info-label">Teléfono:</span>
<span class="info-value">{phone}</span>
</div>
<div class="info-row">
<span class="info-label">Panadería:</span>
<span class="info-value">{bakery_name}</span>
</div>
<div class="info-row">
<span class="info-label">Tipo:</span>
<span class="info-value"><span class="type-badge type-{type}">{type_label}</span></span>
</div>
</div>
<div class="message-box">
<h3>{subject}</h3>
<p style="white-space: pre-wrap; color: #374151;">{message}</p>
</div>
</div>
<div class="footer">
Recibido: {timestamp}<br>
BakeWise - Sistema de Contacto
</div>
</div>
</body>
</html>
"""
FEEDBACK_EMAIL_TEMPLATE_TEXT = """
Nuevo feedback recibido
==============================================
DATOS DEL USUARIO
==============================================
Nombre: {name}
Email: {email}
Categoría: {category_label}
Valoración: {rating_display}
==============================================
TÍTULO
==============================================
{title}
==============================================
DESCRIPCIÓN
==============================================
{description}
==============================================
Recibido: {timestamp}
"""
FEEDBACK_EMAIL_TEMPLATE_HTML = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; background-color: #f5f5f5; }}
.container {{ max-width: 600px; margin: 0 auto; background-color: #ffffff; }}
.header {{ background: linear-gradient(135deg, #8b5cf6 0%, #6d28d9 100%); padding: 30px; text-align: center; }}
.header h1 {{ color: white; margin: 0; font-size: 24px; }}
.content {{ padding: 30px; }}
.info-box {{ background-color: #ede9fe; border-left: 4px solid #8b5cf6; padding: 15px; margin: 20px 0; }}
.info-row {{ display: flex; margin-bottom: 10px; }}
.info-label {{ font-weight: bold; color: #5b21b6; min-width: 120px; }}
.info-value {{ color: #374151; }}
.message-box {{ background-color: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; margin: 20px 0; }}
.message-box h3 {{ color: #1f2937; margin-top: 0; }}
.footer {{ background-color: #f3f4f6; padding: 20px; text-align: center; font-size: 12px; color: #6b7280; }}
.category-badge {{ display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; }}
.category-suggestion {{ background-color: #dbeafe; color: #1e40af; }}
.category-bug {{ background-color: #fee2e2; color: #991b1b; }}
.category-feature {{ background-color: #d1fae5; color: #065f46; }}
.category-praise {{ background-color: #fef3c7; color: #92400e; }}
.category-complaint {{ background-color: #fce7f3; color: #9d174d; }}
.stars {{ color: #f59e0b; font-size: 18px; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Nuevo Feedback Recibido</h1>
</div>
<div class="content">
<div class="info-box">
<div class="info-row">
<span class="info-label">Nombre:</span>
<span class="info-value">{name}</span>
</div>
<div class="info-row">
<span class="info-label">Email:</span>
<span class="info-value"><a href="mailto:{email}">{email}</a></span>
</div>
<div class="info-row">
<span class="info-label">Categoría:</span>
<span class="info-value"><span class="category-badge category-{category}">{category_label}</span></span>
</div>
<div class="info-row">
<span class="info-label">Valoración:</span>
<span class="info-value stars">{rating_display}</span>
</div>
</div>
<div class="message-box">
<h3>{title}</h3>
<p style="white-space: pre-wrap; color: #374151;">{description}</p>
</div>
</div>
<div class="footer">
Recibido: {timestamp}<br>
BakeWise - Sistema de Feedback
</div>
</div>
</body>
</html>
"""
PRELAUNCH_EMAIL_TEMPLATE_TEXT = """
Nueva suscripción de pre-lanzamiento
==============================================
NUEVO INTERESADO
==============================================
Email: {email}
El usuario ha mostrado interés en BakeWise y quiere ser notificado cuando se lance oficialmente.
==============================================
Recibido: {timestamp}
"""
PRELAUNCH_EMAIL_TEMPLATE_HTML = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; background-color: #f5f5f5; }}
.container {{ max-width: 600px; margin: 0 auto; background-color: #ffffff; }}
.header {{ background: linear-gradient(135deg, #10b981 0%, #059669 100%); padding: 30px; text-align: center; }}
.header h1 {{ color: white; margin: 0; font-size: 24px; }}
.content {{ padding: 30px; text-align: center; }}
.email-box {{ background-color: #d1fae5; border: 2px solid #10b981; border-radius: 12px; padding: 20px; margin: 20px 0; }}
.email-box h2 {{ color: #065f46; margin: 0 0 10px 0; }}
.email-box a {{ color: #059669; font-size: 18px; font-weight: bold; }}
.footer {{ background-color: #f3f4f6; padding: 20px; text-align: center; font-size: 12px; color: #6b7280; }}
.rocket {{ font-size: 48px; margin-bottom: 10px; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Nueva Suscripción Pre-Lanzamiento</h1>
</div>
<div class="content">
<div class="rocket">🚀</div>
<p style="color: #374151; font-size: 16px;">Un nuevo usuario quiere ser notificado del lanzamiento de BakeWise</p>
<div class="email-box">
<h2>Email del interesado:</h2>
<a href="mailto:{email}">{email}</a>
</div>
<p style="color: #6b7280;">Añade este email a la lista de espera para el lanzamiento oficial.</p>
</div>
<div class="footer">
Recibido: {timestamp}<br>
BakeWise - Lista de Espera
</div>
</div>
</body>
</html>
"""
# ================================================================
# HELPER FUNCTIONS
# ================================================================
def get_type_label(type_code: str) -> str:
"""Get human-readable label for contact type"""
labels = {
"general": "Consulta General",
"technical": "Soporte Técnico",
"sales": "Ventas",
"feedback": "Feedback"
}
return labels.get(type_code, type_code)
def get_category_label(category: str) -> str:
"""Get human-readable label for feedback category"""
labels = {
"suggestion": "Sugerencia",
"bug": "Error/Bug",
"feature": "Nueva Funcionalidad",
"praise": "Elogio",
"complaint": "Queja"
}
return labels.get(category, category)
def get_rating_display(rating: Optional[int]) -> str:
"""Convert rating to star display"""
if rating is None:
return "N/A"
return "" * rating + "" * (5 - rating)
# ================================================================
# API ENDPOINTS
# ================================================================
@router.post("/contact", response_model=ContactFormResponse)
async def submit_contact_form(request: ContactFormRequest):
"""
Submit a contact form message.
Sends an email to contact@bakewise.ai with the form data.
"""
try:
email_service = EmailService()
timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
type_label = get_type_label(request.type)
# Format email content
text_content = CONTACT_EMAIL_TEMPLATE_TEXT.format(
name=request.name,
email=request.email,
phone=request.phone or "No proporcionado",
bakery_name=request.bakery_name or "No proporcionado",
type=request.type,
type_label=type_label,
subject=request.subject,
message=request.message,
timestamp=timestamp
)
html_content = CONTACT_EMAIL_TEMPLATE_HTML.format(
name=request.name,
email=request.email,
phone=request.phone or "No proporcionado",
bakery_name=request.bakery_name or "No proporcionado",
type=request.type,
type_label=type_label,
subject=request.subject,
message=request.message,
timestamp=timestamp
)
# Send email to contact@bakewise.ai
success = await email_service.send_email(
to_email="contact@bakewise.ai",
subject=f"[{type_label}] {request.subject}",
text_content=text_content,
html_content=html_content,
reply_to=request.email
)
if success:
logger.info("Contact form submitted successfully",
from_email=request.email,
type=request.type,
subject=request.subject)
return ContactFormResponse(
success=True,
message="Mensaje enviado correctamente. Te responderemos pronto."
)
else:
logger.error("Failed to send contact form email",
from_email=request.email)
raise HTTPException(
status_code=500,
detail="Error al enviar el mensaje. Por favor, inténtalo de nuevo."
)
except HTTPException:
raise
except Exception as e:
logger.error("Contact form submission error", error=str(e))
raise HTTPException(
status_code=500,
detail="Error interno. Por favor, inténtalo más tarde."
)
@router.post("/feedback", response_model=ContactFormResponse)
async def submit_feedback_form(request: FeedbackFormRequest):
"""
Submit a feedback form.
Sends an email to contact@bakewise.ai with the feedback data.
"""
try:
email_service = EmailService()
timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
category_label = get_category_label(request.category)
rating_display = get_rating_display(request.rating)
# Format email content
text_content = FEEDBACK_EMAIL_TEMPLATE_TEXT.format(
name=request.name,
email=request.email,
category=request.category,
category_label=category_label,
rating_display=rating_display,
title=request.title,
description=request.description,
timestamp=timestamp
)
html_content = FEEDBACK_EMAIL_TEMPLATE_HTML.format(
name=request.name,
email=request.email,
category=request.category,
category_label=category_label,
rating_display=rating_display,
title=request.title,
description=request.description,
timestamp=timestamp
)
# Send email to contact@bakewise.ai
success = await email_service.send_email(
to_email="contact@bakewise.ai",
subject=f"[Feedback - {category_label}] {request.title}",
text_content=text_content,
html_content=html_content,
reply_to=request.email
)
if success:
logger.info("Feedback form submitted successfully",
from_email=request.email,
category=request.category,
title=request.title)
return ContactFormResponse(
success=True,
message="Gracias por tu feedback. Lo revisaremos pronto."
)
else:
logger.error("Failed to send feedback form email",
from_email=request.email)
raise HTTPException(
status_code=500,
detail="Error al enviar el feedback. Por favor, inténtalo de nuevo."
)
except HTTPException:
raise
except Exception as e:
logger.error("Feedback form submission error", error=str(e))
raise HTTPException(
status_code=500,
detail="Error interno. Por favor, inténtalo más tarde."
)
@router.post("/prelaunch-subscribe", response_model=ContactFormResponse)
async def submit_prelaunch_email(request: PrelaunchEmailRequest):
"""
Submit a prelaunch email subscription.
Sends a notification email to contact@bakewise.ai.
"""
try:
email_service = EmailService()
timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
# Format email content
text_content = PRELAUNCH_EMAIL_TEMPLATE_TEXT.format(
email=request.email,
timestamp=timestamp
)
html_content = PRELAUNCH_EMAIL_TEMPLATE_HTML.format(
email=request.email,
timestamp=timestamp
)
# Send email to contact@bakewise.ai
success = await email_service.send_email(
to_email="contact@bakewise.ai",
subject=f"[Pre-Lanzamiento] Nueva suscripción: {request.email}",
text_content=text_content,
html_content=html_content,
reply_to=request.email
)
if success:
logger.info("Prelaunch subscription submitted successfully",
email=request.email)
return ContactFormResponse(
success=True,
message="Te hemos apuntado a la lista de espera."
)
else:
logger.error("Failed to send prelaunch subscription email",
email=request.email)
raise HTTPException(
status_code=500,
detail="Error al registrar tu email. Por favor, inténtalo de nuevo."
)
except HTTPException:
raise
except Exception as e:
logger.error("Prelaunch subscription error", error=str(e))
raise HTTPException(
status_code=500,
detail="Error interno. Por favor, inténtalo más tarde."
)

View File

@@ -15,6 +15,7 @@ from app.api.notification_operations import router as notification_operations_ro
from app.api.analytics import router as analytics_router
from app.api.audit import router as audit_router
from app.api.whatsapp_webhooks import router as whatsapp_webhooks_router
from app.api.public_contact import router as public_contact_router
from app.services.sse_service import SSEService
from app.services.notification_orchestrator import NotificationOrchestrator
from app.services.email_service import EmailService
@@ -306,6 +307,7 @@ service.setup_custom_endpoints()
# where {notification_id} would match literal paths like "audit-logs"
service.add_router(audit_router, tags=["audit-logs"])
service.add_router(whatsapp_webhooks_router, tags=["whatsapp-webhooks"])
service.add_router(public_contact_router, tags=["public-contact"])
service.add_router(notification_operations_router, tags=["notification-operations"])
service.add_router(analytics_router, tags=["notifications-analytics"])
service.add_router(notification_router, tags=["notifications"])

View File

@@ -480,12 +480,32 @@ async def trigger_failure_notifications(notification_service: any, tenant_id: UU
}
html_content = template.render(**template_vars)
text_content = f"Equipment failure alert: {equipment.name} - {failure_data.get('failureType', 'Unknown')}"
# Send via notification service (which will handle the actual email sending)
# This is a simplified approach - in production you'd want to get manager emails from DB
logger.info("Failure notifications triggered (template rendered)",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id))
# Send via notification service API
if notification_service:
result = await notification_service.send_email(
tenant_id=str(tenant_id),
to_email="managers@bakeryia.com", # Should be configured from DB in production
subject=f"🚨 Fallo de Equipo: {equipment.name}",
message=text_content,
html_content=html_content,
priority="high"
)
if result:
logger.info("Failure notifications sent via notification service",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id),
notification_id=result.get('notification_id'))
else:
logger.error("Failed to send failure notifications via notification service",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id))
else:
logger.warning("Notification service not available, failure notifications not sent",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id))
except Exception as e:
logger.error("Error triggering failure notifications",
@@ -523,11 +543,32 @@ async def trigger_repair_notifications(notification_service: any, tenant_id: UUI
}
html_content = template.render(**template_vars)
text_content = f"Equipment repair completed: {equipment.name} - {repair_data.get('repairDescription', 'Repair completed')}"
# Send via notification service
logger.info("Repair notifications triggered (template rendered)",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id))
# Send via notification service API
if notification_service:
result = await notification_service.send_email(
tenant_id=str(tenant_id),
to_email="managers@bakeryia.com", # Should be configured from DB in production
subject=f"✅ Equipo Reparado: {equipment.name}",
message=text_content,
html_content=html_content,
priority="normal"
)
if result:
logger.info("Repair notifications sent via notification service",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id),
notification_id=result.get('notification_id'))
else:
logger.error("Failed to send repair notifications via notification service",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id))
else:
logger.warning("Notification service not available, repair notifications not sent",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id))
except Exception as e:
logger.error("Error triggering repair notifications",
@@ -565,14 +606,35 @@ async def send_support_contact_notification(notification_service: any, tenant_id
}
html_content = template.render(**template_vars)
text_content = f"Equipment failure alert: {equipment.name} - {failure_data.get('failureType', 'Unknown')}"
# TODO: Actually send email via notification service
# For now, just log that we would send to the support email
logger.info("Support contact notification prepared (would send to support)",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id),
support_email=support_email,
subject=f"🚨 URGENTE: Fallo de Equipo - {equipment.name}")
# Send via notification service API
if notification_service and support_email:
result = await notification_service.send_email(
tenant_id=str(tenant_id),
to_email=support_email,
subject=f"🚨 URGENTE: Fallo de Equipo - {equipment.name}",
message=text_content,
html_content=html_content,
priority="high"
)
if result:
logger.info("Support contact notification sent via notification service",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id),
support_email=support_email,
notification_id=result.get('notification_id'))
else:
logger.error("Failed to send support contact notification via notification service",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id),
support_email=support_email)
else:
logger.warning("Notification service not available or no support email provided",
equipment_id=str(equipment.id),
tenant_id=str(tenant_id),
support_email=support_email)
except Exception as e:
logger.error("Error sending support contact notification",

View File

@@ -22,9 +22,10 @@ class AlertEventConsumer:
Handles email and Slack notifications for critical alerts
"""
def __init__(self, db_session: AsyncSession):
def __init__(self, db_session: AsyncSession, notification_client: Optional[any] = None):
self.db_session = db_session
self.notification_config = self._load_notification_config()
self.notification_client = notification_client
def _load_notification_config(self) -> Dict[str, Any]:
"""
@@ -451,7 +452,7 @@ class AlertEventConsumer:
data: Dict[str, Any]
) -> bool:
"""
Send email notification
Send email notification using notification service API
Args:
tenant_id: Tenant ID
@@ -466,42 +467,77 @@ class AlertEventConsumer:
logger.debug("Email notifications disabled")
return False
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# Use notification service client if available
if self.notification_client:
# Build email content
subject = self._format_email_subject(notification_type, data)
body = self._format_email_body(notification_type, data)
# Build email content
subject = self._format_email_subject(notification_type, data)
body = self._format_email_body(notification_type, data)
# Send via notification service API
result = await self.notification_client.send_email(
tenant_id=tenant_id,
to_email=", ".join(self.notification_config['email']['recipients']),
subject=subject,
message="Supplier alert notification", # Plain text fallback
html_content=body,
priority="high" if data.get('severity') == 'critical' else "normal"
)
# Create message
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = self.notification_config['email']['from_address']
msg['To'] = ', '.join(self.notification_config['email']['recipients'])
if result:
logger.info(
"Email notification sent via notification service",
tenant_id=tenant_id,
notification_type=notification_type,
recipients=len(self.notification_config['email']['recipients'])
)
return True
else:
logger.error(
"Notification service failed to send email",
tenant_id=tenant_id,
notification_type=notification_type
)
return False
else:
# Fallback to direct SMTP for backward compatibility
logger.warning("Notification client not available, falling back to direct SMTP")
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# Attach HTML body
html_part = MIMEText(body, 'html')
msg.attach(html_part)
# Build email content
subject = self._format_email_subject(notification_type, data)
body = self._format_email_body(notification_type, data)
# Send email
smtp_config = self.notification_config['email']
with smtplib.SMTP(smtp_config['smtp_host'], smtp_config['smtp_port']) as server:
if smtp_config['use_tls']:
server.starttls()
# Create message
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = self.notification_config['email']['from_address']
msg['To'] = ', '.join(self.notification_config['email']['recipients'])
if smtp_config['smtp_username'] and smtp_config['smtp_password']:
server.login(smtp_config['smtp_username'], smtp_config['smtp_password'])
# Attach HTML body
html_part = MIMEText(body, 'html')
msg.attach(html_part)
server.send_message(msg)
# Send email
smtp_config = self.notification_config['email']
with smtplib.SMTP(smtp_config['smtp_host'], smtp_config['smtp_port']) as server:
if smtp_config['use_tls']:
server.starttls()
logger.info(
"Email notification sent",
tenant_id=tenant_id,
notification_type=notification_type,
recipients=len(self.notification_config['email']['recipients'])
)
return True
if smtp_config['smtp_username'] and smtp_config['smtp_password']:
server.login(smtp_config['smtp_username'], smtp_config['smtp_password'])
server.send_message(msg)
logger.info(
"Email notification sent via direct SMTP (fallback)",
tenant_id=tenant_id,
notification_type=notification_type,
recipients=len(self.notification_config['email']['recipients'])
)
return True
except Exception as e:
logger.error(
@@ -785,6 +821,6 @@ class AlertEventConsumer:
# Factory function for creating consumer instance
def create_alert_event_consumer(db_session: AsyncSession) -> AlertEventConsumer:
def create_alert_event_consumer(db_session: AsyncSession, notification_client: Optional[any] = None) -> AlertEventConsumer:
"""Create alert event consumer instance"""
return AlertEventConsumer(db_session)
return AlertEventConsumer(db_session, notification_client)