Skip to content

Commit 813c274

Browse files
committed
refactor: added the blog service layer
1 parent 90bcd64 commit 813c274

File tree

8 files changed

+308
-182
lines changed

8 files changed

+308
-182
lines changed

api/v1/routes/blog.py

Lines changed: 33 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import logging
22

33
from fastapi import APIRouter, Depends, HTTPException, Query, status
4-
from sqlalchemy import not_
54
from sqlalchemy.exc import SQLAlchemyError
65
from sqlalchemy.orm import Session
76

87
from api.db.database import get_db
9-
from api.v1.models.blog import Blog
108
from api.v1.schemas.blog import (
119
BlogCreateSchema,
12-
BlogListItemResponseSchema,
1310
BlogListResponseSchema,
1411
BlogResponseSchema,
1512
BlogUpdateSchema,
1613
)
14+
from api.v1.services.blog import BlogService
1715

1816
blog = APIRouter(prefix="/blogs", tags=["Blog"])
1917

@@ -30,45 +28,19 @@ async def create_blog(
3028
db: Session = Depends(get_db),
3129
) -> BlogResponseSchema:
3230
try:
33-
existing_blog = db.query(Blog).filter(Blog.title == blog.title).first()
34-
if existing_blog:
35-
logger.warning(f"Blog post with title '{blog.title}' already exists.")
36-
raise HTTPException(
37-
status_code=status.HTTP_409_CONFLICT,
38-
detail="A blog post with this title already exists.",
39-
)
40-
41-
new_blog = Blog(
42-
title=blog.title,
43-
excerpt=blog.excerpt,
44-
content=blog.content,
45-
image_url=blog.image_url,
46-
)
47-
db.add(new_blog)
48-
db.commit()
49-
db.refresh(new_blog)
50-
logger.info(f"Blog post '{new_blog.title}' created successfully.")
51-
52-
return BlogResponseSchema(
53-
id=new_blog.id,
54-
title=new_blog.title,
55-
excerpt=new_blog.excerpt,
56-
content=new_blog.content,
57-
image_url=new_blog.image_url,
58-
created_at=new_blog.created_at,
59-
updated_at=new_blog.updated_at,
31+
return BlogService.create_blog(db, blog)
32+
except ValueError as e:
33+
logger.warning(str(e))
34+
raise HTTPException(
35+
status_code=status.HTTP_409_CONFLICT,
36+
detail=str(e),
6037
)
61-
62-
except HTTPException as http_err:
63-
raise http_err
64-
65-
except SQLAlchemyError as sql_err:
66-
logger.error(f"Database error occurred: {sql_err}")
38+
except SQLAlchemyError as e:
39+
logger.error(f"Database error occurred: {e}")
6740
raise HTTPException(
6841
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
6942
detail="Database error occurred.",
7043
)
71-
7244
except Exception as e:
7345
logger.error(f"Internal server error: {e}")
7446
raise HTTPException(
@@ -88,41 +60,7 @@ async def list_blog(
8860
db: Session = Depends(get_db),
8961
) -> BlogListResponseSchema:
9062
try:
91-
offset = (page - 1) * page_size
92-
query = (
93-
db.query(Blog)
94-
.filter(not_(Blog.is_deleted))
95-
.order_by(Blog.created_at.desc())
96-
)
97-
total_count = query.count()
98-
blogs = query.offset(offset).limit(page_size).all()
99-
100-
next_page = None
101-
if offset + page_size < total_count:
102-
next_page = f"/api/v1/blogs?page={page + 1}&page_size={page_size}"
103-
104-
prev_page = None
105-
if page > 1:
106-
prev_page = f"/api/v1/blogs?page={page - 1}&page_size={page_size}"
107-
108-
results = [
109-
BlogListItemResponseSchema(
110-
id=blog.id,
111-
title=blog.title,
112-
excerpt=blog.excerpt,
113-
image_url=blog.image_url,
114-
created_at=blog.created_at,
115-
)
116-
for blog in blogs
117-
]
118-
119-
return BlogListResponseSchema(
120-
count=total_count,
121-
next=next_page,
122-
previous=prev_page,
123-
results=results,
124-
)
125-
63+
return BlogService.list_blog(db, page, page_size)
12664
except SQLAlchemyError as e:
12765
logger.error(f"Database error occurred: {e}")
12866
raise HTTPException(
@@ -147,27 +85,13 @@ async def read_blog(
14785
db: Session = Depends(get_db),
14886
) -> BlogResponseSchema:
14987
try:
150-
blog = (
151-
db.query(Blog)
152-
.filter(
153-
Blog.id == id,
154-
not_(Blog.is_deleted),
155-
)
156-
.first()
88+
return BlogService.read_blog(db, id)
89+
except ValueError as e:
90+
logger.warning(str(e))
91+
raise HTTPException(
92+
status_code=status.HTTP_404_NOT_FOUND,
93+
detail=str(e),
15794
)
158-
if not blog:
159-
logger.warning(f"Blog post with ID '{id}' not found.")
160-
raise HTTPException(
161-
status_code=status.HTTP_404_NOT_FOUND,
162-
detail="Blog post not found.",
163-
)
164-
165-
logger.info(f"Blog post with ID '{id}' retrieved successfully.")
166-
return BlogResponseSchema.model_validate(blog.__dict__)
167-
168-
except HTTPException as http_err:
169-
raise http_err
170-
17195
except SQLAlchemyError as e:
17296
logger.error(f"Database error occurred: {e}")
17397
raise HTTPException(
@@ -187,81 +111,29 @@ async def read_blog(
187111
response_model=BlogResponseSchema,
188112
status_code=status.HTTP_200_OK,
189113
)
190-
def update_blog(
114+
async def update_blog(
191115
id: int,
192116
blog_update: BlogUpdateSchema,
193117
db: Session = Depends(get_db),
194118
) -> BlogResponseSchema:
195119
try:
196-
# Fetch the blog post to be updated
197-
blog = (
198-
db.query(Blog)
199-
.filter(
200-
Blog.id == id,
201-
not_(Blog.is_deleted),
202-
)
203-
.first()
204-
)
205-
if not blog:
206-
raise HTTPException(
207-
status_code=status.HTTP_404_NOT_FOUND,
208-
detail="Blog post not found.",
209-
)
210-
211-
# Get the updated data
212-
update_data = blog_update.model_dump(exclude_unset=True)
213-
214-
# Check for title uniqueness
215-
if "title" in update_data and update_data["title"] != blog.title:
216-
existing_blog = (
217-
db.query(Blog)
218-
.filter(
219-
Blog.title == update_data["title"],
220-
not_(Blog.is_deleted),
221-
)
222-
.first()
223-
)
224-
if existing_blog:
225-
logger.warning(
226-
f"Blog post with title '{update_data['title']}' already exists."
227-
)
228-
raise HTTPException(
229-
status_code=status.HTTP_409_CONFLICT,
230-
detail="A blog post with this title already exists.",
231-
)
232-
233-
# Update fields if they are provided
234-
for field, value in update_data.items():
235-
setattr(blog, field, value)
236-
237-
db.commit()
238-
db.refresh(blog)
239-
logger.info(f"Blog post '{blog.title}' updated successfully.")
240-
241-
return BlogResponseSchema(
242-
id=blog.id,
243-
title=blog.title,
244-
excerpt=blog.excerpt,
245-
content=blog.content,
246-
image_url=blog.image_url,
247-
created_at=blog.created_at,
248-
updated_at=blog.updated_at,
120+
return BlogService.update_blog(db, id, blog_update)
121+
except ValueError as e:
122+
logger.warning(str(e))
123+
raise HTTPException(
124+
status_code=status.HTTP_404_NOT_FOUND
125+
if "not found" in str(e).lower()
126+
else status.HTTP_409_CONFLICT,
127+
detail=str(e),
249128
)
250-
251-
except HTTPException as http_err:
252-
raise http_err
253-
254-
except SQLAlchemyError as sql_err:
255-
logger.error(f"Database error occurred: {sql_err}")
256-
db.rollback()
129+
except SQLAlchemyError as e:
130+
logger.error(f"Database error occurred: {e}")
257131
raise HTTPException(
258132
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
259133
detail="Database error occurred.",
260134
)
261-
262135
except Exception as e:
263136
logger.error(f"Internal server error: {e}")
264-
db.rollback()
265137
raise HTTPException(
266138
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
267139
detail="Internal server error.",
@@ -277,39 +149,21 @@ async def delete_blog(
277149
db: Session = Depends(get_db),
278150
) -> None:
279151
try:
280-
blog_to_delete = (
281-
db.query(Blog)
282-
.filter(
283-
Blog.id == id,
284-
not_(Blog.is_deleted),
285-
)
286-
.first()
152+
BlogService.delete_blog(db, id)
153+
except ValueError as e:
154+
logger.warning(str(e))
155+
raise HTTPException(
156+
status_code=status.HTTP_404_NOT_FOUND,
157+
detail=str(e),
287158
)
288-
if not blog_to_delete:
289-
logger.warning(f"Blog post with ID '{id}' not found.")
290-
raise HTTPException(
291-
status_code=status.HTTP_404_NOT_FOUND,
292-
detail="Blog post with given id not found.",
293-
)
294-
295-
blog_to_delete.is_deleted = True
296-
db.commit()
297-
logger.info(f"Blog post '{blog_to_delete.title}' deleted successfully.")
298-
299-
except HTTPException as http_err:
300-
raise http_err
301-
302159
except SQLAlchemyError as e:
303160
logger.error(f"Database error occurred: {e}")
304-
db.rollback()
305161
raise HTTPException(
306162
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
307163
detail="Database error occurred.",
308164
)
309-
310165
except Exception as e:
311166
logger.error(f"Internal server error: {e}")
312-
db.rollback()
313167
raise HTTPException(
314168
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
315169
detail="Internal server error.",

0 commit comments

Comments
 (0)