Skip to content

Commit f3270b2

Browse files
committed
refactor: привести FastAPI зависимости к стилю Annotated
- Убраны дубли response_model при наличии return type - Переведены Depends-параметры на Annotated - Сохранены текущие контракты и логика роутов
1 parent 2ebeb4f commit f3270b2

6 files changed

Lines changed: 40 additions & 41 deletions

File tree

app/get_or_404.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
async def get_task_or_404(
1818
task_id: UUID,
19-
session: AsyncSession = Depends(get_db),
20-
current_user: User = Depends(get_current_user),
19+
session: Annotated[AsyncSession, Depends(get_db)],
20+
current_user: Annotated[User, Depends(get_current_user)],
2121
) -> Task:
2222
"""Возвращает задачу по id. 404 если не найдена, 403 если чужая."""
2323
task = await task_repo.get_task_by_id(session, task_id)
@@ -32,7 +32,7 @@ async def get_task_or_404(
3232

3333
async def get_tag_or_404(
3434
tag_id: UUID,
35-
session: AsyncSession = Depends(get_db),
35+
session: Annotated[AsyncSession, Depends(get_db)],
3636
) -> Tag:
3737
"""Возвращает тег по id или 404 если не найден."""
3838
tag = await tag_repo.get_tag_by_id(session, tag_id)

app/limits/dependencies.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections.abc import Callable
2+
from typing import Annotated
23

34
from fastapi import Depends, Request
45
from redis.asyncio import Redis
@@ -12,7 +13,7 @@ def rate_limit_by_ip(limit: int, window_seconds: int, scope: str) -> Callable:
1213

1314
async def dependency(
1415
request: Request,
15-
redis: Redis = Depends(get_redis),
16+
redis: Annotated[Redis, Depends(get_redis)],
1617
) -> None:
1718
client_ip = request.client.host if request.client else "unknown"
1819
key = f"rl:{scope}:ip:{client_ip}"

app/routes/auth.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import Annotated
12
from uuid import UUID
23

34
from fastapi import APIRouter, Depends, HTTPException, Response, status
@@ -31,6 +32,11 @@
3132
router = APIRouter(prefix="/auth", tags=["auth"])
3233
tracer = trace.get_tracer(__name__)
3334

35+
DbSession = Annotated[AsyncSession, Depends(get_db)]
36+
RedisClient = Annotated[Redis, Depends(get_redis)]
37+
LoginForm = Annotated[OAuth2PasswordRequestForm, Depends()]
38+
CurrentUserDep = Annotated[User, Depends(get_current_user)]
39+
3440

3541
def _refresh_ttl_seconds() -> int:
3642
"""TTL refresh-токена в секундах из настроек (неделя)"""
@@ -86,7 +92,6 @@ async def _issue_token_pair(user_id: UUID, redis: Redis) -> TokenPair:
8692

8793
@router.post(
8894
"/register",
89-
response_model=UserRead,
9095
status_code=status.HTTP_201_CREATED,
9196
summary="Регистрация пользователя",
9297
dependencies=[
@@ -95,7 +100,7 @@ async def _issue_token_pair(user_id: UUID, redis: Redis) -> TokenPair:
95100
)
96101
async def create_user(
97102
payload: RegisterCreate,
98-
session: AsyncSession = Depends(get_db),
103+
session: DbSession,
99104
) -> UserRead:
100105
try:
101106
new_user = await register_user(session, payload.email, payload.password)
@@ -109,16 +114,15 @@ async def create_user(
109114

110115
@router.post(
111116
"/login",
112-
response_model=TokenPair,
113117
summary="Вход в систему",
114118
dependencies=[
115119
Depends(rate_limit_by_ip(limit=10, window_seconds=60, scope="auth_login"))
116120
],
117121
)
118122
async def login_user(
119-
form_data: OAuth2PasswordRequestForm = Depends(),
120-
session: AsyncSession = Depends(get_db),
121-
redis: Redis = Depends(get_redis),
123+
form_data: LoginForm,
124+
session: DbSession,
125+
redis: RedisClient,
122126
) -> TokenPair:
123127
try:
124128
user = await authenticate_active_user(
@@ -144,16 +148,15 @@ async def login_user(
144148

145149
@router.post(
146150
"/refresh",
147-
response_model=TokenPair,
148151
summary="Обновить access/refresh токены",
149152
dependencies=[
150153
Depends(rate_limit_by_ip(limit=10, window_seconds=60, scope="auth_refresh"))
151154
],
152155
)
153156
async def refresh_token_pair(
154157
payload: RefreshTokenRequest,
155-
session: AsyncSession = Depends(get_db),
156-
redis: Redis = Depends(get_redis),
158+
session: DbSession,
159+
redis: RedisClient,
157160
) -> TokenPair:
158161
user_id, jti = _parse_refresh_token_or_401(payload.refresh_token)
159162

@@ -176,8 +179,8 @@ async def refresh_token_pair(
176179
)
177180
async def logout_user(
178181
payload: RefreshTokenRequest,
179-
session: AsyncSession = Depends(get_db),
180-
redis: Redis = Depends(get_redis),
182+
session: DbSession,
183+
redis: RedisClient,
181184
) -> Response:
182185
user_id, jti = _parse_refresh_token_or_401(payload.refresh_token)
183186

@@ -187,6 +190,6 @@ async def logout_user(
187190
return Response(status_code=status.HTTP_204_NO_CONTENT)
188191

189192

190-
@router.get("/me", response_model=UserRead, summary="Получить текущего пользователя")
191-
async def current_user(current_user: User = Depends(get_current_user)) -> UserRead:
193+
@router.get("/me", summary="Получить текущего пользователя")
194+
async def current_user(current_user: CurrentUserDep) -> UserRead:
192195
return UserRead.model_validate(current_user)

app/routes/tags.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from typing import Annotated
4+
35
from fastapi import APIRouter, Depends, HTTPException, status
46
from sqlalchemy.ext.asyncio import AsyncSession
57

@@ -10,6 +12,7 @@
1012
from app.services.tag import TagAlreadyExists
1113

1214
router = APIRouter(prefix="/tags", tags=["tags"])
15+
DbSession = Annotated[AsyncSession, Depends(get_db)]
1316

1417
_TAG_EXISTS_ERROR = HTTPException(
1518
status_code=status.HTTP_409_CONFLICT,
@@ -19,14 +22,13 @@
1922

2023
@router.post(
2124
"",
22-
response_model=TagRead,
2325
status_code=status.HTTP_201_CREATED,
2426
summary="Создать тег",
2527
)
2628
async def create_tag(
2729
payload: TagCreate,
2830
_: CurrentUser,
29-
session: AsyncSession = Depends(get_db),
31+
session: DbSession,
3032
) -> TagRead:
3133
try:
3234
created = await tag_service.create_tag(session, payload)
@@ -37,20 +39,18 @@ async def create_tag(
3739

3840
@router.get(
3941
"",
40-
response_model=list[TagRead],
4142
summary="Список тегов",
4243
)
4344
async def list_tags(
4445
_: CurrentUser,
45-
session: AsyncSession = Depends(get_db),
46+
session: DbSession,
4647
) -> list[TagRead]:
4748
tags = await tag_service.list_tags(session)
4849
return [TagRead.model_validate(tag) for tag in tags]
4950

5051

5152
@router.get(
5253
"/{tag_id}",
53-
response_model=TagRead,
5454
summary="Получить тег по id",
5555
)
5656
async def get_tag(
@@ -62,14 +62,13 @@ async def get_tag(
6262

6363
@router.patch(
6464
"/{tag_id}",
65-
response_model=TagRead,
6665
summary="Обновить тег",
6766
)
6867
async def update_tag(
6968
payload: TagUpdate,
7069
_: CurrentUser,
7170
tag: TagDep,
72-
session: AsyncSession = Depends(get_db),
71+
session: DbSession,
7372
) -> TagRead:
7473
try:
7574
updated = await tag_service.update_tag(session, tag, payload)
@@ -80,13 +79,12 @@ async def update_tag(
8079

8180
@router.delete(
8281
"/{tag_id}",
83-
response_model=TagDeleted,
8482
summary="Удалить тег",
8583
)
8684
async def delete_tag(
8785
_: CurrentUser,
8886
tag: TagDep,
89-
session: AsyncSession = Depends(get_db),
87+
session: DbSession,
9088
) -> TagDeleted:
9189
tag_id = await tag_service.delete_tag(session, tag)
9290
return TagDeleted(id=tag_id)

app/routes/tasks.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
from typing import Annotated
4+
35
from fastapi import APIRouter, Depends, HTTPException, status
46
from sqlalchemy.ext.asyncio import AsyncSession
57

@@ -12,6 +14,7 @@
1214
from app.services.task import InvalidDueDate
1315

1416
router = APIRouter(prefix="/tasks", tags=["tasks"])
17+
DbSession = Annotated[AsyncSession, Depends(get_db)]
1518

1619
_DUE_DATE_ERROR = HTTPException(
1720
status_code=status.HTTP_400_BAD_REQUEST,
@@ -21,14 +24,13 @@
2124

2225
@router.post(
2326
"",
24-
response_model=TaskRead,
2527
status_code=status.HTTP_201_CREATED,
2628
summary="Создать задачу",
2729
)
2830
async def create_task(
2931
payload: TaskCreate,
3032
current_user: CurrentUser,
31-
session: AsyncSession = Depends(get_db),
33+
session: DbSession,
3234
) -> TaskRead:
3335
try:
3436
task = await task_service.create_task(session, current_user, payload)
@@ -39,20 +41,18 @@ async def create_task(
3941

4042
@router.get(
4143
"",
42-
response_model=list[TaskRead],
4344
summary="Список задач текущего пользователя",
4445
)
4546
async def list_tasks(
4647
current_user: CurrentUser,
47-
session: AsyncSession = Depends(get_db),
48+
session: DbSession,
4849
) -> list[TaskRead]:
4950
tasks = await task_service.get_user_tasks(session, current_user)
5051
return [TaskRead.model_validate(t) for t in tasks]
5152

5253

5354
@router.get(
5455
"/{task_id}",
55-
response_model=TaskRead,
5656
summary="Получить задачу по id",
5757
)
5858
async def get_task(task: TaskDep) -> TaskRead:
@@ -61,13 +61,12 @@ async def get_task(task: TaskDep) -> TaskRead:
6161

6262
@router.patch(
6363
"/{task_id}",
64-
response_model=TaskRead,
6564
summary="Обновить задачу",
6665
)
6766
async def update_task(
6867
payload: TaskUpdate,
6968
task: TaskDep,
70-
session: AsyncSession = Depends(get_db),
69+
session: DbSession,
7170
) -> TaskRead:
7271
try:
7372
updated = await task_service.update_task(session, task, payload)
@@ -78,41 +77,38 @@ async def update_task(
7877

7978
@router.delete(
8079
"/{task_id}",
81-
response_model=TaskDeleted,
8280
summary="Удалить задачу",
8381
)
8482
async def delete_task(
8583
task: TaskDep,
86-
session: AsyncSession = Depends(get_db),
84+
session: DbSession,
8785
) -> TaskDeleted:
8886
task_id = await task_service.delete_task(session, task)
8987
return TaskDeleted(id=task_id)
9088

9189

9290
@router.post(
9391
"/{task_id}/tags/{tag_id}",
94-
response_model=TaskTagLink,
9592
status_code=status.HTTP_201_CREATED,
9693
summary="Привязать тег к задаче",
9794
)
9895
async def attach_tag_to_task(
9996
task: TaskDep,
10097
tag: TagDep,
101-
session: AsyncSession = Depends(get_db),
98+
session: DbSession,
10299
) -> TaskTagLink:
103100
await tag_service.attach_tag_to_task(session, task, tag)
104101
return TaskTagLink(task_id=task.id, tag_id=tag.id)
105102

106103

107104
@router.delete(
108105
"/{task_id}/tags/{tag_id}",
109-
response_model=TaskTagLink,
110106
summary="Отвязать тег от задачи",
111107
)
112108
async def detach_tag_from_task(
113109
task: TaskDep,
114110
tag: TagDep,
115-
session: AsyncSession = Depends(get_db),
111+
session: DbSession,
116112
) -> TaskTagLink:
117113
await tag_service.detach_tag_from_task(session, task, tag)
118114
return TaskTagLink(task_id=task.id, tag_id=tag.id)

app/security/dependences.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""FastAPI зависимости для аутентификации и авторизации."""
22

33
import uuid
4+
from typing import Annotated
45

56
from fastapi import Depends, HTTPException, status
67
from fastapi.security import OAuth2PasswordBearer
@@ -15,8 +16,8 @@
1516

1617

1718
async def get_current_user(
18-
token: str = Depends(oauth2_scheme),
19-
session: AsyncSession = Depends(get_db),
19+
token: Annotated[str, Depends(oauth2_scheme)],
20+
session: Annotated[AsyncSession, Depends(get_db)],
2021
) -> User:
2122
"""Декодирует access-токен и возвращает активного пользователя. 401/403 при ошибке."""
2223
# 1. Проверка токена

0 commit comments

Comments
 (0)