From dce9de636bcee7c80c5ff2c22e676bb95323fd59 Mon Sep 17 00:00:00 2001 From: izzyjosh Date: Wed, 11 Sep 2024 15:45:43 +0100 Subject: [PATCH 1/5] feat: added notifications system --- api/v1/responses/.success_response.py.swp | Bin 0 -> 12288 bytes api/v1/routes/__init__.py | 2 ++ api/v1/routes/notification.py | 26 +++++++++++++++++++ api/v1/routes/post.py | 10 +++++--- api/v1/routes/post_comment.py | 5 ++-- api/v1/services/notification.py | 27 +++++++++++++++++++ api/v1/services/post.py | 30 ++++++++++++++++++---- api/v1/services/post_comment.py | 18 ++++++++++--- 8 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 api/v1/responses/.success_response.py.swp create mode 100644 api/v1/routes/notification.py create mode 100644 api/v1/services/notification.py diff --git a/api/v1/responses/.success_response.py.swp b/api/v1/responses/.success_response.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..8d5f1ac0ac7e303d7b2c9ca65d9d890886aa3470 GIT binary patch literal 12288 zcmeI2zi-qq6vthVm?$6^KunLY^w8bq?idh}ZbkeMZB+_Fs?f5{f}pyI01+SpM1Tko0U|&Ih`@;?;Ho9Ihn1fS)n5(s(vdk_(nJJ^ z01+SpM1Tko0U|&IhyW2F0z`la{D%a1#@NTRjNMqqNJX>{+wg%E9yO}MmZFquAnZX80rVc{et?8`h@y`dV|_S`8|6m8bp8y5CI}U1c(3; zAOb{y2oQmPivZv-0{fH|+<&yTx)EBCSFkbJ)pnNZOiUNneMq%(Lb>U4s}rJ zLXdMN+C1Bkp?<99AZ^e&xLvWIZR79j>m|){{~7B{f00lEm@^`2cOUwFXOq!rw)UVO za$gI%Z+UqBz4`aUGLgg4-t-kb(7Dkigl_g>$IgN*D{b7NgLT+7oSl8oqlfB{cNeZr Qx;43ZCDl%6>5^uD0S5dyuK)l5 literal 0 HcmV?d00001 diff --git a/api/v1/routes/__init__.py b/api/v1/routes/__init__.py index 55574b4..f6a07b7 100644 --- a/api/v1/routes/__init__.py +++ b/api/v1/routes/__init__.py @@ -3,6 +3,7 @@ from api.v1.routes.user import users from api.v1.routes.post import posts from api.v1.routes.post_comment import comments +from api.v1.routes.notification import notifications # version 1 routes @@ -12,3 +13,4 @@ version_one.include_router(users) version_one.include_router(posts) version_one.include_router(comments) +version_one.include_router(notifications) diff --git a/api/v1/routes/notification.py b/api/v1/routes/notification.py new file mode 100644 index 0000000..88b072c --- /dev/null +++ b/api/v1/routes/notification.py @@ -0,0 +1,26 @@ +from fastapi import APIRouter, Depends +from fastapi.responses import StreamingResponse +from sqlalchemy.orm import Session +from api.v1.models.user import User +from api.v1.services.user import user_service +from api.v1.services.notification import notification_service +from api.v1.utils.dependencies import get_db +from api.v1.responses.success_response import success_response +from typing import List + +notifications = APIRouter(prefix="/notifications", tags=["notification"]) + +@notifications.get("/sse") +async def sse_endpoint(user: User = Depends(user_service.get_current_user)): + return StreamingResponse(notification_service.event_generator(user.id), media_type="text/event_stream") + + +@notifications.get("") +async def user_notifications(user: User = Depends(user_service.get_current_user), db: Session = Depends(get_db)): + + notifications: List = notification_service.notifications(user=user, db=db) + + return success_response( + status_code=200, + message="Notifications returned successfully", + data=notifications) diff --git a/api/v1/routes/post.py b/api/v1/routes/post.py index a84008d..802a3c2 100644 --- a/api/v1/routes/post.py +++ b/api/v1/routes/post.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, status, WebSocket, WebSocketDisconnect +from fastapi import APIRouter, Depends, status, WebSocket, WebSocketDisconnect, BackgroundTasks from sqlalchemy.orm import Session from typing import List @@ -96,15 +96,16 @@ async def websocket_post_endpoint(websocket: WebSocket): @posts.patch("/{id}/like", status_code=status.HTTP_200_OK) async def like_post( id: str, + background_task: BackgroundTasks = BackgroundTasks(), db: Session = Depends(get_db), user: User = Depends(user_service.get_current_user), ): - liked_post = post_service.like_post(db=db, user=user, post_id=id) + liked_post = post_service.like_post(db=db, user=user, post_id=id, background_task=background_task) return success_response( status_code=status.HTTP_200_OK, - message="Post updated successfully", + message="Post liked successfully", ) @@ -128,11 +129,12 @@ async def get_likes( async def repost( id: str, schema: RepostCreate, + background_task: BackgroundTasks = BackgroundTasks(), db: Session = Depends(get_db), user: User = Depends(user_service.get_current_user), ): - repost = post_service.repost(db=db, post_id=id, schema=schema, user=user) + repost = post_service.repost(db=db, post_id=id, schema=schema, user=user, background_task=background_task) manager.broadcast(repost) diff --git a/api/v1/routes/post_comment.py b/api/v1/routes/post_comment.py index a804bca..e7d589a 100644 --- a/api/v1/routes/post_comment.py +++ b/api/v1/routes/post_comment.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Depends, status, BackgroundTasks from sqlalchemy.orm import Session from api.v1.schemas.post_comment import CreateCommentSchema, CommentResponse from api.v1.schemas.user import UserResponse @@ -34,12 +34,13 @@ async def get_comments( async def create_comment( post_id: str, comment: CreateCommentSchema, + background_task: BackgroundTasks = BackgroumdTasks(), db: Session = Depends(get_db), user: User = Depends(user_service.get_current_user), ): new_comment: CommentResponse = comment_service.create( - db=db, user=user, post_id=post_id, schema=comment + db=db, user=user, post_id=post_id, schema=comment, background_task=background_task ) return success_response( diff --git a/api/v1/services/notification.py b/api/v1/services/notification.py new file mode 100644 index 0000000..c87f287 --- /dev/null +++ b/api/v1/services/notification.py @@ -0,0 +1,27 @@ +from fastapi import BackgroundTasks +from sqlalchemy.orm import Session +from typing import Dict +import asyncio +from api.v1.models.user import User +from api.v1.models.notification import Notification + + +class NotificationService: + + user_event_queues: Dict[str, asyncio.Queue] = {} + + async def event_generator(user_id: str): + if user_id not in user_event_queues: + user_event_queues[user_id] = asyncio.Queue() + + while True: + event = await user_event_queues[user_id].get() + yield f"data: {event}" + + + def notifications(self, user: User, db: Session): + notifications = db.query(Notification).filter(Notification.user_id==user.id).all() + + return notifications + +notification_service = NotificationService() diff --git a/api/v1/services/post.py b/api/v1/services/post.py index 45d90fd..beda255 100644 --- a/api/v1/services/post.py +++ b/api/v1/services/post.py @@ -1,4 +1,4 @@ -from fastapi import HTTPException, status +from fastapi import HTTPException, status, BackgroundTasks from fastapi.encoders import jsonable_encoder from sqlalchemy.orm import Session, joinedload from api.v1.models.post import Post, Like @@ -14,7 +14,8 @@ ) from api.v1.schemas.user import UserResponse from api.v1.services.user import user_service - +from api.v1.models.notification import Notification +from api.v1.services.notification import notification_service class PostService: def get_post(self, db: Session, user: User, post_id: str): @@ -103,11 +104,11 @@ def update(self, db: Session, user: User, post_id: str, schema: UpdatePostSchema return jsonable_encoder(post) - def like_post(self, db: Session, user: User, post_id: str): + def like_post(self, db: Session, user: User, post_id: str, background_task: BackgroundTasks): # get the post post = ( - db.query(Post).filter(Post.id == post_id, Post.user_id == user.id).first() + db.query(Post).filter(Post.id == post_id).first() ) if not post: @@ -131,6 +132,17 @@ def like_post(self, db: Session, user: User, post_id: str): db.add(like) db.commit() + # add notification for like + + notification = Notification(user_id=post.user_id, message=f"{user.username} recently liked your post") + + db.add(notification) + db.commit() + + # background task for sse notification + + background_task.add_task(notification_service.user_event_queues[notification.user_id].put, notification.message) + def get_likes(self, db: Session, post_id: str, user: User): @@ -162,7 +174,7 @@ def get_likes(self, db: Session, post_id: str, user: User): return likes_response - def repost(self, db: Session, post_id: str, user: User, schema: RepostCreate): + def repost(self, db: Session, post_id: str, user: User, schema: RepostCreate, background_task: BackgroundTasks): original_post = self.get_post(db=db, user=user, post_id=post_id) @@ -189,6 +201,14 @@ def repost(self, db: Session, post_id: str, user: User, schema: RepostCreate): new_post_response["user"] = jsonable_encoder(new_post_owner) new_post_response["post"] = original_post_response + # repost notification + notification = Notification(user_id=original_post.user_id,essage=f"{user.username} shared your post") + db.add(notification) + db.commit() + + # background task for notificatiom + background_task.add_task(notification_service.user_event_queues[notification.user_id].put, notification.message) + return RepostResponse(**new_post_response) diff --git a/api/v1/services/post_comment.py b/api/v1/services/post_comment.py index c86a70c..2c9b5e7 100644 --- a/api/v1/services/post_comment.py +++ b/api/v1/services/post_comment.py @@ -1,4 +1,4 @@ -from fastapi import HTTPException, status +from fastapi import HTTPException, status, BackgroundTasks from fastapi.encoders import jsonable_encoder from sqlalchemy.orm import Session from api.v1.schemas.post_comment import ( @@ -10,7 +10,9 @@ from api.v1.models.post_comment import PostComment from api.v1.models.post import Post from api.v1.models.user import User +from api.v1.models.notification import Notification from api.v1.services.user import user_service +from api.v1.services.notification import notification_service class CommentService: @@ -27,7 +29,7 @@ class CommentService: # class methods def create( - self, db: Session, user: User, post_id: str, schema: CreateCommentSchema + self, db: Session, user: User, post_id: str, schema: CreateCommentSchema, background_task: BackgroundTasks ): schema_dict = schema.model_dump() @@ -39,7 +41,7 @@ def create( # get the post post = ( - db.query(Post).filter(Post.user_id == user.id, Post.id == post_id).first() + db.query(Post).filter(Post.id == post_id).first() ) if not post: @@ -58,6 +60,16 @@ def create( encoded = jsonable_encoder(comment) encoded["user"] = response_user + # Comment Notification + notification = Notification(user_id=post.user_id, message=f"{user.username} commented on your post") + + db.add(notification) + db.commit() + + # add background task to send notifcation + background_task.add_task(notification_service.user_event_queues[notification.user_id].put, notification.message) + + return CommentResponse(**encoded) def update( From 9786ba5f7cc9242c71f4f0eb25740ff21ad61442 Mon Sep 17 00:00:00 2001 From: izzyjosh Date: Thu, 12 Sep 2024 15:19:31 +0100 Subject: [PATCH 2/5] feat: added like notification as well as follow notification with SSE login for streaming response --- api/v1/routes/post_comment.py | 2 +- api/v1/routes/user.py | 9 ++++++--- api/v1/services/notification.py | 11 ++++++----- api/v1/services/post.py | 9 +++++++++ api/v1/services/user.py | 12 +++++++++--- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/api/v1/routes/post_comment.py b/api/v1/routes/post_comment.py index e7d589a..13f1222 100644 --- a/api/v1/routes/post_comment.py +++ b/api/v1/routes/post_comment.py @@ -34,7 +34,7 @@ async def get_comments( async def create_comment( post_id: str, comment: CreateCommentSchema, - background_task: BackgroundTasks = BackgroumdTasks(), + background_task: BackgroundTasks = BackgroundTasks(), db: Session = Depends(get_db), user: User = Depends(user_service.get_current_user), ): diff --git a/api/v1/routes/user.py b/api/v1/routes/user.py index 99693d7..52e0666 100644 --- a/api/v1/routes/user.py +++ b/api/v1/routes/user.py @@ -1,5 +1,5 @@ from typing import Annotated -from fastapi import APIRouter, Depends, Query, status +from fastapi import APIRouter, Depends, Query, status, BackgroundTasks from fastapi.encoders import jsonable_encoder from sqlalchemy.orm import Session from api.v1.models.user import User @@ -67,11 +67,13 @@ async def get_users(search: str = "", db: Session = Depends(get_db)): @users.patch("/{followee_id}/follow", summary="Follow a particular user") async def follow( followee_id: str, + background_task: BackgroundTasks = BackgroundTasks(), user: User = Depends(user_service.get_current_user), db: Session = Depends(get_db), ): - user_service.follow_user(db=db, user=user, user_id=followee_id) + user_service.follow_user(db=db, user=user, user_id=followee_id, background_task=background_task) + return success_response( status_code=200, message="User followed successfully", @@ -81,11 +83,12 @@ async def follow( @users.delete("/{followee_id}/unfollow", summary="Unfollow the user with the id") async def unfollow( followee_id: str, + background_task: BackgroundTasks = BackgroundTasks(), user: User = Depends(user_service.get_current_user), db: Session = Depends(get_db), ): - user_service.unfollow_user(db=db, user_id=followee_id, user=user) + user_service.unfollow_user(db=db, user_id=followee_id, user=user, background_task=background_task) return success_response( status_code=200, diff --git a/api/v1/services/notification.py b/api/v1/services/notification.py index c87f287..60bc6b2 100644 --- a/api/v1/services/notification.py +++ b/api/v1/services/notification.py @@ -7,15 +7,16 @@ class NotificationService: + def __init__(self): - user_event_queues: Dict[str, asyncio.Queue] = {} + self.user_event_queues: Dict[str, asyncio.Queue] = {} - async def event_generator(user_id: str): - if user_id not in user_event_queues: - user_event_queues[user_id] = asyncio.Queue() + async def event_generator(self, user_id: str): + if user_id not in self.user_event_queues: + self.user_event_queues[user_id] = asyncio.Queue() while True: - event = await user_event_queues[user_id].get() + event = await self.user_event_queues[user_id].get() yield f"data: {event}" diff --git a/api/v1/services/post.py b/api/v1/services/post.py index beda255..90b5b7e 100644 --- a/api/v1/services/post.py +++ b/api/v1/services/post.py @@ -126,6 +126,15 @@ def like_post(self, db: Session, user: User, post_id: str, background_task: Back if like: db.delete(like) db.commit() + + # notification for unliking a post + notification = Notification(user_id=post.user_id, message=f"{user.username} recently unliked your post") + + db.add(notification) + db.commit() + + background_task.add_task(notification_service.user_event_queues[notification.user_id].put, notification.message) + else: like = Like(user_id=user.id, post_id=post_id) like.liked = True diff --git a/api/v1/services/user.py b/api/v1/services/user.py index 7821c65..a78209a 100644 --- a/api/v1/services/user.py +++ b/api/v1/services/user.py @@ -12,7 +12,7 @@ from api.v1.utils.dependencies import get_db load_dotenv() -from fastapi import Depends, HTTPException, status +from fastapi import Depends, HTTPException, status, BackgroundTasks from fastapi.security import OAuth2PasswordBearer from sqlalchemy.orm import Session, joinedload from sqlalchemy import or_, text @@ -22,6 +22,7 @@ from api.v1.schemas.user import UserCreate, UserUpdateSchema, UserResponse from api.v1.utils.storage import upload from api.v1.models.notification import Notification +from api.v1.services.notification import notification_service oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") hash_context = CryptContext(schemes=["bcrypt"], deprecated="auto") @@ -350,7 +351,7 @@ def fetch_all(self, db: Session, search: str = ""): return jsonable_encoder(users, exclude={"password"}) - def follow_user(self, db: Session, user_id: str, user: User): + def follow_user(self, db: Session, user_id: str, user: User, background_task: BackgroundTasks): followee = db.query(User).filter(User.id == user_id).first() if not followee: @@ -369,7 +370,9 @@ def follow_user(self, db: Session, user_id: str, user: User): db.add(notification) db.commit() - def unfollow_user(self, db: Session, user_id: str, user: User): + background_task.add_task(notification_service.user_event_queues[notification.user_id].put, notification.message) + + def unfollow_user(self, db: Session, user_id: str, user: User, background_task: BackgroundTasks): user_to_unfollow = db.query(User).filter(User.id == user_id).first() if not user_to_unfollow: @@ -389,6 +392,9 @@ def unfollow_user(self, db: Session, user_id: str, user: User): db.add(notification) db.commit() + background_task.add_task(notification_service.user_event_queues[notification.user_id].put, notification.message) + + def followers(self, db: Session, user: User): followers = [ From 653ecb330ee4c608dfa0fd9343001b396f292835 Mon Sep 17 00:00:00 2001 From: izzyjosh Date: Sat, 14 Sep 2024 19:59:20 +0100 Subject: [PATCH 3/5] test: added test file for get notifications endpoint --- api/v1/tests/notification/conftest.py | 22 +++++++++++++ .../notification/test_get_notifications.py | 32 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 api/v1/tests/notification/conftest.py create mode 100644 api/v1/tests/notification/test_get_notifications.py diff --git a/api/v1/tests/notification/conftest.py b/api/v1/tests/notification/conftest.py new file mode 100644 index 0000000..37c4ebc --- /dev/null +++ b/api/v1/tests/notification/conftest.py @@ -0,0 +1,22 @@ +import os +import sys + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))) + +import pytest +import fastapi +from unittest.mock import patch + +@pytest.fixture +def mock_get_notifications(): + with patch("api.v1.services.notification.notification_service.notifications") as get_notifications: + get_notifications.return_value = [ + { + "id": "hhh", + "message": "test notification", + "created_at": "today", + "status": "unread", + }, + ] + yield get_notifications + diff --git a/api/v1/tests/notification/test_get_notifications.py b/api/v1/tests/notification/test_get_notifications.py new file mode 100644 index 0000000..9a746d6 --- /dev/null +++ b/api/v1/tests/notification/test_get_notifications.py @@ -0,0 +1,32 @@ +import os +import sys + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))) + + +from main import app +import pytest +from fastapi.testclient import TestClient +from sqlalchemy.orm import Session + +client = TestClient(app) +endpoint = "/api/v1/notifications" + + +def test_get_notifications( + mock_db_session: Session, + current_user, + access_token, + mock_get_notifications,): + + response = client.get(endpoint, headers={"Authorization": f"Bearer {access_token}"},) + + assert response.status_code == 200 + assert response.json()["data"] == [ + { + "id": "hhh", + "message": "test notification", + "created_at": "today", + "status": "unread", + }, + ] From 1c31140e1d6d138c198f525e9d5687ddd5a53ef8 Mon Sep 17 00:00:00 2001 From: izzyjosh Date: Sat, 14 Sep 2024 20:02:16 +0100 Subject: [PATCH 4/5] fix: reformatted files --- api/v1/models/notification.py | 2 +- api/v1/routes/notification.py | 16 +++++++--- api/v1/services/notification.py | 8 +++-- api/v1/tests/conftest.py | 4 ++- api/v1/tests/notification/conftest.py | 24 ++++++++------ .../notification/test_get_notifications.py | 32 +++++++++++-------- 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/api/v1/models/notification.py b/api/v1/models/notification.py index fdc5c1d..208efb6 100644 --- a/api/v1/models/notification.py +++ b/api/v1/models/notification.py @@ -17,7 +17,7 @@ class Notification(AbstractBaseModel): status: Mapped[str] = mapped_column( SQLAlchemyEnum(NotificationStatus), server_default="unread" ) - + user = relationship("User", back_populates="notifications") def __str__(self): diff --git a/api/v1/routes/notification.py b/api/v1/routes/notification.py index 88b072c..6b66e65 100644 --- a/api/v1/routes/notification.py +++ b/api/v1/routes/notification.py @@ -10,17 +10,23 @@ notifications = APIRouter(prefix="/notifications", tags=["notification"]) + @notifications.get("/sse") async def sse_endpoint(user: User = Depends(user_service.get_current_user)): - return StreamingResponse(notification_service.event_generator(user.id), media_type="text/event_stream") + return StreamingResponse( + notification_service.event_generator(user.id), media_type="text/event_stream" + ) @notifications.get("") -async def user_notifications(user: User = Depends(user_service.get_current_user), db: Session = Depends(get_db)): +async def user_notifications( + user: User = Depends(user_service.get_current_user), db: Session = Depends(get_db) +): notifications: List = notification_service.notifications(user=user, db=db) return success_response( - status_code=200, - message="Notifications returned successfully", - data=notifications) + status_code=200, + message="Notifications returned successfully", + data=notifications, + ) diff --git a/api/v1/services/notification.py b/api/v1/services/notification.py index 60bc6b2..cc83435 100644 --- a/api/v1/services/notification.py +++ b/api/v1/services/notification.py @@ -14,15 +14,17 @@ def __init__(self): async def event_generator(self, user_id: str): if user_id not in self.user_event_queues: self.user_event_queues[user_id] = asyncio.Queue() - + while True: event = await self.user_event_queues[user_id].get() yield f"data: {event}" - def notifications(self, user: User, db: Session): - notifications = db.query(Notification).filter(Notification.user_id==user.id).all() + notifications = ( + db.query(Notification).filter(Notification.user_id == user.id).all() + ) return notifications + notification_service = NotificationService() diff --git a/api/v1/tests/conftest.py b/api/v1/tests/conftest.py index fe16bd0..1917fa4 100644 --- a/api/v1/tests/conftest.py +++ b/api/v1/tests/conftest.py @@ -2,7 +2,9 @@ import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))) +sys.path.insert( + 0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")) +) from fastapi import HTTPException, status import pytest diff --git a/api/v1/tests/notification/conftest.py b/api/v1/tests/notification/conftest.py index 37c4ebc..dcec89c 100644 --- a/api/v1/tests/notification/conftest.py +++ b/api/v1/tests/notification/conftest.py @@ -1,22 +1,26 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))) +sys.path.insert( + 0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")) +) import pytest import fastapi from unittest.mock import patch + @pytest.fixture def mock_get_notifications(): - with patch("api.v1.services.notification.notification_service.notifications") as get_notifications: + with patch( + "api.v1.services.notification.notification_service.notifications" + ) as get_notifications: get_notifications.return_value = [ - { - "id": "hhh", - "message": "test notification", - "created_at": "today", - "status": "unread", - }, - ] + { + "id": "hhh", + "message": "test notification", + "created_at": "today", + "status": "unread", + }, + ] yield get_notifications - diff --git a/api/v1/tests/notification/test_get_notifications.py b/api/v1/tests/notification/test_get_notifications.py index 9a746d6..d661c11 100644 --- a/api/v1/tests/notification/test_get_notifications.py +++ b/api/v1/tests/notification/test_get_notifications.py @@ -1,7 +1,9 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../"))) +sys.path.insert( + 0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")) +) from main import app @@ -14,19 +16,23 @@ def test_get_notifications( - mock_db_session: Session, - current_user, - access_token, - mock_get_notifications,): + mock_db_session: Session, + current_user, + access_token, + mock_get_notifications, +): - response = client.get(endpoint, headers={"Authorization": f"Bearer {access_token}"},) + response = client.get( + endpoint, + headers={"Authorization": f"Bearer {access_token}"}, + ) assert response.status_code == 200 assert response.json()["data"] == [ - { - "id": "hhh", - "message": "test notification", - "created_at": "today", - "status": "unread", - }, - ] + { + "id": "hhh", + "message": "test notification", + "created_at": "today", + "status": "unread", + }, + ] From 394264a5897008aa4fd74e7b791fe831aaec6542 Mon Sep 17 00:00:00 2001 From: izzyjosh Date: Sun, 15 Sep 2024 14:45:42 +0100 Subject: [PATCH 5/5] fix: deleted a file --- api/v1/responses/.success_response.py.swp | Bin 12288 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 api/v1/responses/.success_response.py.swp diff --git a/api/v1/responses/.success_response.py.swp b/api/v1/responses/.success_response.py.swp deleted file mode 100644 index 8d5f1ac0ac7e303d7b2c9ca65d9d890886aa3470..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2zi-qq6vthVm?$6^KunLY^w8bq?idh}ZbkeMZB+_Fs?f5{f}pyI01+SpM1Tko0U|&Ih`@;?;Ho9Ihn1fS)n5(s(vdk_(nJJ^ z01+SpM1Tko0U|&IhyW2F0z`la{D%a1#@NTRjNMqqNJX>{+wg%E9yO}MmZFquAnZX80rVc{et?8`h@y`dV|_S`8|6m8bp8y5CI}U1c(3; zAOb{y2oQmPivZv-0{fH|+<&yTx)EBCSFkbJ)pnNZOiUNneMq%(Lb>U4s}rJ zLXdMN+C1Bkp?<99AZ^e&xLvWIZR79j>m|){{~7B{f00lEm@^`2cOUwFXOq!rw)UVO za$gI%Z+UqBz4`aUGLgg4-t-kb(7Dkigl_g>$IgN*D{b7NgLT+7oSl8oqlfB{cNeZr Qx;43ZCDl%6>5^uD0S5dyuK)l5