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 / container (push) Has been cancelled
ci / quality (push) Has started running

This commit is contained in:
2026-08-09 16:03:42 +00:00
parent b9ffed91d7
commit 9d5137d688

View File

@@ -1,51 +1,65 @@
from datetime import datetime from datetime import datetime, timezone
from typing import Literal from enum import Enum
from typing import Annotated
from uuid import UUID from uuid import UUID
from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, model_validator from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field, StringConstraints, field_validator
StatusState = Literal["unknown", "up", "down", "error"] Name = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1, max_length=100)]
class CheckStatus(BaseModel): def utcnow() -> datetime:
state: StatusState = "unknown" return datetime.now(timezone.utc)
checked_at: datetime | None = None
status_code: int | None = None
latency_ms: float | None = Field(default=None, ge=0) class MonitorStatus(str, Enum):
error: str | None = None unknown = "unknown"
up = "up"
down = "down"
error = "error"
class MonitorCreate(BaseModel): class MonitorCreate(BaseModel):
name: str = Field(min_length=1, max_length=200) name: Name
url: AnyHttpUrl url: AnyHttpUrl
@field_validator("url")
@classmethod
def no_credentials(cls, value: AnyHttpUrl) -> AnyHttpUrl:
if value.username or value.password:
raise ValueError("URL credentials are not allowed")
return value
class MonitorUpdate(BaseModel):
name: str | None = Field(default=None, min_length=1, max_length=200)
url: AnyHttpUrl | None = None
@model_validator(mode="after") class MonitorUpdate(MonitorCreate):
def not_empty(self) -> "MonitorUpdate": pass
if self.name is None and self.url is None:
raise ValueError("at least one field is required")
return self
class Monitor(BaseModel): class Monitor(BaseModel):
model_config = ConfigDict(from_attributes=True) model_config = ConfigDict(frozen=True)
id: UUID id: UUID
name: str name: str
url: AnyHttpUrl url: AnyHttpUrl
status: MonitorStatus = MonitorStatus.unknown
created_at: datetime created_at: datetime
updated_at: datetime updated_at: datetime
status: CheckStatus 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
class CheckResult(CheckStatus): class CheckResult(BaseModel):
monitor_id: UUID status: MonitorStatus
status_applied: bool = True checked_at: datetime
latency_ms: float = Field(ge=0)
http_status: int | None = None
error: str | None = None
class HealthResponse(BaseModel): class StatusResponse(CheckResult):
status: Literal["ok", "ready"] id: UUID
class ProbeResponse(BaseModel):
status: str