diff --git a/app/api/api_v1/__init__.py b/app/api/api_v1/__init__.py index a9c6bfe..992c4a6 100644 --- a/app/api/api_v1/__init__.py +++ b/app/api/api_v1/__init__.py @@ -2,10 +2,11 @@ from fastapi import APIRouter +from api.api_v1.projects import router as project_router + from .auth import router as auth_router -from .project import router as project_router from .upload_file import router as upload_file_router -from .user import router as user_router +from .users import router as user_router router = APIRouter( prefix="/v1", diff --git a/app/api/api_v1/projects/__init__.py b/app/api/api_v1/projects/__init__.py new file mode 100644 index 0000000..c3d24ce --- /dev/null +++ b/app/api/api_v1/projects/__init__.py @@ -0,0 +1,13 @@ +__all__ = ("router",) +from fastapi import APIRouter + +from .details_views import router as details_router +from .list_view import router as list_router + +router = APIRouter( + prefix="/projects", + tags=["Project"], +) + +router.include_router(list_router) +router.include_router(details_router) diff --git a/app/api/api_v1/projects/details_views.py b/app/api/api_v1/projects/details_views.py new file mode 100644 index 0000000..28efc64 --- /dev/null +++ b/app/api/api_v1/projects/details_views.py @@ -0,0 +1,65 @@ +from dependencies.annotations import ( + AuthUserByAccessTokenDep, + MinioClientDep, + ProjectServiceDep, +) +from fastapi import APIRouter +from schemas.project import ( + ProjectPartialUpdate, + ProjectWithRenderFileFullResponse, + ProjectWithRenderFileResponse, +) +from starlette import status + +router = APIRouter() + + +@router.get( + "/{project_id}", + response_model=ProjectWithRenderFileFullResponse, + status_code=status.HTTP_200_OK, +) +async def get_project( + project_id: int, + user_id: AuthUserByAccessTokenDep, + project_service: ProjectServiceDep, + s3_client: MinioClientDep, +) -> ProjectWithRenderFileFullResponse: + return await project_service.get_by_id( + project_id=project_id, + user_id=user_id, + s3_client=s3_client, + ) + + +@router.patch( + "/{project_id}", + response_model=ProjectWithRenderFileResponse, + status_code=status.HTTP_200_OK, +) +async def partial_update_project( + project_id: int, + user_id: AuthUserByAccessTokenDep, + partial_update_project_data: ProjectPartialUpdate, + project_service: ProjectServiceDep, +) -> ProjectWithRenderFileResponse: + return await project_service.partial_update_project( + project_id, + user_id, + partial_update_project_data, + ) + + +@router.delete( + "/{project_id}", + status_code=status.HTTP_204_NO_CONTENT, +) +async def delete_project( + project_id: int, + user_id: AuthUserByAccessTokenDep, + project_service: ProjectServiceDep, +) -> None: + return await project_service.delete_by_id( + project_id=project_id, + user_id=user_id, + ) diff --git a/app/api/api_v1/project.py b/app/api/api_v1/projects/list_view.py similarity index 54% rename from app/api/api_v1/project.py rename to app/api/api_v1/projects/list_view.py index 8324921..f653293 100644 --- a/app/api/api_v1/project.py +++ b/app/api/api_v1/projects/list_view.py @@ -1,144 +1,89 @@ -from dependencies.annotations import ( - AuthUserByAccessTokenDep, - MinioClientDep, - PaginationPageDep, - PaginationSizeDep, - ProjectServiceDep, -) -from dependencies.auth import get_auth_user_by_access_token -from fastapi import APIRouter, Depends, status -from schemas.project import ( - ProjectPartialUpdate, - ProjectResponseList, - ProjectWithRenderCreate, - ProjectWithRenderFileFullResponse, - ProjectWithRenderFileResponse, - ProjectWithRenderResponse, -) - -router = APIRouter( - prefix="/project", - tags=["Project"], -) - - -@router.get( - "/", - response_model=ProjectResponseList, - status_code=status.HTTP_200_OK, -) -async def get_current_user_projects( - project_service: ProjectServiceDep, - user_id: AuthUserByAccessTokenDep, - size: PaginationSizeDep = 10, - page: PaginationPageDep = 1, -) -> ProjectResponseList: - return await project_service.get_user_projects( - user_id=user_id, - size=size, - page=page, - ) - - -@router.get( - "/public", - response_model=ProjectResponseList, - status_code=status.HTTP_200_OK, - dependencies=[ - Depends(get_auth_user_by_access_token), - ], -) -async def get_public_projects( - project_service: ProjectServiceDep, - size: PaginationSizeDep = 10, - page: PaginationPageDep = 1, -) -> ProjectResponseList: - return await project_service.get_public_projects(size=size, page=page) - - -@router.get( - "/public/user/{user_id}", - response_model=ProjectResponseList, - status_code=status.HTTP_200_OK, - dependencies=[ - Depends(get_auth_user_by_access_token), - ], -) -async def get_user_public_projects( - project_service: ProjectServiceDep, - user_id: int, - size: PaginationSizeDep = 10, - page: PaginationPageDep = 1, -) -> ProjectResponseList: - return await project_service.get_user_public_projects( - user_id=user_id, - size=size, - page=page, - ) - - -@router.post( - "/create", - response_model=ProjectWithRenderResponse, - status_code=status.HTTP_201_CREATED, -) -async def create_project( - user_id: AuthUserByAccessTokenDep, - create_project_data: ProjectWithRenderCreate, - project_service: ProjectServiceDep, -) -> ProjectWithRenderResponse: - return await project_service.create_project( - user_id=user_id, - create_project=create_project_data, - ) - - -@router.patch( - "/update/{project_id}", - response_model=ProjectWithRenderFileResponse, - status_code=status.HTTP_200_OK, -) -async def partial_update_project( - project_id: int, - user_id: AuthUserByAccessTokenDep, - partial_update_project_data: ProjectPartialUpdate, - project_service: ProjectServiceDep, -) -> ProjectWithRenderFileResponse: - return await project_service.partial_update_project( - project_id, - user_id, - partial_update_project_data, - ) - - -@router.delete( - "/delete/{project_id}", - status_code=status.HTTP_204_NO_CONTENT, -) -async def delete_project( - project_id: int, - user_id: AuthUserByAccessTokenDep, - project_service: ProjectServiceDep, -) -> None: - return await project_service.delete_by_id( - project_id=project_id, - user_id=user_id, - ) - - -@router.get( - "/{project_id}", - response_model=ProjectWithRenderFileFullResponse, - status_code=status.HTTP_200_OK, -) -async def get_project( - project_id: int, - user_id: AuthUserByAccessTokenDep, - project_service: ProjectServiceDep, - s3_client: MinioClientDep, -) -> ProjectWithRenderFileFullResponse: - return await project_service.get_by_id( - project_id=project_id, - user_id=user_id, - s3_client=s3_client, - ) +from dependencies.annotations import ( + AuthUserByAccessTokenDep, + PaginationPageDep, + PaginationSizeDep, + ProjectServiceDep, +) +from dependencies.auth import get_auth_user_by_access_token +from fastapi import APIRouter, Depends, status +from schemas.project import ( + ProjectResponseList, + ProjectWithRenderCreate, + ProjectWithRenderResponse, +) + +router = APIRouter() + + +@router.get( + "/", + response_model=ProjectResponseList, + status_code=status.HTTP_200_OK, + dependencies=[ + Depends(get_auth_user_by_access_token), + ], +) +async def get_public_projects( + project_service: ProjectServiceDep, + size: PaginationSizeDep = 10, + page: PaginationPageDep = 1, +) -> ProjectResponseList: + return await project_service.get_public_projects( + size=size, + page=page, + ) + + +@router.post( + "/create", + response_model=ProjectWithRenderResponse, + status_code=status.HTTP_201_CREATED, +) +async def create_project( + user_id: AuthUserByAccessTokenDep, + create_project_data: ProjectWithRenderCreate, + project_service: ProjectServiceDep, +) -> ProjectWithRenderResponse: + return await project_service.create_project( + user_id=user_id, + create_project=create_project_data, + ) + + +@router.get( + "/about-me", + response_model=ProjectResponseList, + status_code=status.HTTP_200_OK, +) +async def get_current_user_projects( + project_service: ProjectServiceDep, + user_id: AuthUserByAccessTokenDep, + size: PaginationSizeDep = 10, + page: PaginationPageDep = 1, +) -> ProjectResponseList: + return await project_service.get_user_projects( + user_id=user_id, + size=size, + page=page, + ) + + +@router.get( + "/user/{user_id}", + response_model=ProjectResponseList, + status_code=status.HTTP_200_OK, + dependencies=[ + Depends(get_auth_user_by_access_token), + ], +) +async def get_user_public_projects( + project_service: ProjectServiceDep, + user_id: int, + size: PaginationSizeDep = 10, + page: PaginationPageDep = 1, +) -> ProjectResponseList: + return await project_service.get_user_public_projects( + user_id=user_id, + size=size, + page=page, + ) diff --git a/app/api/api_v1/user.py b/app/api/api_v1/users.py similarity index 70% rename from app/api/api_v1/user.py rename to app/api/api_v1/users.py index 90fb5c1..afd547a 100644 --- a/app/api/api_v1/user.py +++ b/app/api/api_v1/users.py @@ -1,45 +1,57 @@ -from dependencies.annotations import AuthUserByAccessTokenDep, UserServiceDep -from fastapi import APIRouter, status -from schemas.user import UserResponse, UserUpdate - -router = APIRouter( - prefix="/user/about-me", - tags=["User"], -) - - -@router.get( - "/", - response_model=UserResponse, - status_code=status.HTTP_200_OK, -) -async def get_current_user_profile( - user_id: AuthUserByAccessTokenDep, - user_service: UserServiceDep, -) -> UserResponse: - return await user_service.get_by_id(user_id) - - -@router.put( - "/update", - response_model=UserResponse, - status_code=status.HTTP_200_OK, -) -async def update_current_user_profile( - user_id: AuthUserByAccessTokenDep, - user_service: UserServiceDep, - update_user_data: UserUpdate, -) -> UserResponse: - return await user_service.update_user(user_id, update_user_data) - - -@router.delete( - "/", - response_model=None, - status_code=status.HTTP_200_OK, -) -async def delete_current_user_profile( - user_id: AuthUserByAccessTokenDep, - user_service: UserServiceDep, -) -> None: - await user_service.delete_by_id(user_id) +from dependencies.annotations import AuthUserByAccessTokenDep, UserServiceDep +from fastapi import APIRouter, status +from schemas.user import UserFullResponse, UserResponse, UserUpdate + +router = APIRouter( + prefix="/users", + tags=["User"], +) + + +@router.get( + "/about-me", + response_model=UserFullResponse, + status_code=status.HTTP_200_OK, +) +async def get_current_user_profile( + user_id: AuthUserByAccessTokenDep, + user_service: UserServiceDep, +) -> UserFullResponse: + return await user_service.get_profile_by_id(user_id) + + +@router.put( + "/about-me", + response_model=UserResponse, + status_code=status.HTTP_200_OK, +) +async def update_current_user_profile( + user_id: AuthUserByAccessTokenDep, + user_service: UserServiceDep, + update_user_data: UserUpdate, +) -> UserResponse: + return await user_service.update_user(user_id, update_user_data) + + +@router.delete( + "/about-me", + response_model=None, + status_code=status.HTTP_200_OK, +) +async def delete_current_user_profile( + user_id: AuthUserByAccessTokenDep, + user_service: UserServiceDep, +) -> None: + await user_service.delete_by_id(user_id) + + +@router.get( + "/{user_id}", + response_model=UserResponse, + status_code=status.HTTP_200_OK, +) +async def get_user_by_id( + user_id: int, + user_service: UserServiceDep, +) -> UserResponse: + return await user_service.get_by_id(user_id) diff --git a/app/core/interfaces/kafka.py b/app/core/interfaces/kafka.py deleted file mode 100644 index e35bb8c..0000000 --- a/app/core/interfaces/kafka.py +++ /dev/null @@ -1,50 +0,0 @@ -from abc import ABC, abstractmethod -from collections.abc import Callable - - -class KafkaProducer(ABC): - """ - Интерфейс для реализации продюсера кафка брокера. - """ - - @abstractmethod - def produce( - self, - topic: str, - value: str | bytes | None, - key: str | bytes | None, - partition: int, - callback: Callable | None, # type: ignore[type-arg] - on_delivery: Callable | None, # type: ignore[type-arg] - timestamp: int, - ) -> None: - """ - Метод для отправки сообщения в буфер. - """ - - @abstractmethod - async def flush(self) -> None: - """ - Метод для отправки всех текущих сообщений. - """ - - -class KafkaConsumer(ABC): - """ - Интерфейс для реализации консьюмера кафка брокера. - """ - - @abstractmethod - def subscribe(self, topics: list[str]) -> None: - """ - Метод для подписки консьюмера на события в списке топиков. - """ - - @abstractmethod - async def poll( - self, - timeout: int, # noqa:ASYNC109 - ) -> None: - """ - Получение сообщений с таймаутом в случае пустых топиков. - """ diff --git a/app/schemas/project.py b/app/schemas/project.py index 6808c03..98ccd98 100644 --- a/app/schemas/project.py +++ b/app/schemas/project.py @@ -62,6 +62,7 @@ class ProjectResponse(ProjectBase): visibility: ProjectVisibility status: RenderStatus + user_id: int render_id: int | None id: int diff --git a/app/schemas/user.py b/app/schemas/user.py index 2c49818..c56fe1e 100644 --- a/app/schemas/user.py +++ b/app/schemas/user.py @@ -20,7 +20,6 @@ class UserBase(BaseModel): surname: SurnameConstraint name: NameConstraint username: UsernameConstraint - email: EmailConstraint model_config: ClassVar[ConfigDict] = ConfigDict(from_attributes=True) @@ -41,8 +40,17 @@ class UserUpdate(UserBase): class UserResponse(UserBase): """ - Схема для вывода информации о пользователе. + Схема для вывода информации о пользователе, + которая будет видна другим пользователям. """ id: int registration_date: datetime + + +class UserFullResponse(UserResponse): + """ + Схема для вывода информации о пользователе. + """ + + email: EmailConstraint diff --git a/app/services/user.py b/app/services/user.py index 7ad2c5b..916fc67 100644 --- a/app/services/user.py +++ b/app/services/user.py @@ -5,7 +5,7 @@ from core.logging import get_logger from infrastructure.database.models import User from infrastructure.database.repositories.user import UserRepository -from schemas.user import UserResponse, UserUpdate +from schemas.user import UserFullResponse, UserResponse, UserUpdate logger = get_logger(__name__) @@ -19,6 +19,10 @@ def __init__( self.user_repository = self.unit_of_work.get_repository(UserRepository) async def get_by_id(self, user_id: int) -> UserResponse: + user_full_response = await self.get_profile_by_id(user_id) + return UserResponse.model_validate(user_full_response) + + async def get_profile_by_id(self, user_id: int) -> UserFullResponse: user = await self.user_repository.get_by_id(user_id) if user is None: raise UserIdNotFoundError(user_id) @@ -28,7 +32,7 @@ async def get_by_id(self, user_id: int) -> UserResponse: user_id, user.username, ) - return UserResponse.model_validate(user) + return UserFullResponse.model_validate(user) async def update_user( self,