048414c5cb
- PATCH/DELETE /api/locations/{person}/{datetime} and /api/poo/{timestamp}
- update only non-PK fields (PK immutable); 404 on missing PK
- delete scoped to exact full PK with rowcount guard (0->404, 1->ok);
no batch/truncate/drop path
- session + CSRF protected; bare ingestion endpoints untouched
- service helpers in app/services/location.py and poo.py; regenerate openapi/
- tests/test_api_record_crud.py
93 lines
2.1 KiB
Python
93 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Location
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class LocationRecord(BaseModel):
|
|
person: str
|
|
datetime: str
|
|
latitude: float
|
|
longitude: float
|
|
altitude: float | None
|
|
|
|
|
|
class LocationsResponse(BaseModel):
|
|
items: list[LocationRecord]
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class LocationUpdateRequest(BaseModel):
|
|
"""PATCH body for a location record — all fields optional; PK fields excluded."""
|
|
|
|
latitude: float | None = None
|
|
longitude: float | None = None
|
|
altitude: float | None = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Poo
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class PooRecord(BaseModel):
|
|
timestamp: str
|
|
status: str
|
|
latitude: float
|
|
longitude: float
|
|
|
|
|
|
class PooResponse(BaseModel):
|
|
items: list[PooRecord]
|
|
limit: int
|
|
offset: int
|
|
|
|
|
|
class PooUpdateRequest(BaseModel):
|
|
"""PATCH body for a poo record — all fields optional; PK field excluded."""
|
|
|
|
status: str | None = None
|
|
latitude: float | None = None
|
|
longitude: float | None = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Public IP
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class PublicIPStateSchema(BaseModel):
|
|
id: int
|
|
current_ipv4: str
|
|
previous_ipv4: str | None
|
|
first_seen_at: datetime
|
|
last_checked_at: datetime
|
|
last_changed_at: datetime | None
|
|
last_check_status: str
|
|
last_check_error: str | None
|
|
last_provider: str | None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class PublicIPHistorySchema(BaseModel):
|
|
id: int
|
|
ipv4: str
|
|
observed_at: datetime
|
|
change_type: str
|
|
provider: str | None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class PublicIPResponse(BaseModel):
|
|
state: PublicIPStateSchema | None
|
|
history: list[PublicIPHistorySchema]
|