55 lines
2.0 KiB
Python
55 lines
2.0 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy import and_, or_, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ..cursors import decode, decode_datetime, encode
|
|
from ..db import get_session
|
|
from ..models import Incident as IncidentModel
|
|
from ..schemas import Incident, IncidentsPage
|
|
from ..timeutil import to_utc
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _to_schema(i: IncidentModel) -> Incident:
|
|
return Incident(
|
|
id=str(i.id),
|
|
incident_key=i.incident_key,
|
|
state=i.state,
|
|
severity=i.severity,
|
|
agent_id=i.agent_id,
|
|
check_id=i.check_id,
|
|
opened_at=to_utc(i.opened_at),
|
|
resolved_at=to_utc(i.resolved_at) if i.resolved_at else None,
|
|
summary=i.summary,
|
|
)
|
|
|
|
|
|
@router.get("/incidents", response_model=IncidentsPage)
|
|
async def list_incidents(
|
|
state: str | None = Query(default=None),
|
|
cursor: str | None = Query(default=None, max_length=512),
|
|
limit: int = Query(default=100, ge=1, le=500),
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> IncidentsPage:
|
|
if state is not None and state not in ("open", "resolved"):
|
|
raise HTTPException(status_code=400, detail="invalid state")
|
|
stmt = select(IncidentModel).order_by(IncidentModel.opened_at.desc(), IncidentModel.id.desc())
|
|
if state is not None:
|
|
stmt = stmt.where(IncidentModel.state == state)
|
|
if cursor is not None:
|
|
ts_str, last_id = decode(cursor, 2)
|
|
ts = decode_datetime(ts_str)
|
|
stmt = stmt.where(
|
|
or_(
|
|
IncidentModel.opened_at < ts,
|
|
and_(IncidentModel.opened_at == ts, IncidentModel.id < last_id),
|
|
)
|
|
)
|
|
rows = (await session.execute(stmt.limit(limit + 1))).scalars().all()
|
|
next_cursor = None
|
|
if len(rows) > limit:
|
|
rows = rows[:limit]
|
|
next_cursor = encode(to_utc(rows[-1].opened_at).isoformat(), rows[-1].id)
|
|
return IncidentsPage(items=[_to_schema(r) for r in rows], next_cursor=next_cursor)
|