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:
@@ -1,11 +1,14 @@
|
||||
"""Post repository interface.
|
||||
|
||||
This module extends the base repository interface with post-specific
|
||||
query methods including slug lookup, author filtering, and search.
|
||||
query methods including slug lookup, author filtering, search, and
|
||||
like management.
|
||||
"""
|
||||
|
||||
from abc import abstractmethod
|
||||
from uuid import UUID
|
||||
|
||||
from app.domain.entities.like import PostLike
|
||||
from app.domain.entities.post import Post
|
||||
from app.domain.repositories.base import Repository
|
||||
|
||||
@@ -101,6 +104,38 @@ class PostRepository(Repository[Post]):
|
||||
"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def get_like(self, post_id: UUID, liked_by: str) -> PostLike | None:
|
||||
"""Get a like by post and user/device.
|
||||
|
||||
Args:
|
||||
post_id: UUID of the post.
|
||||
liked_by: User ID or device ID.
|
||||
|
||||
Returns:
|
||||
PostLike if found, None otherwise.
|
||||
"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def add_like(self, like: PostLike) -> None:
|
||||
"""Add a new like.
|
||||
|
||||
Args:
|
||||
like: PostLike entity to add.
|
||||
"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def remove_like(self, post_id: UUID, liked_by: str) -> None:
|
||||
"""Remove a like by post and user/device.
|
||||
|
||||
Args:
|
||||
post_id: UUID of the post.
|
||||
liked_by: User ID or device ID.
|
||||
"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def search(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user