From 83effc80c94e0fa96526bb0e3286af0c835ee5ef Mon Sep 17 00:00:00 2001 From: BlessOnyi Date: Fri, 28 Feb 2025 20:47:48 +0100 Subject: [PATCH 1/3] Updated requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e8b8a8e48..e66f04156 100644 --- a/requirements.txt +++ b/requirements.txt @@ -110,7 +110,7 @@ typing_extensions==4.12.2 urllib3==2.2.2 uuid7==0.1.0 uvicorn==0.30.3 -uvloop==0.19.0 +# uvloop==0.19.0 virtualenv==20.26.3 watchfiles==0.22.0 webencodings==0.5.1 From d41301f78657aae51cab9111caf0803921fc5081 Mon Sep 17 00:00:00 2001 From: BlessOnyi Date: Sat, 1 Mar 2025 15:39:04 +0100 Subject: [PATCH 2/3] committing feat-endpoint-edit-comment --- tests/v1/comment/test_update_comment.py | 67 +++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 tests/v1/comment/test_update_comment.py 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." From 1640721e1a238e01f61181071add0952eb6760e2 Mon Sep 17 00:00:00 2001 From: BlessOnyi Date: Sat, 1 Mar 2025 15:45:12 +0100 Subject: [PATCH 3/3] committing feat-endpoint-edit-comment --- api/v1/routes/comment.py | 75 ++++++++++++++++++++++++++++++++++++++ api/v1/schemas/comment.py | 20 ++++++++++ api/v1/schemas/plans.py | 6 +-- api/v1/schemas/stripe.py | 6 +-- api/v1/services/comment.py | 19 ++++++++++ 5 files changed, 120 insertions(+), 6 deletions(-) diff --git a/api/v1/routes/comment.py b/api/v1/routes/comment.py index 2d512151f..56e02dda4 100644 --- a/api/v1/routes/comment.py +++ b/api/v1/routes/comment.py @@ -11,6 +11,9 @@ DislikeSuccessResponse, CommentDislike, LikeSuccessResponse, + CommentEditResponse, + EditCommentRequest + ) from api.v1.services.comment_dislike import comment_dislike_service from api.v1.services.comment import comment_service @@ -139,3 +142,75 @@ async def delete_comment( error=str(e), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR ) + + + + +@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_comments( + 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 c865a747b..69d11166e 100644 --- a/api/v1/schemas/comment.py +++ b/api/v1/schemas/comment.py @@ -26,6 +26,26 @@ class CommentSuccessResponse(BaseModel): data: CommentData +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 CommentDislike(BaseModel): id: str = "" comment_id: str = "" diff --git a/api/v1/schemas/plans.py b/api/v1/schemas/plans.py index 18bddb4a3..e6244a61c 100644 --- a/api/v1/schemas/plans.py +++ b/api/v1/schemas/plans.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, validator +from pydantic import BaseModel, field_validator from typing import List, Optional from datetime import datetime @@ -14,14 +14,14 @@ class CreateBillingPlanSchema(BaseModel): organisation_id: str features: List[str] - @validator("price") + @field_validator("price") def adjust_price(cls, value, values): duration = values.get("duration") if duration == "yearly": value = value * 12 * 0.8 # Multiply by 12 and apply a 20% discount return value - @validator("duration") + @field_validator("duration") def validate_duration(cls, value): v = value.lower() if v not in ["monthly", "yearly"]: diff --git a/api/v1/schemas/stripe.py b/api/v1/schemas/stripe.py index 7bded814e..7f1e1e744 100644 --- a/api/v1/schemas/stripe.py +++ b/api/v1/schemas/stripe.py @@ -1,6 +1,6 @@ from typing import List, Optional -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, Field, field_validator class PaymentInfo(BaseModel): @@ -9,13 +9,13 @@ class PaymentInfo(BaseModel): exp_year: int cvc: str = Field(..., min_length=3, max_length=4) - @validator('card_number') + @field_validator('card_number') def card_number_validator(cls, v): if not v.isdigit() or len(v) != 16: raise ValueError('Card number must be 16 digits') return v - @validator('cvc') + @field_validator('cvc') def cvc_validator(cls, v): if not v.isdigit() or not (3 <= len(v) <= 4): raise ValueError('CVC must be 3 or 4 digits') diff --git a/api/v1/services/comment.py b/api/v1/services/comment.py index 1e88b67f5..e5ed67c37 100644 --- a/api/v1/services/comment.py +++ b/api/v1/services/comment.py @@ -58,6 +58,25 @@ def update(self, db: Session, id: str, schema): db.commit() db.refresh(comment) return comment + + def update_comments(self, db: Session, id: str, content: str): + """Updates a comment""" + + comment = self.fetch(db=db, id=id) + # Update the fields with the provided schema data + if comment is None: + return None + + # Update the comment's content field + comment.content = content + + # Commit the changes to the database + db.commit() + db.refresh(comment) + + return comment + + def delete(self, db: Session, id: str): """Deletes a comment"""