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 typing import Literal
from datetime import datetime, timezone
from enum import Enum
from typing import Annotated
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):
state: StatusState = "unknown"
checked_at: datetime | None = None
status_code: int | None = None
latency_ms: float | None = Field(default=None, ge=0)
error: str | None = None
def utcnow() -> datetime:
return datetime.now(timezone.utc)
class MonitorStatus(str, Enum):
unknown = "unknown"
up = "up"
down = "down"
error = "error"
class MonitorCreate(BaseModel):
name: str = Field(min_length=1, max_length=200)
name: Name
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")
def not_empty(self) -> "MonitorUpdate":
if self.name is None and self.url is None:
raise ValueError("at least one field is required")
return self
class MonitorUpdate(MonitorCreate):
pass
class Monitor(BaseModel):
model_config = ConfigDict(from_attributes=True)
model_config = ConfigDict(frozen=True)
id: UUID
name: str
url: AnyHttpUrl
status: MonitorStatus = MonitorStatus.unknown
created_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):
monitor_id: UUID
status_applied: bool = True
class CheckResult(BaseModel):
status: MonitorStatus
checked_at: datetime
latency_ms: float = Field(ge=0)
http_status: int | None = None
error: str | None = None
class HealthResponse(BaseModel):
status: Literal["ok", "ready"]
class StatusResponse(CheckResult):
id: UUID
class ProbeResponse(BaseModel):
status: str