Skip to content

Commit 4939d87

Browse files
committed
feat: Blog Delete API Endpoint [Issue #2]
1 parent f3043c7 commit 4939d87

File tree

4 files changed

+95
-15
lines changed

4 files changed

+95
-15
lines changed

api/v1/routes/blog.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,54 @@ async def read_blog(
163163
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
164164
detail="Database error occurred.",
165165
)
166-
except ValueError as e:
167-
logger.error(f"Invalid blog post ID '{id}': {e}")
166+
except Exception as e:
167+
logger.error(f"Unexpected error occurred: {e}")
168+
raise HTTPException(
169+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
170+
detail="Unexpected error occurred.",
171+
)
172+
173+
174+
@blog.delete(
175+
"/{id}",
176+
status_code=status.HTTP_204_NO_CONTENT,
177+
)
178+
async def delete_blog(
179+
id: int,
180+
db: Session = Depends(get_db),
181+
) -> None:
182+
try:
183+
blog_to_delete = (
184+
db.query(Blog)
185+
.filter(
186+
Blog.id == id,
187+
not_(Blog.is_deleted),
188+
)
189+
.first()
190+
)
191+
if not blog_to_delete:
192+
logger.warning(f"Blog post with ID '{id}' not found.")
193+
raise HTTPException(
194+
status_code=status.HTTP_404_NOT_FOUND,
195+
detail="Blog post with given id not found.",
196+
)
197+
198+
blog_to_delete.is_deleted = True
199+
db.commit()
200+
logger.info(f"Blog post '{blog_to_delete.title}' deleted successfully.")
201+
202+
except SQLAlchemyError as e:
203+
logger.error(f"Database error occurred: {e}")
204+
db.rollback()
168205
raise HTTPException(
169-
status_code=status.HTTP_400_BAD_REQUEST,
170-
detail="Invalid blog post ID.",
206+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
207+
detail="Database error occurred.",
171208
)
209+
172210
except Exception as e:
173211
logger.error(f"Unexpected error occurred: {e}")
212+
db.rollback()
174213
raise HTTPException(
175214
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
176-
detail="Unexpected error occurred.",
215+
detail="Internal server error.",
177216
)

tests/v1/blog/test_create_blog.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,21 +108,17 @@ def test_create_blog_internal_server_error(db_session_mock):
108108
assert response.json()["detail"] == "Internal server error."
109109

110110

111-
def test_create_blog_invalid_data(db_session_mock):
111+
def test_create_blog_invalid_data():
112112
"""Sends invalid data (e.g., empty title) and checks for validation errors."""
113113
invalid_blog_data = {
114-
"title": "", # Title is required and cannot be empty
114+
"title": "",
115115
"excerpt": "A summary of the blog post...",
116116
"content": "The content of the blog post...",
117117
"image_url": "image-url-link",
118118
}
119119

120120
response = client.post("/api/v1/blogs", json=invalid_blog_data)
121-
122-
# Unprocessable Entity (validation error)
123121
assert response.status_code == 422
124-
response_data = response.json()
125-
assert "detail" in response_data
126122

127123

128124
def test_create_blog_boundary_testing(db_session_mock):

tests/v1/blog/test_delete_blog.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from datetime import datetime
2+
from unittest.mock import MagicMock
3+
4+
import pytest
5+
from fastapi.testclient import TestClient
6+
7+
from api.db.database import get_db
8+
from api.v1.models.blog import Blog
9+
from main import app
10+
11+
client = TestClient(app)
12+
13+
14+
@pytest.fixture
15+
def db_session_mock():
16+
return MagicMock()
17+
18+
19+
@pytest.fixture(autouse=True)
20+
def override_get_db(db_session_mock):
21+
app.dependency_overrides[get_db] = lambda: db_session_mock
22+
yield
23+
app.dependency_overrides[get_db] = None
24+
25+
26+
def test_successful_soft_delete_blog_post(db_session_mock):
27+
blog = Blog(
28+
id=1,
29+
title="Test Blog Post",
30+
excerpt="This is a test excerpt",
31+
content="This is the content of the test blog post",
32+
image_url="http://example.com/image.png",
33+
created_at=datetime(2024, 7, 22, 12, 0, 0),
34+
updated_at=datetime(2024, 7, 22, 12, 0, 0),
35+
is_deleted=False,
36+
)
37+
38+
db_session_mock.query().filter().first.return_value = blog
39+
40+
response = client.delete("/api/v1/blogs/1")
41+
42+
assert response.status_code == 204
43+
db_session_mock.commit.assert_called_once()
44+
45+
46+
def test_delete_invalid_blog_id(db_session_mock):
47+
response = client.delete("/api/v1/blogs/abc")
48+
49+
assert response.status_code == 422

tests/v1/blog/test_read_blog.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,3 @@ def test_invalid_blog_post_id():
5353
response = client.get("/api/v1/blogs/abc")
5454

5555
assert response.status_code == 422
56-
data = response.json()
57-
assert "detail" in data
58-
assert isinstance(data["detail"], list)
59-
assert any("msg" in error for error in data["detail"])

0 commit comments

Comments
 (0)