Coverage for src / links / schemas.py: 97%
29 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-10 22:03 +0200
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-10 22:03 +0200
1from datetime import datetime, timedelta, timezone
2from pydantic import BaseModel, HttpUrl, Field, field_validator
3from typing import Optional
6class LinkBase(BaseModel):
7 original_url: HttpUrl = Field(example="https://vkvideo.ru")
8 custom_alias: Optional[str] = Field(
9 None,
10 min_length=4,
11 max_length=16,
12 pattern="^[a-zA-Z0-9_-]+$",
13 example="my-link"
14 )
15 expire_at: Optional[datetime] = Field(
16 example=f"{(datetime.now(timezone.utc) + timedelta(days=30)).isoformat(timespec='minutes')}"
17 )
19 @field_validator('expire_at')
20 def validate_expire_at(cls, value):
21 if value and value < datetime.now(value.tzinfo):
22 raise ValueError("Expiration date must be in the future")
23 return value.replace(second=0, microsecond=0) if value else None
26class LinkCreate(LinkBase):
27 @field_validator('original_url')
28 def ensure_scheme(cls, url):
29 if not str(url).startswith(('http://', 'https://')):
30 return f"http://{url}"
31 return url
34class LinkResponse(LinkBase):
35 short_id: str = Field(example="abc123")
36 created_at: datetime = Field(example=datetime.now(timezone.utc).isoformat())
39class LinkStatsResponse(LinkResponse):
40 click_count: int = Field(example=42)
43class LinkUpdate(BaseModel):
44 original_url: Optional[HttpUrl] = None
45 expire_at: Optional[datetime] = None
47 @field_validator('expire_at')
48 def round_expire_at(cls, value):
49 return value.replace(second=0, microsecond=0) if value else None