"""API routes for Webhook CRUD and testing.""" from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db from app.deps import get_current_user, require_permission from app.schemas.webhook import ( WebhookCreate, WebhookResponse, WebhookUpdate, ) from app.services import webhook_service router = APIRouter(prefix="/api/v1/webhooks", tags=["webhooks"]) @router.get( "", response_model=list[WebhookResponse], dependencies=[Depends(require_permission("automation:read"))], ) async def list_webhooks( event: str | None = Query(None, description="Filter by event name"), db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """List all webhooks for the current tenant.""" tenant_id = uuid.UUID(current_user["tenant_id"]) webhooks = await webhook_service.list_webhooks(db, tenant_id, event=event) return webhooks @router.post( "", response_model=WebhookResponse, status_code=201, dependencies=[Depends(require_permission("automation:write"))], ) async def create_webhook( body: WebhookCreate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Create a new webhook subscription.""" tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) webhook = await webhook_service.create_webhook( db, tenant_id, user_id, body.model_dump() ) return webhook @router.get( "/{webhook_id}", response_model=WebhookResponse, dependencies=[Depends(require_permission("automation:read"))], ) async def get_webhook( webhook_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Get a single webhook by ID.""" tenant_id = uuid.UUID(current_user["tenant_id"]) try: wh_id = uuid.UUID(webhook_id) except (ValueError, TypeError): raise HTTPException(400, detail={"detail": "Invalid webhook_id", "code": "invalid_id"}) from None webhook = await webhook_service.get_webhook(db, tenant_id, wh_id) if webhook is None: raise HTTPException(404, detail={"detail": "Webhook not found", "code": "not_found"}) return webhook @router.patch( "/{webhook_id}", response_model=WebhookResponse, dependencies=[Depends(require_permission("automation:write"))], ) async def update_webhook( webhook_id: str, body: WebhookUpdate, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Update an existing webhook subscription.""" tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) try: wh_id = uuid.UUID(webhook_id) except (ValueError, TypeError): raise HTTPException(400, detail={"detail": "Invalid webhook_id", "code": "invalid_id"}) from None update_data = {k: v for k, v in body.model_dump().items() if v is not None} if not update_data: raise HTTPException(400, detail={"detail": "No fields to update", "code": "no_updates"}) webhook = await webhook_service.update_webhook( db, tenant_id, wh_id, update_data, user_id=user_id ) if webhook is None: raise HTTPException(404, detail={"detail": "Webhook not found", "code": "not_found"}) return webhook @router.delete( "/{webhook_id}", status_code=204, dependencies=[Depends(require_permission("automation:write"))], ) async def delete_webhook( webhook_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Delete a webhook subscription.""" tenant_id = uuid.UUID(current_user["tenant_id"]) try: wh_id = uuid.UUID(webhook_id) except (ValueError, TypeError): raise HTTPException(400, detail={"detail": "Invalid webhook_id", "code": "invalid_id"}) from None deleted = await webhook_service.delete_webhook(db, tenant_id, wh_id) if not deleted: raise HTTPException(404, detail={"detail": "Webhook not found", "code": "not_found"}) return None @router.post( "/{webhook_id}/test", dependencies=[Depends(require_permission("automation:write"))], ) async def test_webhook( webhook_id: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(get_current_user), ): """Send a test payload to a webhook to verify connectivity.""" tenant_id = uuid.UUID(current_user["tenant_id"]) try: wh_id = uuid.UUID(webhook_id) except (ValueError, TypeError): raise HTTPException(400, detail={"detail": "Invalid webhook_id", "code": "invalid_id"}) from None webhook = await webhook_service.get_webhook(db, tenant_id, wh_id) if webhook is None: raise HTTPException(404, detail={"detail": "Webhook not found", "code": "not_found"}) test_payload = { "type": "test", "message": "This is a test webhook from LeoCRM", "webhook_id": str(webhook.id), } result = await webhook_service.send_webhook(webhook, "webhook.test", test_payload) return result