Add notification service
This commit is contained in:
291
services/notification/app/schemas/notifications.py
Normal file
291
services/notification/app/schemas/notifications.py
Normal file
@@ -0,0 +1,291 @@
|
||||
# ================================================================
|
||||
# services/notification/app/schemas/notifications.py
|
||||
# ================================================================
|
||||
"""
|
||||
Notification schemas for API validation and serialization
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field, validator
|
||||
from typing import Optional, Dict, Any, List
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
# Reuse enums from models
|
||||
class NotificationType(str, Enum):
|
||||
EMAIL = "email"
|
||||
WHATSAPP = "whatsapp"
|
||||
PUSH = "push"
|
||||
SMS = "sms"
|
||||
|
||||
class NotificationStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
SENT = "sent"
|
||||
DELIVERED = "delivered"
|
||||
FAILED = "failed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
class NotificationPriority(str, Enum):
|
||||
LOW = "low"
|
||||
NORMAL = "normal"
|
||||
HIGH = "high"
|
||||
URGENT = "urgent"
|
||||
|
||||
# ================================================================
|
||||
# REQUEST SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class NotificationCreate(BaseModel):
|
||||
"""Schema for creating a new notification"""
|
||||
type: NotificationType
|
||||
recipient_id: Optional[str] = None # For individual notifications
|
||||
recipient_email: Optional[EmailStr] = None
|
||||
recipient_phone: Optional[str] = None
|
||||
|
||||
# Content
|
||||
subject: Optional[str] = None
|
||||
message: str = Field(..., min_length=1, max_length=5000)
|
||||
html_content: Optional[str] = None
|
||||
|
||||
# Template-based content
|
||||
template_id: Optional[str] = None
|
||||
template_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Configuration
|
||||
priority: NotificationPriority = NotificationPriority.NORMAL
|
||||
scheduled_at: Optional[datetime] = None
|
||||
broadcast: bool = False
|
||||
|
||||
# Internal fields (set by service)
|
||||
tenant_id: Optional[str] = None
|
||||
sender_id: Optional[str] = None
|
||||
|
||||
@validator('recipient_phone')
|
||||
def validate_phone(cls, v):
|
||||
"""Validate Spanish phone number format"""
|
||||
if v and not v.startswith(('+34', '6', '7', '9')):
|
||||
raise ValueError('Invalid Spanish phone number format')
|
||||
return v
|
||||
|
||||
@validator('scheduled_at')
|
||||
def validate_scheduled_at(cls, v):
|
||||
"""Ensure scheduled time is in the future"""
|
||||
if v and v <= datetime.utcnow():
|
||||
raise ValueError('Scheduled time must be in the future')
|
||||
return v
|
||||
|
||||
class NotificationUpdate(BaseModel):
|
||||
"""Schema for updating notification status"""
|
||||
status: Optional[NotificationStatus] = None
|
||||
error_message: Optional[str] = None
|
||||
delivered_at: Optional[datetime] = None
|
||||
read: Optional[bool] = None
|
||||
read_at: Optional[datetime] = None
|
||||
|
||||
class BulkNotificationCreate(BaseModel):
|
||||
"""Schema for creating bulk notifications"""
|
||||
type: NotificationType
|
||||
recipients: List[str] = Field(..., min_items=1, max_items=1000) # User IDs or emails
|
||||
|
||||
# Content
|
||||
subject: Optional[str] = None
|
||||
message: str = Field(..., min_length=1, max_length=5000)
|
||||
html_content: Optional[str] = None
|
||||
|
||||
# Template-based content
|
||||
template_id: Optional[str] = None
|
||||
template_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
# Configuration
|
||||
priority: NotificationPriority = NotificationPriority.NORMAL
|
||||
scheduled_at: Optional[datetime] = None
|
||||
|
||||
# ================================================================
|
||||
# RESPONSE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class NotificationResponse(BaseModel):
|
||||
"""Schema for notification response"""
|
||||
id: str
|
||||
tenant_id: str
|
||||
sender_id: str
|
||||
recipient_id: Optional[str]
|
||||
|
||||
type: NotificationType
|
||||
status: NotificationStatus
|
||||
priority: NotificationPriority
|
||||
|
||||
subject: Optional[str]
|
||||
message: str
|
||||
recipient_email: Optional[str]
|
||||
recipient_phone: Optional[str]
|
||||
|
||||
scheduled_at: Optional[datetime]
|
||||
sent_at: Optional[datetime]
|
||||
delivered_at: Optional[datetime]
|
||||
|
||||
broadcast: bool
|
||||
read: bool
|
||||
read_at: Optional[datetime]
|
||||
|
||||
retry_count: int
|
||||
error_message: Optional[str]
|
||||
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class NotificationHistory(BaseModel):
|
||||
"""Schema for notification history"""
|
||||
notifications: List[NotificationResponse]
|
||||
total: int
|
||||
page: int
|
||||
per_page: int
|
||||
has_next: bool
|
||||
has_prev: bool
|
||||
|
||||
class NotificationStats(BaseModel):
|
||||
"""Schema for notification statistics"""
|
||||
total_sent: int
|
||||
total_delivered: int
|
||||
total_failed: int
|
||||
delivery_rate: float
|
||||
avg_delivery_time_minutes: Optional[float]
|
||||
by_type: Dict[str, int]
|
||||
by_status: Dict[str, int]
|
||||
recent_activity: List[Dict[str, Any]]
|
||||
|
||||
# ================================================================
|
||||
# PREFERENCE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class NotificationPreferences(BaseModel):
|
||||
"""Schema for user notification preferences"""
|
||||
user_id: str
|
||||
tenant_id: str
|
||||
|
||||
# Email preferences
|
||||
email_enabled: bool = True
|
||||
email_alerts: bool = True
|
||||
email_marketing: bool = False
|
||||
email_reports: bool = True
|
||||
|
||||
# WhatsApp preferences
|
||||
whatsapp_enabled: bool = False
|
||||
whatsapp_alerts: bool = False
|
||||
whatsapp_reports: bool = False
|
||||
|
||||
# Push notification preferences
|
||||
push_enabled: bool = True
|
||||
push_alerts: bool = True
|
||||
push_reports: bool = False
|
||||
|
||||
# Timing preferences
|
||||
quiet_hours_start: str = Field(default="22:00", pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||||
quiet_hours_end: str = Field(default="08:00", pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||||
timezone: str = "Europe/Madrid"
|
||||
|
||||
# Frequency preferences
|
||||
digest_frequency: str = Field(default="daily", pattern=r"^(none|daily|weekly)$")
|
||||
max_emails_per_day: int = Field(default=10, ge=1, le=100)
|
||||
|
||||
# Language preference
|
||||
language: str = Field(default="es", pattern=r"^(es|en)$")
|
||||
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class PreferencesUpdate(BaseModel):
|
||||
"""Schema for updating notification preferences"""
|
||||
email_enabled: Optional[bool] = None
|
||||
email_alerts: Optional[bool] = None
|
||||
email_marketing: Optional[bool] = None
|
||||
email_reports: Optional[bool] = None
|
||||
|
||||
whatsapp_enabled: Optional[bool] = None
|
||||
whatsapp_alerts: Optional[bool] = None
|
||||
whatsapp_reports: Optional[bool] = None
|
||||
|
||||
push_enabled: Optional[bool] = None
|
||||
push_alerts: Optional[bool] = None
|
||||
push_reports: Optional[bool] = None
|
||||
|
||||
quiet_hours_start: Optional[str] = Field(None, pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||||
quiet_hours_end: Optional[str] = Field(None, pattern=r"^([01]?[0-9]|2[0-3]):[0-5][0-9]$")
|
||||
timezone: Optional[str] = None
|
||||
|
||||
digest_frequency: Optional[str] = Field(None, pattern=r"^(none|daily|weekly)$")
|
||||
max_emails_per_day: Optional[int] = Field(None, ge=1, le=100)
|
||||
language: Optional[str] = Field(None, pattern=r"^(es|en)$")
|
||||
|
||||
# ================================================================
|
||||
# TEMPLATE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class TemplateCreate(BaseModel):
|
||||
"""Schema for creating notification templates"""
|
||||
template_key: str = Field(..., min_length=3, max_length=100)
|
||||
name: str = Field(..., min_length=3, max_length=255)
|
||||
description: Optional[str] = None
|
||||
category: str = Field(..., pattern=r"^(alert|marketing|transactional)$")
|
||||
|
||||
type: NotificationType
|
||||
subject_template: Optional[str] = None
|
||||
body_template: str = Field(..., min_length=10)
|
||||
html_template: Optional[str] = None
|
||||
|
||||
language: str = Field(default="es", pattern=r"^(es|en)$")
|
||||
default_priority: NotificationPriority = NotificationPriority.NORMAL
|
||||
required_variables: Optional[List[str]] = None
|
||||
|
||||
class TemplateResponse(BaseModel):
|
||||
"""Schema for template response"""
|
||||
id: str
|
||||
tenant_id: Optional[str]
|
||||
template_key: str
|
||||
name: str
|
||||
description: Optional[str]
|
||||
category: str
|
||||
|
||||
type: NotificationType
|
||||
subject_template: Optional[str]
|
||||
body_template: str
|
||||
html_template: Optional[str]
|
||||
|
||||
language: str
|
||||
is_active: bool
|
||||
is_system: bool
|
||||
default_priority: NotificationPriority
|
||||
required_variables: Optional[List[str]]
|
||||
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# ================================================================
|
||||
# WEBHOOK SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class DeliveryWebhook(BaseModel):
|
||||
"""Schema for delivery status webhooks"""
|
||||
notification_id: str
|
||||
status: NotificationStatus
|
||||
provider: str
|
||||
provider_message_id: Optional[str] = None
|
||||
delivered_at: Optional[datetime] = None
|
||||
error_code: Optional[str] = None
|
||||
error_message: Optional[str] = None
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
|
||||
class ReadReceiptWebhook(BaseModel):
|
||||
"""Schema for read receipt webhooks"""
|
||||
notification_id: str
|
||||
read_at: datetime
|
||||
user_agent: Optional[str] = None
|
||||
ip_address: Optional[str] = None
|
||||
Reference in New Issue
Block a user