104 lines
3.1 KiB
Python
104 lines
3.1 KiB
Python
|
|
"""Incident API endpoints."""
|
||
|
|
|
||
|
|
from datetime import datetime
|
||
|
|
from uuid import UUID
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, Query, status
|
||
|
|
|
||
|
|
from app.api.deps import CurrentUser, get_current_user, require_role
|
||
|
|
from app.schemas.common import PaginatedResponse
|
||
|
|
from app.schemas.incident import (
|
||
|
|
CommentRequest,
|
||
|
|
IncidentEventResponse,
|
||
|
|
IncidentResponse,
|
||
|
|
IncidentStatus,
|
||
|
|
TransitionRequest,
|
||
|
|
IncidentCreate,
|
||
|
|
)
|
||
|
|
from app.services import IncidentService
|
||
|
|
|
||
|
|
|
||
|
|
router = APIRouter(tags=["incidents"])
|
||
|
|
incident_service = IncidentService()
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/incidents", response_model=PaginatedResponse[IncidentResponse])
|
||
|
|
async def list_incidents(
|
||
|
|
status: IncidentStatus | None = Query(default=None),
|
||
|
|
cursor: datetime | None = Query(default=None, description="Cursor (created_at)"),
|
||
|
|
limit: int = Query(default=20, ge=1, le=100),
|
||
|
|
current_user: CurrentUser = Depends(get_current_user),
|
||
|
|
) -> PaginatedResponse[IncidentResponse]:
|
||
|
|
"""List incidents for the active organization."""
|
||
|
|
|
||
|
|
return await incident_service.get_incidents(
|
||
|
|
current_user,
|
||
|
|
status=status,
|
||
|
|
cursor=cursor,
|
||
|
|
limit=limit,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post(
|
||
|
|
"/services/{service_id}/incidents",
|
||
|
|
response_model=IncidentResponse,
|
||
|
|
status_code=status.HTTP_201_CREATED,
|
||
|
|
)
|
||
|
|
async def create_incident(
|
||
|
|
service_id: UUID,
|
||
|
|
payload: IncidentCreate,
|
||
|
|
current_user: CurrentUser = Depends(require_role("member")),
|
||
|
|
) -> IncidentResponse:
|
||
|
|
"""Create a new incident for the given service (member+)."""
|
||
|
|
|
||
|
|
return await incident_service.create_incident(current_user, service_id, payload)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/incidents/{incident_id}", response_model=IncidentResponse)
|
||
|
|
async def get_incident(
|
||
|
|
incident_id: UUID,
|
||
|
|
current_user: CurrentUser = Depends(get_current_user),
|
||
|
|
) -> IncidentResponse:
|
||
|
|
"""Fetch a single incident by ID."""
|
||
|
|
|
||
|
|
return await incident_service.get_incident(current_user, incident_id)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/incidents/{incident_id}/events", response_model=list[IncidentEventResponse])
|
||
|
|
async def get_incident_events(
|
||
|
|
incident_id: UUID,
|
||
|
|
current_user: CurrentUser = Depends(get_current_user),
|
||
|
|
) -> list[IncidentEventResponse]:
|
||
|
|
"""Get the event timeline for an incident."""
|
||
|
|
|
||
|
|
return await incident_service.get_incident_events(current_user, incident_id)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post(
|
||
|
|
"/incidents/{incident_id}/transition",
|
||
|
|
response_model=IncidentResponse,
|
||
|
|
)
|
||
|
|
async def transition_incident(
|
||
|
|
incident_id: UUID,
|
||
|
|
payload: TransitionRequest,
|
||
|
|
current_user: CurrentUser = Depends(require_role("member")),
|
||
|
|
) -> IncidentResponse:
|
||
|
|
"""Transition an incident status (member+)."""
|
||
|
|
|
||
|
|
return await incident_service.transition_incident(current_user, incident_id, payload)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post(
|
||
|
|
"/incidents/{incident_id}/comment",
|
||
|
|
response_model=IncidentEventResponse,
|
||
|
|
status_code=status.HTTP_201_CREATED,
|
||
|
|
)
|
||
|
|
async def add_comment(
|
||
|
|
incident_id: UUID,
|
||
|
|
payload: CommentRequest,
|
||
|
|
current_user: CurrentUser = Depends(require_role("member")),
|
||
|
|
) -> IncidentEventResponse:
|
||
|
|
"""Add a comment to the incident timeline (member+)."""
|
||
|
|
|
||
|
|
return await incident_service.add_comment(current_user, incident_id, payload)
|