From d805780149369ece1349769c63de6b0a2ce57293 Mon Sep 17 00:00:00 2001 From: demo-bot Date: Sun, 9 Aug 2026 15:35:34 +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 | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 app/models.py diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..7889635 --- /dev/null +++ b/app/models.py @@ -0,0 +1,68 @@ +from datetime import datetime, timezone +from enum import StrEnum +from typing import Self +from uuid import UUID + +from pydantic import BaseModel, ConfigDict, Field, HttpUrl, model_validator + + +class State(StrEnum): + UNKNOWN = "unknown" + UP = "up" + DOWN = "down" + ERROR = "error" + + +class CurrentStatus(BaseModel): + model_config = ConfigDict(extra="forbid") + state: State = State.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) + final_url: str | None = None + error: str | None = None + + +class MonitorCreate(BaseModel): + model_config = ConfigDict(extra="forbid") + name: str = Field(min_length=1, max_length=120) + url: HttpUrl + + @model_validator(mode="after") + def safe_url_shape(self) -> Self: + if self.url.scheme not in {"http", "https"}: + raise ValueError("only http and https URLs are supported") + if self.url.username is not None or self.url.password is not None: + raise ValueError("URL credentials are not allowed") + return self + + +class MonitorUpdate(BaseModel): + model_config = ConfigDict(extra="forbid") + name: str | None = Field(default=None, min_length=1, max_length=120) + url: HttpUrl | None = None + + @model_validator(mode="after") + def validate_update(self) -> Self: + if self.name is None and self.url is None: + raise ValueError("at least one field is required") + if self.url is not None: + if self.url.scheme not in {"http", "https"}: + raise ValueError("only http and https URLs are supported") + if self.url.username is not None or self.url.password is not None: + raise ValueError("URL credentials are not allowed") + return self + + +class Monitor(BaseModel): + model_config = ConfigDict(extra="forbid") + id: UUID + name: str + url: HttpUrl + created_at: datetime + updated_at: datetime + current_status: CurrentStatus + + +def utcnow() -> datetime: + return datetime.now(timezone.utc)