Files
crucible-agent-build-fastap…/app/api.py

93 lines
3.0 KiB
Python

from typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from app.checker import EndpointChecker
from app.models import CurrentStatus, Monitor, MonitorCreate, MonitorUpdate
from app.store import MonitorStore
router = APIRouter()
def store(request: Request) -> MonitorStore:
return request.app.state.store
def checker(request: Request) -> EndpointChecker:
return request.app.state.checker
Store = Annotated[MonitorStore, Depends(store)]
Checker = Annotated[EndpointChecker, Depends(checker)]
def missing() -> HTTPException:
return HTTPException(status_code=404, detail=("monitor_not_found", "Monitor not found"))
@router.post("/v1/monitors", response_model=Monitor, status_code=status.HTTP_201_CREATED)
async def create_monitor(data: MonitorCreate, state: Store) -> Monitor:
return await state.create(data)
@router.get("/v1/monitors", response_model=list[Monitor])
async def list_monitors(state: Store) -> list[Monitor]:
return await state.list()
@router.get("/v1/monitors/{monitor_id}", response_model=Monitor)
async def get_monitor(monitor_id: UUID, state: Store) -> Monitor:
item = await state.get(monitor_id)
if item is None:
raise missing()
return item
@router.patch("/v1/monitors/{monitor_id}", response_model=Monitor)
async def update_monitor(monitor_id: UUID, data: MonitorUpdate, state: Store) -> Monitor:
item = await state.update(monitor_id, data)
if item is None:
raise missing()
return item
@router.delete("/v1/monitors/{monitor_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_monitor(monitor_id: UUID, state: Store) -> Response:
if not await state.delete(monitor_id):
raise missing()
return Response(status_code=status.HTTP_204_NO_CONTENT)
@router.post("/v1/monitors/{monitor_id}/check", response_model=CurrentStatus)
async def check_monitor(monitor_id: UUID, state: Store, outbound: Checker) -> CurrentStatus:
snapshot = await state.snapshot(monitor_id)
if snapshot is None:
raise missing()
item, revision = snapshot
result = await outbound.check(str(item.url))
if await state.record_status(monitor_id, revision, result) is None:
raise HTTPException(status_code=409,
detail=("monitor_changed", "Monitor changed while check was running"))
return result
@router.get("/v1/monitors/{monitor_id}/status", response_model=CurrentStatus)
async def get_status(monitor_id: UUID, state: Store) -> CurrentStatus:
item = await state.get(monitor_id)
if item is None:
raise missing()
return item.current_status
@router.get("/healthz")
async def health() -> dict[str, str]:
return {"status": "ok"}
@router.get("/readyz")
async def readiness(request: Request) -> dict[str, str]:
if not hasattr(request.app.state, "store"):
raise HTTPException(status_code=503, detail=("not_ready", "Service is not ready"))
return {"status": "ready", "storage": "process-local-memory"}