From dcecec8e7a9aa7fc574907229acb8d5b1a42557b Mon Sep 17 00:00:00 2001 From: demo-bot Date: Sun, 9 Aug 2026 16:07:02 +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 | 70 +++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/app/models.py b/app/models.py index 237a4ea..2e2c92d 100644 --- a/app/models.py +++ b/app/models.py @@ -1,65 +1,57 @@ from datetime import datetime, timezone from enum import Enum -from typing import Annotated from uuid import UUID -from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, StringConstraints, field_validator - -Name = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1, max_length=100)] +from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, field_validator -def utcnow() -> datetime: +def utc_now() -> datetime: return datetime.now(timezone.utc) -class MonitorStatus(str, Enum): - unknown = "unknown" - up = "up" - down = "down" - error = "error" +class MonitorState(str, Enum): + UNKNOWN = "unknown" + UP = "up" + DOWN = "down" + ERROR = "error" -class MonitorCreate(BaseModel): - name: Name - url: AnyHttpUrl +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) - @field_validator("url") + +class MonitorFields(BaseModel): + model_config = ConfigDict(str_strip_whitespace=True) + + name: str = Field(min_length=1, max_length=100) + target_url: AnyHttpUrl + + @field_validator("target_url") @classmethod - def no_credentials(cls, value: AnyHttpUrl) -> AnyHttpUrl: - if value.username or value.password: + 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 -class MonitorUpdate(MonitorCreate): +class MonitorCreate(MonitorFields): pass -class Monitor(BaseModel): - model_config = ConfigDict(frozen=True) +class MonitorUpdate(MonitorFields): + pass + + +class Monitor(MonitorFields): id: UUID - name: str - url: AnyHttpUrl - status: MonitorStatus = MonitorStatus.unknown created_at: datetime updated_at: datetime - 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 = None + current_status: CurrentStatus -class CheckResult(BaseModel): - status: MonitorStatus - checked_at: datetime - latency_ms: float = Field(ge=0) - http_status: int | None = None - error: str | None = None - - -class StatusResponse(CheckResult): - id: UUID - - -class ProbeResponse(BaseModel): +class Health(BaseModel): status: str