ai-robot-core/ai-service/app/api/admin/script_flows.py

158 lines
4.6 KiB
Python

"""
Script Flow Management API.
[AC-AISVC-71, AC-AISVC-72, AC-AISVC-73] Script flow CRUD endpoints.
"""
import logging
import uuid
from typing import Any
from fastapi import APIRouter, Depends, Header, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_session
from app.models.entities import ScriptFlowCreate, ScriptFlowUpdate
from app.services.flow.flow_service import ScriptFlowService
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/admin/script-flows", tags=["Script Flows"])
def get_tenant_id(x_tenant_id: str = Header(..., alias="X-Tenant-Id")) -> str:
"""Extract tenant ID from header."""
if not x_tenant_id:
raise HTTPException(status_code=400, detail="X-Tenant-Id header is required")
return x_tenant_id
@router.get("")
async def list_flows(
tenant_id: str = Depends(get_tenant_id),
is_enabled: bool | None = None,
session: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
"""
[AC-AISVC-72] List all script flows for a tenant.
"""
logger.info(f"[AC-AISVC-72] Listing script flows for tenant={tenant_id}, is_enabled={is_enabled}")
service = ScriptFlowService(session)
flows = await service.list_flows(tenant_id, is_enabled)
data = []
for f in flows:
linked_rule_count = await service._get_linked_rule_count(tenant_id, f.id)
data.append({
"id": str(f.id),
"name": f.name,
"description": f.description,
"step_count": len(f.steps),
"is_enabled": f.is_enabled,
"linked_rule_count": linked_rule_count,
"created_at": f.created_at.isoformat(),
"updated_at": f.updated_at.isoformat(),
})
return {"data": data}
@router.post("", status_code=201)
async def create_flow(
body: ScriptFlowCreate,
tenant_id: str = Depends(get_tenant_id),
session: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
"""
[AC-AISVC-71] Create a new script flow.
"""
logger.info(f"[AC-AISVC-71] Creating script flow for tenant={tenant_id}, name={body.name}")
service = ScriptFlowService(session)
try:
flow = await service.create_flow(tenant_id, body)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return {
"id": str(flow.id),
"name": flow.name,
"description": flow.description,
"step_count": len(flow.steps),
"is_enabled": flow.is_enabled,
"created_at": flow.created_at.isoformat(),
"updated_at": flow.updated_at.isoformat(),
}
@router.get("/{flow_id}")
async def get_flow_detail(
flow_id: uuid.UUID,
tenant_id: str = Depends(get_tenant_id),
session: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
"""
[AC-AISVC-73] Get script flow detail with complete step definitions.
"""
logger.info(f"[AC-AISVC-73] Getting flow detail for tenant={tenant_id}, id={flow_id}")
service = ScriptFlowService(session)
detail = await service.get_flow_detail(tenant_id, flow_id)
if not detail:
raise HTTPException(status_code=404, detail="Flow not found")
return detail
@router.put("/{flow_id}")
async def update_flow(
flow_id: uuid.UUID,
body: ScriptFlowUpdate,
tenant_id: str = Depends(get_tenant_id),
session: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
"""
[AC-AISVC-73] Update script flow definition.
"""
logger.info(f"[AC-AISVC-73] Updating flow for tenant={tenant_id}, id={flow_id}")
service = ScriptFlowService(session)
try:
flow = await service.update_flow(tenant_id, flow_id, body)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
if not flow:
raise HTTPException(status_code=404, detail="Flow not found")
return {
"id": str(flow.id),
"name": flow.name,
"description": flow.description,
"step_count": len(flow.steps),
"is_enabled": flow.is_enabled,
"created_at": flow.created_at.isoformat(),
"updated_at": flow.updated_at.isoformat(),
}
@router.delete("/{flow_id}", status_code=204)
async def delete_flow(
flow_id: uuid.UUID,
tenant_id: str = Depends(get_tenant_id),
session: AsyncSession = Depends(get_session),
) -> None:
"""
Delete a script flow.
"""
logger.info(f"Deleting flow for tenant={tenant_id}, id={flow_id}")
service = ScriptFlowService(session)
success = await service.delete_flow(tenant_id, flow_id)
if not success:
raise HTTPException(status_code=404, detail="Flow not found")