Files
bakery-ia/services/notification/app/api/notifications.py

207 lines
8.3 KiB
Python
Raw Normal View History

2025-07-21 22:44:11 +02:00
"""
2025-10-06 15:27:01 +02:00
Notification CRUD API endpoints (ATOMIC operations only)
Handles basic notification retrieval and listing
2025-07-21 22:44:11 +02:00
"""
2025-07-21 14:41:33 +02:00
import structlog
2025-10-06 15:27:01 +02:00
from fastapi import APIRouter, Depends, HTTPException, status, Query, Path
2025-08-08 09:08:41 +02:00
from typing import List, Optional, Dict, Any
from uuid import UUID
2025-07-21 14:41:33 +02:00
2025-07-21 22:44:11 +02:00
from app.schemas.notifications import (
2025-10-06 15:27:01 +02:00
NotificationResponse, NotificationType, NotificationStatus
2025-07-21 22:44:11 +02:00
)
2025-08-08 09:08:41 +02:00
from app.services.notification_service import EnhancedNotificationService
from app.models.notifications import NotificationType as ModelNotificationType
2025-10-06 15:27:01 +02:00
from shared.auth.decorators import get_current_user_dep
from shared.auth.access_control import require_user_role
from shared.routing.route_builder import RouteBuilder
2025-08-08 09:08:41 +02:00
from shared.database.base import create_database_manager
from shared.monitoring.metrics import track_endpoint_metrics
2025-07-21 14:41:33 +02:00
logger = structlog.get_logger()
2025-08-08 09:08:41 +02:00
router = APIRouter()
2025-11-02 20:24:44 +01:00
route_builder = RouteBuilder('notifications')
2025-07-21 14:41:33 +02:00
2025-08-08 09:08:41 +02:00
# Dependency injection for enhanced notification service
def get_enhanced_notification_service():
database_manager = create_database_manager()
return EnhancedNotificationService(database_manager)
2025-07-21 22:44:11 +02:00
2025-10-06 15:27:01 +02:00
# ============================================================================
# ATOMIC CRUD ENDPOINTS - Get/List notifications only
# ============================================================================
2025-07-21 14:41:33 +02:00
2025-10-06 15:27:01 +02:00
@router.get(
route_builder.build_resource_detail_route("{notification_id}"),
response_model=NotificationResponse
)
@require_user_role(['viewer', 'member', 'admin', 'owner'])
2025-08-08 09:08:41 +02:00
@track_endpoint_metrics("notification_get")
async def get_notification_enhanced(
notification_id: UUID = Path(..., description="Notification ID"),
2025-10-06 15:27:01 +02:00
tenant_id: UUID = Path(..., description="Tenant ID"),
2025-07-21 22:44:11 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
2025-08-08 09:08:41 +02:00
notification_service: EnhancedNotificationService = Depends(get_enhanced_notification_service)
2025-07-21 22:44:11 +02:00
):
2025-08-08 09:08:41 +02:00
"""Get a specific notification by ID with enhanced access control"""
2025-07-21 22:44:11 +02:00
try:
2025-08-08 09:08:41 +02:00
notification = await notification_service.get_notification_by_id(str(notification_id))
if not notification:
2025-07-21 22:44:11 +02:00
raise HTTPException(
2025-08-08 09:08:41 +02:00
status_code=status.HTTP_404_NOT_FOUND,
detail="Notification not found"
2025-07-21 22:44:11 +02:00
)
2025-08-08 09:08:41 +02:00
# Verify user has access to this notification
if (notification.recipient_id != current_user["user_id"] and
notification.sender_id != current_user["user_id"] and
not notification.broadcast and
current_user.get("role") not in ["admin", "manager"]):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access denied to notification"
)
2025-07-21 22:44:11 +02:00
2025-08-08 09:08:41 +02:00
return NotificationResponse.from_orm(notification)
2025-07-21 22:44:11 +02:00
except HTTPException:
raise
except Exception as e:
2025-08-08 09:08:41 +02:00
logger.error("Failed to get notification",
notification_id=str(notification_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get notification"
)
2025-07-21 22:44:11 +02:00
2025-10-06 15:27:01 +02:00
@router.get(
route_builder.build_base_route("user/{user_id}"),
response_model=List[NotificationResponse]
)
@require_user_role(['viewer', 'member', 'admin', 'owner'])
2025-08-08 09:08:41 +02:00
@track_endpoint_metrics("notification_get_user_notifications")
async def get_user_notifications_enhanced(
user_id: str = Path(..., description="User ID"),
2025-10-06 15:27:01 +02:00
tenant_id: UUID = Path(..., description="Tenant ID"),
2025-08-08 09:08:41 +02:00
unread_only: bool = Query(False, description="Only return unread notifications"),
notification_type: Optional[NotificationType] = Query(None, description="Filter by notification type"),
skip: int = Query(0, ge=0, description="Number of records to skip"),
limit: int = Query(50, ge=1, le=100, description="Maximum number of records"),
2025-07-21 22:44:11 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
2025-08-08 09:08:41 +02:00
notification_service: EnhancedNotificationService = Depends(get_enhanced_notification_service)
2025-07-21 22:44:11 +02:00
):
2025-08-08 09:08:41 +02:00
"""Get notifications for a user with enhanced filtering"""
# Users can only get their own notifications unless they're admin
if user_id != current_user["user_id"] and current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Can only access your own notifications"
2025-07-21 22:44:11 +02:00
)
2025-08-08 09:08:41 +02:00
2025-07-21 22:44:11 +02:00
try:
2025-08-08 09:08:41 +02:00
# Convert string type to model enum if provided
model_notification_type = None
if notification_type:
try:
model_notification_type = ModelNotificationType(notification_type.value)
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid notification type: {notification_type.value}"
)
notifications = await notification_service.get_user_notifications(
user_id=user_id,
2025-07-21 22:44:11 +02:00
tenant_id=tenant_id,
2025-08-08 09:08:41 +02:00
unread_only=unread_only,
notification_type=model_notification_type,
skip=skip,
limit=limit
2025-07-21 22:44:11 +02:00
)
2025-08-08 09:08:41 +02:00
return [NotificationResponse.from_orm(notification) for notification in notifications]
2025-07-21 22:44:11 +02:00
except HTTPException:
raise
except Exception as e:
2025-08-08 09:08:41 +02:00
logger.error("Failed to get user notifications",
user_id=user_id,
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get user notifications"
2025-07-21 14:41:33 +02:00
)
2025-07-21 22:44:11 +02:00
2025-10-06 15:27:01 +02:00
@router.get(
route_builder.build_base_route("list"),
response_model=List[NotificationResponse]
)
@require_user_role(["viewer", "member", "admin", "owner"])
2025-08-08 09:08:41 +02:00
@track_endpoint_metrics("notification_get_tenant_notifications")
async def get_tenant_notifications_enhanced(
tenant_id: str = Path(..., description="Tenant ID"),
status_filter: Optional[NotificationStatus] = Query(None, description="Filter by status"),
notification_type: Optional[NotificationType] = Query(None, description="Filter by type"),
skip: int = Query(0, ge=0, description="Number of records to skip"),
limit: int = Query(50, ge=1, le=100, description="Maximum number of records"),
2025-07-21 22:44:11 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
2025-08-08 09:08:41 +02:00
notification_service: EnhancedNotificationService = Depends(get_enhanced_notification_service)
2025-07-21 22:44:11 +02:00
):
2025-08-08 09:08:41 +02:00
"""Get notifications for a tenant with enhanced filtering (admin/manager only)"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only admins and managers can view tenant notifications"
)
2025-07-21 22:44:11 +02:00
try:
2025-08-08 09:08:41 +02:00
# Convert enums if provided
model_notification_type = None
if notification_type:
try:
model_notification_type = ModelNotificationType(notification_type.value)
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid notification type: {notification_type.value}"
)
model_status = None
if status_filter:
try:
from app.models.notifications import NotificationStatus as ModelStatus
model_status = ModelStatus(status_filter.value)
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid status: {status_filter.value}"
)
notifications = await notification_service.get_tenant_notifications(
2025-07-21 22:44:11 +02:00
tenant_id=tenant_id,
2025-08-08 09:08:41 +02:00
status=model_status,
notification_type=model_notification_type,
skip=skip,
limit=limit
2025-07-21 22:44:11 +02:00
)
2025-08-08 09:08:41 +02:00
return [NotificationResponse.from_orm(notification) for notification in notifications]
2025-07-21 22:44:11 +02:00
except HTTPException:
raise
except Exception as e:
2025-08-08 09:08:41 +02:00
logger.error("Failed to get tenant notifications",
tenant_id=tenant_id,
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get tenant notifications"
)
2025-07-21 22:44:11 +02:00