"""Delete protection — prevents deletion of entities that are in use. Each check function returns a list of consumer descriptions. If non-empty, the entity cannot be deleted. """ from fastapi import HTTPException, status from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession from ..database.models import ( CommandConfig, CommandTracker, CommandTrackerListener, NotificationTarget, NotificationTracker, NotificationTrackerTarget, ) def raise_if_used(consumers: list[str], entity_name: str) -> None: """Raise 409 Conflict if the entity has consumers. Produces a human-readable summary string (kept as the primary ``detail``) plus a structured ``blocked_by`` list so the frontend can render a clickable warning modal. """ if consumers: summary = f"Cannot delete {entity_name}: used by {len(consumers)} consumer(s)." raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail={ "message": summary, "entity": entity_name, "blocked_by": consumers, }, ) async def check_service_provider(session: AsyncSession, provider_id: int) -> list[str]: """Check if a ServiceProvider is used by any trackers.""" consumers = [] result = await session.exec( select(NotificationTracker).where(NotificationTracker.provider_id == provider_id) ) for t in result.all(): consumers.append(f"Notification Tracker: {t.name}") result = await session.exec( select(CommandTracker).where(CommandTracker.provider_id == provider_id) ) for t in result.all(): consumers.append(f"Command Tracker: {t.name}") return consumers async def check_telegram_bot(session: AsyncSession, bot_id: int) -> list[str]: """Check if a TelegramBot is used by any targets or command listeners.""" consumers = [] # Check notification targets with this bot in config result = await session.exec( select(NotificationTarget).where(NotificationTarget.type == "telegram") ) for t in result.all(): if t.config.get("bot_id") == bot_id: consumers.append(f"Target: {t.name}") # Check command tracker listeners result = await session.exec( select(CommandTrackerListener).where( CommandTrackerListener.listener_type == "telegram_bot", CommandTrackerListener.listener_id == bot_id, ) ) for listener in result.all(): tracker = await session.get(CommandTracker, listener.command_tracker_id) name = tracker.name if tracker else f"#{listener.command_tracker_id}" consumers.append(f"Command Tracker Listener: {name}") return consumers async def check_email_bot(session: AsyncSession, bot_id: int) -> list[str]: """Check if an EmailBot is used by any targets.""" consumers = [] result = await session.exec( select(NotificationTarget).where(NotificationTarget.type == "email") ) for t in result.all(): if t.config.get("email_bot_id") == bot_id: consumers.append(f"Target: {t.name}") return consumers async def check_matrix_bot(session: AsyncSession, bot_id: int) -> list[str]: """Check if a MatrixBot is used by any targets.""" consumers = [] result = await session.exec( select(NotificationTarget).where(NotificationTarget.type == "matrix") ) for t in result.all(): if t.config.get("matrix_bot_id") == bot_id: consumers.append(f"Target: {t.name}") return consumers async def check_tracking_config(session: AsyncSession, config_id: int) -> list[str]: """Check if a TrackingConfig is used by any tracker-target links.""" consumers = [] result = await session.exec( select(NotificationTrackerTarget).where( NotificationTrackerTarget.tracking_config_id == config_id ) ) for tt in result.all(): tracker = await session.get(NotificationTracker, tt.tracker_id) target = await session.get(NotificationTarget, tt.target_id) tracker_name = tracker.name if tracker else f"#{tt.tracker_id}" target_name = target.name if target else f"#{tt.target_id}" consumers.append(f"Tracker Link: {tracker_name} → {target_name}") return consumers async def check_template_config(session: AsyncSession, config_id: int) -> list[str]: """Check if a TemplateConfig is used by any tracker-target links.""" consumers = [] result = await session.exec( select(NotificationTrackerTarget).where( NotificationTrackerTarget.template_config_id == config_id ) ) for tt in result.all(): tracker = await session.get(NotificationTracker, tt.tracker_id) target = await session.get(NotificationTarget, tt.target_id) tracker_name = tracker.name if tracker else f"#{tt.tracker_id}" target_name = target.name if target else f"#{tt.target_id}" consumers.append(f"Tracker Link: {tracker_name} → {target_name}") return consumers async def check_command_template_config(session: AsyncSession, config_id: int) -> list[str]: """Check if a CommandTemplateConfig is used by any command configs.""" consumers = [] result = await session.exec( select(CommandConfig).where( CommandConfig.command_template_config_id == config_id ) ) for c in result.all(): consumers.append(f"Command Config: {c.name}") return consumers async def check_command_config(session: AsyncSession, config_id: int) -> list[str]: """Check if a CommandConfig is used by any command trackers.""" consumers = [] result = await session.exec( select(CommandTracker).where(CommandTracker.command_config_id == config_id) ) for t in result.all(): consumers.append(f"Command Tracker: {t.name}") return consumers async def check_notification_target(session: AsyncSession, target_id: int) -> list[str]: """Check if a NotificationTarget is used by any tracker-target links.""" consumers = [] result = await session.exec( select(NotificationTrackerTarget).where( NotificationTrackerTarget.target_id == target_id ) ) for tt in result.all(): tracker = await session.get(NotificationTracker, tt.tracker_id) name = tracker.name if tracker else f"#{tt.tracker_id}" consumers.append(f"Notification Tracker: {name}") return consumers