From 4dd966b0b737e9c994149766a1d643b372f52dbe Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sun, 4 Oct 2026 23:54:59 +0400 Subject: [PATCH] =?UTF-8?q?refactor(schemas):=20=D0=BE=D0=B4=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20=D1=84=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=D0=B0=D0=BB=D0=B0=20=D0=B2=20schemas/directories.py=20(R?= =?UTF-8?q?-13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RegionalOfficeOut больше не дублируется в schemas/me.py: в schemas/directories.py определены RegionalOfficeBrief (id, name, is_head_office — форма /me Среза 0) и RegionalOfficeOut(RegionalOfficeBrief) с is_active. /me импортирует RegionalOfficeBrief, тело ответа /me не меняется; в OpenAPI нет двух одноимённых компонентов. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2 --- api/src/api/endpoints/me.py | 5 +++-- api/src/schemas/directories.py | 9 +++++++-- api/src/schemas/me.py | 11 ++--------- api/tests/test_auth_deps.py | 5 +++-- api/tests/test_uow_and_errors.py | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/api/src/api/endpoints/me.py b/api/src/api/endpoints/me.py index 30be0b4..8ca157a 100644 --- a/api/src/api/endpoints/me.py +++ b/api/src/api/endpoints/me.py @@ -9,7 +9,8 @@ from typing import Annotated from fastapi import APIRouter, Depends from src.api.dependencies.auth import Principal, current_user -from src.schemas.me import MeResponse, RegionalOfficeOut +from src.schemas.directories import RegionalOfficeBrief +from src.schemas.me import MeResponse router = APIRouter(tags=["me"]) @@ -24,7 +25,7 @@ async def get_me(principal: Annotated[Principal, Depends(current_user)]) -> MeRe # пользователь найден по email, поэтому он заполнен; пустая строка — лишь для типов (колонка nullable) email=user.email or "", full_name=user.full_name, - regional_office=RegionalOfficeOut(id=office.id, name=office.name, is_head_office=office.is_head_office), + regional_office=RegionalOfficeBrief.model_validate(office), business_segment=user.business_segment, roles=sorted(principal.roles), permissions=sorted(principal.permissions), diff --git a/api/src/schemas/directories.py b/api/src/schemas/directories.py index 711a61e..3b139b2 100644 --- a/api/src/schemas/directories.py +++ b/api/src/schemas/directories.py @@ -19,14 +19,19 @@ def _blank_to_none(value: str | None) -> str | None: return stripped or None -class RegionalOfficeOut(BaseModel): - """Филиал (`GET /directories/regional-offices`).""" +class RegionalOfficeBrief(BaseModel): + """Филиал кратко: профиль `GET /me` (форма ответа Среза 0 — без `is_active`).""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID name: str is_head_office: bool + + +class RegionalOfficeOut(RegionalOfficeBrief): + """Филиал целиком (`GET /directories/regional-offices`, пользователи администратора).""" + is_active: bool diff --git a/api/src/schemas/me.py b/api/src/schemas/me.py index ea11a2a..2365003 100644 --- a/api/src/schemas/me.py +++ b/api/src/schemas/me.py @@ -5,14 +5,7 @@ import uuid from pydantic import BaseModel, ConfigDict from src.models.enums import BusinessSegment - - -class RegionalOfficeOut(BaseModel): - """Филиал пользователя.""" - - id: uuid.UUID - name: str - is_head_office: bool +from src.schemas.directories import RegionalOfficeBrief class MeResponse(BaseModel): @@ -23,7 +16,7 @@ class MeResponse(BaseModel): id: uuid.UUID email: str full_name: str - regional_office: RegionalOfficeOut + regional_office: RegionalOfficeBrief business_segment: BusinessSegment | None roles: list[str] permissions: list[str] diff --git a/api/tests/test_auth_deps.py b/api/tests/test_auth_deps.py index db37f87..1e0da35 100644 --- a/api/tests/test_auth_deps.py +++ b/api/tests/test_auth_deps.py @@ -34,7 +34,8 @@ from src.models.access import AppUser, Role, UserRole from src.models.directories import RegionalOffice from src.models.enums import BusinessSegment from src.repositories.users import AmbiguousUserError, load_principal_data -from src.schemas.me import MeResponse, RegionalOfficeOut +from src.schemas.directories import RegionalOfficeBrief +from src.schemas.me import MeResponse SECRET = "test-secret-for-auth-deps-0123456789abcdef" OTHER_SECRET = "foreign-secret-for-auth-deps-0123456789ab" @@ -487,7 +488,7 @@ async def test_repository_raises_on_ambiguity(seeded: AsyncSession) -> None: def test_me_response_schema_serializes_enum_and_null_segment() -> None: - office = RegionalOfficeOut(id=uuid.uuid4(), name="ГО", is_head_office=True) + office = RegionalOfficeBrief(id=uuid.uuid4(), name="ГО", is_head_office=True) fields: dict[str, Any] = { "id": uuid.uuid4(), "email": "a@b.c", diff --git a/api/tests/test_uow_and_errors.py b/api/tests/test_uow_and_errors.py index cf9d1da..9d95f08 100644 --- a/api/tests/test_uow_and_errors.py +++ b/api/tests/test_uow_and_errors.py @@ -36,7 +36,7 @@ from src.db.db import get_async_session from src.models.directories import RegionalOffice from src.models.enums import AuditAction, AuditSection from src.schemas.common import ErrorDetail, Page, PageParams -from src.schemas.me import RegionalOfficeOut +from src.schemas.directories import RegionalOfficeBrief from src.services.audit import AuditService from tests.pg_helpers import alembic_async @@ -141,8 +141,8 @@ def test_domain_error_extra_cannot_shadow_code_or_message() -> None: def test_page_serializes() -> None: - office = RegionalOfficeOut(id=CONFLICT_ID, name="ГО", is_head_office=True) - page = Page[RegionalOfficeOut](items=[office], total=7, limit=1, offset=3) + office = RegionalOfficeBrief(id=CONFLICT_ID, name="ГО", is_head_office=True) + page = Page[RegionalOfficeBrief](items=[office], total=7, limit=1, offset=3) assert page.model_dump(mode="json") == { "items": [{"id": str(CONFLICT_ID), "name": "ГО", "is_head_office": True}], "total": 7,