feat: add like/unlike toggle on blog posts with per-user tracking

- PostLike domain entity (post_id, liked_by) with BaseEntity integration
- Post entity: add like_count field (default 0) and to_dict serialization
- PostRepository interface: add get_like, add_like, remove_like methods
- TogglePostLikeUseCase: toggle logic (like → unlike, unlike → like)
- PostResponseDTO/PostResponseSchema: add like_count field
- PostLikeORM model with FK to posts and cascade delete
- SQLAlchemyPostRepository: implement like query/add/remove with ORM mapping
- DI provider registration for TogglePostLikeUseCase
- API endpoint POST /api/v1/posts/{id}/like (auth required)
- Unit tests: PostLike entity, Post.like_count, TogglePostLikeUseCase (7 tests)
- API tests: POST /api/v1/posts/{id}/like (4 tests)
- Test model files: FEATURE_LIKES.md, TEST_MODEL.md updated
This commit is contained in:
2026-05-10 18:24:09 +03:00
parent 4497f452a1
commit 3cf6c94da2
21 changed files with 876 additions and 6 deletions

View File

@@ -20,6 +20,7 @@ from app.presentation.api.deps import (
GetPostDep,
ListPostsDep,
PublishPostDep,
ToggleLikeDep,
UpdatePostDep,
)
from app.presentation.schemas import (
@@ -344,3 +345,30 @@ async def unpublish_post(
"""
result = await use_case.unpublish(post_id, current_user_id, role)
return PostResponseSchema(**result.__dict__)
@router.post(
"/{post_id}/like",
response_model=PostResponseSchema,
summary="Toggle like on a post",
)
async def toggle_like(
post_id: UUID,
use_case: ToggleLikeDep,
current_user_id: CurrentUserDep,
) -> PostResponseSchema:
"""Toggle like/unlike on a post.
If the user already liked the post, the like is removed (unlike).
Otherwise, a new like is added.
Args:
post_id: Unique identifier of the post.
use_case: TogglePostLikeUseCase dependency.
current_user_id: Authenticated user ID.
Returns:
PostResponseSchema with updated like_count.
"""
result = await use_case.execute(post_id, current_user_id)
return PostResponseSchema(**result.__dict__)