decomposer: generate deliverable files for Define the service contract and project architecture for the FastAPI endpoint monitoring service.; Implement the typed monitor CRUD API and concurrency-safe in-memory state according to the service design.; Implement secure on-demand endpoint checks with status updates, latency measurement, robust error handling, and redacted structured logs.; Add operational API endpoints and environment-driven runtime configuration to the monitoring service.; Create automated tests for the monitoring service.; Package the service with Docker and developer documentation.; Validate the complete project.
Some checks failed
ci / validate (push) Has been cancelled

This commit is contained in:
2026-08-09 15:49:38 +00:00
parent 0c1c730f9b
commit 77da22255f

View File

@@ -1,53 +1,63 @@
import asyncio
from datetime import UTC, datetime
from uuid import UUID
from uuid import UUID, uuid4
from app.models import CurrentStatus, Monitor, MonitorCreate, MonitorUpdate
from app.models import CurrentStatus, Monitor, MonitorCreate, MonitorUpdate, utc_now
class MonitorStore:
"""Concurrency-safe process-local state; all values cross the lock as copies."""
def __init__(self) -> None:
self._items: dict[UUID, Monitor] = {}
self._lock = asyncio.Lock()
self._items: dict[UUID, Monitor] = {}
async def create(self, data: MonitorCreate) -> Monitor:
monitor = Monitor(name=data.name, url=data.url)
async def create(self, value: MonitorCreate) -> Monitor:
now = utc_now()
item = Monitor(
id=uuid4(), name=value.name, url=value.url, created_at=now, updated_at=now, revision=1
)
async with self._lock:
self._items[monitor.id] = monitor
return monitor.model_copy(deep=True)
self._items[item.id] = item
return item.model_copy(deep=True)
async def list(self) -> list[Monitor]:
async with self._lock:
return [item.model_copy(deep=True) for item in self._items.values()]
values = sorted(self._items.values(), key=lambda item: item.created_at)
return [item.model_copy(deep=True) for item in values]
async def get(self, monitor_id: UUID) -> Monitor | None:
async with self._lock:
item = self._items.get(monitor_id)
return item.model_copy(deep=True) if item else None
async def update(self, monitor_id: UUID, data: MonitorUpdate) -> Monitor | None:
changes = data.model_dump(exclude_unset=True, exclude_none=True)
async def update(self, monitor_id: UUID, value: MonitorUpdate) -> Monitor | None:
async with self._lock:
item = self._items.get(monitor_id)
if item is None:
old = self._items.get(monitor_id)
if old is None:
return None
updated = item.model_copy(update={**changes, "updated_at": datetime.now(UTC)})
self._items[monitor_id] = updated
return updated.model_copy(deep=True)
changed_url = old.url != value.url
item = old.model_copy(
update={
"name": value.name,
"url": value.url,
"updated_at": utc_now(),
"revision": old.revision + 1,
"current_status": None if changed_url else old.current_status,
}
)
self._items[monitor_id] = item
return item.model_copy(deep=True)
async def delete(self, monitor_id: UUID) -> bool:
async with self._lock:
return self._items.pop(monitor_id, None) is not None
async def set_status(self, monitor_id: UUID, status: CurrentStatus) -> Monitor | None:
async def set_status_if_current(
self, monitor_id: UUID, revision: int, status: CurrentStatus
) -> bool:
async with self._lock:
item = self._items.get(monitor_id)
if item is None:
return None
updated = item.model_copy(
update={"current_status": status, "updated_at": datetime.now(UTC)}
old = self._items.get(monitor_id)
if old is None or old.revision != revision:
return False
self._items[monitor_id] = old.model_copy(
update={"current_status": status, "updated_at": utc_now()}
)
self._items[monitor_id] = updated
return updated.model_copy(deep=True)
return True