"""BacklogItem API — AP0.8c.""" from __future__ import annotations from typing import Literal, Optional from capabilities import require_capability from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from services import backlog as backlog_service from tenant_context import TenantContext router = APIRouter(prefix="/api/backlog", tags=["backlog"]) class BacklogCreateRequest(BaseModel): title: str = Field(min_length=1, max_length=255) description: str = "" status: Literal["new", "triaged", "accepted", "rejected"] = "new" priority: Literal["low", "normal", "high"] = "normal" roadmap_item_id: Optional[str] = None sort_order: Optional[int] = None class BacklogUpdateRequest(BaseModel): title: Optional[str] = Field(default=None, min_length=1, max_length=255) description: Optional[str] = None status: Optional[Literal["new", "triaged", "accepted", "rejected"]] = None priority: Optional[Literal["low", "normal", "high"]] = None roadmap_item_id: Optional[str] = None clear_roadmap_item: bool = False sort_order: Optional[int] = None class BacklogConvertRequest(BaseModel): assigned_actor_ids: list[str] = Field(default_factory=list) work_cycle_id: Optional[str] = None assign_active_sprint: bool = True @router.get("/{backlog_item_id}") def get_backlog_item( backlog_item_id: str, ctx: TenantContext = Depends(require_capability("kairo.backlog.read")), ): item = backlog_service.get_backlog_item( tenant_id=ctx.tenant_id, backlog_item_id=backlog_item_id ) if not item: raise HTTPException(status_code=404, detail="Backlog-Item nicht gefunden") return item @router.patch("/{backlog_item_id}") def update_backlog_item( backlog_item_id: str, body: BacklogUpdateRequest, ctx: TenantContext = Depends(require_capability("kairo.backlog.manage")), ): try: item = backlog_service.update_backlog_item( tenant_id=ctx.tenant_id, backlog_item_id=backlog_item_id, user_id=ctx.user_id, title=body.title, description=body.description, status=body.status, priority=body.priority, roadmap_item_id=body.roadmap_item_id, clear_roadmap_item=body.clear_roadmap_item, sort_order=body.sort_order, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc if not item: raise HTTPException(status_code=404, detail="Backlog-Item nicht gefunden") return item @router.delete("/{backlog_item_id}", status_code=204) def delete_backlog_item( backlog_item_id: str, ctx: TenantContext = Depends(require_capability("kairo.backlog.manage")), ): if not backlog_service.delete_backlog_item( tenant_id=ctx.tenant_id, backlog_item_id=backlog_item_id, user_id=ctx.user_id, ): raise HTTPException(status_code=404, detail="Backlog-Item nicht gefunden") @router.post("/{backlog_item_id}/convert-to-action", status_code=201) def convert_backlog_to_action( backlog_item_id: str, body: BacklogConvertRequest, ctx: TenantContext = Depends(require_capability("kairo.backlog.manage")), ): assigned = body.assigned_actor_ids if not assigned and ctx.actor_id: assigned = [ctx.actor_id] try: return backlog_service.convert_backlog_to_action( tenant_id=ctx.tenant_id, backlog_item_id=backlog_item_id, user_id=ctx.user_id, assigned_actor_ids=assigned, work_cycle_id=body.work_cycle_id, assign_active_sprint=body.assign_active_sprint, ) except ValueError as exc: detail = str(exc) if detail == "Backlog-Item nicht gefunden": raise HTTPException(status_code=404, detail=detail) from exc raise HTTPException(status_code=400, detail=detail) from exc