From 27d10998b778a1222607916e7cfb92daa3f77e3b Mon Sep 17 00:00:00 2001 From: demo-bot Date: Sun, 9 Aug 2026 16:09:53 +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/models.py | 80 +++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/app/models.py b/app/models.py index 2e2c92d..3b538c8 100644 --- a/app/models.py +++ b/app/models.py @@ -1,57 +1,61 @@ from datetime import datetime, timezone from enum import Enum -from uuid import UUID +from uuid import UUID, uuid4 -from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, HttpUrl -def utc_now() -> datetime: +def now_utc() -> datetime: return datetime.now(timezone.utc) class MonitorState(str, Enum): - UNKNOWN = "unknown" - UP = "up" - DOWN = "down" - ERROR = "error" + unknown = "unknown" + up = "up" + down = "down" -class CurrentStatus(BaseModel): - state: MonitorState = MonitorState.UNKNOWN - checked_at: datetime | None = None - latency_ms: float | None = Field(default=None, ge=0) - http_status: int | None = Field(default=None, ge=100, le=599) - error: str | None = Field(default=None, max_length=500) - - -class MonitorFields(BaseModel): - model_config = ConfigDict(str_strip_whitespace=True) - +class MonitorCreate(BaseModel): name: str = Field(min_length=1, max_length=100) - target_url: AnyHttpUrl - - @field_validator("target_url") - @classmethod - def reject_credentials(cls, value: AnyHttpUrl) -> AnyHttpUrl: - if value.username is not None or value.password is not None: - raise ValueError("URL credentials are not allowed") - return value + url: HttpUrl + timeout_seconds: float | None = Field(None, gt=0, le=60) -class MonitorCreate(MonitorFields): +class MonitorUpdate(BaseModel): + name: str | None = Field(None, min_length=1, max_length=100) + url: HttpUrl | None = None + timeout_seconds: float | None = Field(None, gt=0, le=60) + + +class MonitorStatus(BaseModel): + state: MonitorState = MonitorState.unknown + checked_at: datetime | None = None + http_status: int | None = None + latency_ms: float | None = None + error: str | None = None + + +class Monitor(BaseModel): + model_config = ConfigDict(from_attributes=True) + + id: UUID = Field(default_factory=uuid4) + name: str + url: HttpUrl + timeout_seconds: float + created_at: datetime = Field(default_factory=now_utc) + updated_at: datetime = Field(default_factory=now_utc) + status: MonitorStatus = Field(default_factory=MonitorStatus) + + +class CheckResult(MonitorStatus): pass -class MonitorUpdate(MonitorFields): - pass +class ErrorBody(BaseModel): + code: str + message: str + details: list[dict[str, object]] | None = None -class Monitor(MonitorFields): - id: UUID - created_at: datetime - updated_at: datetime - current_status: CurrentStatus - - -class Health(BaseModel): - status: str +class ErrorResponse(BaseModel): + error: ErrorBody