- 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
34 lines
986 B
Python
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
|