diff --git a/api/v1/routes/comment.py b/api/v1/routes/comment.py index 996dcc41e..3fd1f31de 100644 --- a/api/v1/routes/comment.py +++ b/api/v1/routes/comment.py @@ -12,6 +12,9 @@ LikeSuccessResponse, ReplyCreate, ReplyResponse, + CommentEditResponse, + EditCommentRequest + ) from api.v1.services.blog_comment_reply import reply_service from api.v1.services.comment_dislike import comment_dislike_service @@ -163,3 +166,73 @@ def reply_comment( message="Reply to comment created successfully", data=jsonable_encoder(new_reply) ) + + +@comment.patch("/{comment_id}", response_model=CommentEditResponse) +async def update_comment( + comment_id: str, + request: EditCommentRequest, + db: Annotated[Session, Depends(get_db)], + current_user: Annotated[User, Depends(user_service.get_current_user)] +) -> dict: + """ + PATCH endpoint to allow authenticated users to update their comments. + + Args: + comment_id (str): ID of the comment to be updated. + request_data (CommentUpdateSchema): The new comment content. + db (Session): Database session. + current_user (User): Authenticated user. + + Returns: + dict: Success or error message. + """ + try: + + try: + comment_uuid = UUID(comment_id) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid comment ID format." + ) + + + comment = comment_service.fetch(db=db, id=str(comment_id)) + if not comment: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Comment not found." + ) + + + if comment.user_id != current_user.id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="You do not have permission to edit this comment." + ) + + + updated_comment = comment_service.update( + db=db, id=str(comment_id), content=request.content + ) + + return success_response( + message="Comment updated successfully", + status_code=status.HTTP_200_OK, + data={ + "comment_id": str(comment_id), + "content": updated_comment.content + } + ) + except HTTPException as e: + return JsonResponseDict( + message=e.detail, + status_code=e.status_code + ) + except Exception as e: + return JsonResponseDict( + message="Internal server error.", + error=str(e), + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR + ) \ No newline at end of file diff --git a/api/v1/schemas/comment.py b/api/v1/schemas/comment.py index a2d245189..e461817b6 100644 --- a/api/v1/schemas/comment.py +++ b/api/v1/schemas/comment.py @@ -36,7 +36,24 @@ class CommentDislike(BaseModel): model_config = ConfigDict(from_attributes=True) +class EditComment(BaseModel): + id: str = "" + comment_id: str = "" + user_id: str = "" + ip_address: str = "" + created_at: datetime = "" + updated_at: datetime = "" + model_config = ConfigDict(from_attributes=True) + +class CommentEditResponse(BaseModel): + status_code: int = 201 + message: str + success: bool = True + data: EditComment +class EditCommentRequest(BaseModel): + content: str + class DislikeSuccessResponse(BaseModel): status_code: int = 201 message: str diff --git a/api/v1/services/comment.py b/api/v1/services/comment.py index 1e88b67f5..cf2ed9395 100644 --- a/api/v1/services/comment.py +++ b/api/v1/services/comment.py @@ -45,18 +45,24 @@ def fetch(self, db: Session, id: str): comment = check_model_existence(db, Comment, id) return comment - def update(self, db: Session, id: str, schema): - """Updates a comment""" + def update(self, db: Session, id: str, schema: Optional[dict] = None, content: Optional[str] = None): + """Updates a comment with either schema data or content""" comment = self.fetch(db=db, id=id) + if comment is None: + return None - # Update the fields with the provided schema data - update_data = schema.dict(exclude_unset=True) - for key, value in update_data.items(): - setattr(comment, key, value) + if schema: + update_data = schema.dict(exclude_unset=True) + for key, value in update_data.items(): + setattr(comment, key, value) + + if content: + comment.content = content db.commit() db.refresh(comment) + return comment def delete(self, db: Session, id: str): diff --git a/tests/v1/comment/test_update_comment.py b/tests/v1/comment/test_update_comment.py new file mode 100644 index 000000000..3451b03b4 --- /dev/null +++ b/tests/v1/comment/test_update_comment.py @@ -0,0 +1,67 @@ +import pytest +from fastapi.testclient import TestClient +from main import app # Ensure you import your FastAPI app +from api.db.database import get_db +from unittest.mock import MagicMock +from api.v1.models.user import User +from api.v1.services.comment import comment_service +from api.v1.services.auth import get_current_user + +client = TestClient(app) + +# Override authentication dependency +app.dependency_overrides[get_current_user] = lambda: User(id="user-123") + +@pytest.fixture +def mock_db(): + return MagicMock() + +@pytest.fixture +def mock_current_user(): + return User(id="user-123") + +@pytest.fixture +def mock_comment(): + return MagicMock(id="comment-123", user_id="user-123", content="Old content") + +def test_update_comment_success(mock_db, mock_current_user, mock_comment, monkeypatch): + monkeypatch.setattr(comment_service, "fetch", lambda db, id: mock_comment) + monkeypatch.setattr(comment_service, "update_comments", lambda db, id, content: mock_comment) + + headers = {"Authorization": "Bearer valid_token"} + response = client.patch( + "/api/v1/comments/comment-123", + json={"content": "Updated comment text"}, + headers=headers + ) + + assert response.status_code == 200 + assert response.json()["message"] == "Comment updated successfully" + assert response.json()["data"]["content"] == "Updated comment text" + +def test_update_comment_not_found(mock_db, mock_current_user, monkeypatch): + monkeypatch.setattr(comment_service, "fetch", lambda db, id: None) + + headers = {"Authorization": "Bearer valid_token"} + response = client.patch( + "/api/v1/comments/invalid-id", + json={"content": "Updated comment text"}, + headers=headers + ) + + assert response.status_code == 404 + assert response.json()["message"] == "Comment not found." + +def test_update_comment_unauthorized(mock_db, mock_comment, monkeypatch): + mock_comment.user_id = "another-user" + monkeypatch.setattr(comment_service, "fetch", lambda db, id: mock_comment) + + headers = {"Authorization": "Bearer valid_token"} + response = client.patch( + "/api/v1/comments/comment-123", + json={"content": "Updated comment text"}, + headers=headers + ) + + assert response.status_code == 403 + assert response.json()["message"] == "You do not have permission to edit this comment."