arm-core/api/src/schemas/common.py
Andrey Epifancev 5976986e1b feat(api): транзакция на запрос, RequestAudit, доменные ошибки 404/409/422, Page
- get_async_session: одна транзакция на запрос (commit при успехе, rollback при исключении), R-1;
  объявляется только через SessionDep (scope="function") — commit до отправки ответа, одна сессия
  на все зависимости запроса; current_user и dev-эмитент переведены на SessionDep.
- DomainError/NotFoundError/ConflictError/DomainValidationError + обработчик
  {"detail": {"code", "message", ...}}; ответы HTTPException и ошибок БД не меняются.
- RequestAudit/get_request_audit: AuditService.record с user_id, ip_address, user_agent запроса.
- schemas/common.py: Page[T], PageParams (limit 1..200, по умолчанию 50; offset >= 0), ErrorDetail.
- tests/test_uow_and_errors.py: формат ошибок, пагинация, аудит, scope сессии во всех маршрутах,
  настоящий commit/rollback на временной БД, commit до http.response.start, ошибка commit -> 409.
- План: Task 2 отмечена; tasks.md: 1.2 отмечен.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-04 21:03:07 +04:00

34 lines
986 B
Python

"""Общие схемы API: страница списка, параметры пагинации, тело доменной ошибки (spec directories-api)."""
from typing import Generic, TypeVar
from pydantic import BaseModel, Field
T = TypeVar("T")
DEFAULT_PAGE_LIMIT = 50
MAX_PAGE_LIMIT = 200
class Page(BaseModel, Generic[T]):
"""Страница списка: элементы, общее число записей по фильтру, запрошенные `limit`/`offset`."""
items: list[T]
total: int
limit: int
offset: int
class PageParams(BaseModel):
"""Параметры пагинации из query: `Annotated[PageParams, Query()]`."""
limit: int = Field(DEFAULT_PAGE_LIMIT, ge=1, le=MAX_PAGE_LIMIT)
offset: int = Field(0, ge=0)
class ErrorDetail(BaseModel):
"""Тело `detail` доменной ошибки (для `responses` в OpenAPI): `{"code": "...", "message": "..."}`."""
code: str
message: str