From 77da22255f5af54abfb54cf2961155361a2e2abc Mon Sep 17 00:00:00 2001 From: demo-bot Date: Sun, 9 Aug 2026 15:49:38 +0000 Subject: [PATCH] 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. --- app/store.py | 62 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/app/store.py b/app/store.py index d0107cb..e9c4c71 100644 --- a/app/store.py +++ b/app/store.py @@ -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