from bson import ObjectId import bson from fastapi import APIRouter, HTTPException, status from fastapi.params import Depends import pymongo from app.dto import FriendAddDTO from app.models import HTTPError, User, Friend from .utils import friend_not_found, get_current_user, objectid_misformatted import app.config as config # Best workaround found for _id typed as ObjectId (creating Exception bcause JSON doesn't support custom types countrary to BSON, used by Mongo) # also allows to create DTOs at the time, but not at it's best (project structure is chaotic FTM :s) import app.serializers as serializers # Import all serializers (detailed in __init__.py) # Database setup client = pymongo.MongoClient(config.MONGODB_URL, username=config.MONGODB_USERNAME, password=config.MONGODB_PASSWORD) db = client[config.MONGODB_DATABASE] friends_collection = db["friends"] friends_router = APIRouter( prefix="/friend", tags=["Friends"] ) @friends_router.get( path="/{id}", responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def get_friend(id: str, current_user: User = Depends(get_current_user)): try: friend = friends_collection.find_one({"_id": ObjectId(id)}) except bson.errors.InvalidId: objectid_misformatted() if friend is None: friend_not_found() return serializers.friend_serialize(friend,False if friend['user_id']==current_user.uid else True) @friends_router.post( path="/add", responses={401: {"model": HTTPError}, 409: {"model": HTTPError}} ) async def add_friend(friend_to_add: FriendAddDTO, current_user: User = Depends(get_current_user)): friend: Friend = friend_to_add.model_dump() if(current_user.uid == friend["friend_user_id"]): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Cannot add yourself as a friend" ) # Test if already friends if friends_collection.find_one({"user_id": current_user.uid, "friend_user_id": friend["friend_user_id"]}) is not None: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Friend already exists" ) # Test if user exists if db["users"].find_one({"_id": ObjectId(friend["friend_user_id"])}): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found" ) friend["user_id"] = current_user.uid friend["status"] = "pending" friend_id = friends_collection.insert_one(friend).inserted_id return {"id": str(friend_id)} @friends_router.delete( path="/{id}/delete", responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def delete_friend(id: str, current_user: User = Depends(get_current_user)): try: result = friends_collection.delete_one({"_id": ObjectId(id)}) except bson.errors.InvalidId: objectid_misformatted() if result.deleted_count == 0: friend_not_found() return {"message": "Friend deleted"} @friends_router.patch( path="/{id}/accept", responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def accept_friend(id: str, current_user: User = Depends(get_current_user)): try: check_friend = friends_collection.find_one({"_id": ObjectId(id)}) if check_friend is None: friend_not_found() if check_friend["status"] != "pending": raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Friend request already accepted" ) friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "accepted"}}) except bson.errors.InvalidId: objectid_misformatted() return {"message": "Friend request accepted"} @friends_router.post( path="/{id}/deny", responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def deny_friend(id: str, current_user: User = Depends(get_current_user)): try: result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "denied"}}) except bson.errors.InvalidId: objectid_misformatted() if result.matched_count == 0: friend_not_found() return {"message": "Friend request denied"} @friends_router.get( path="s", response_model=list[Friend], responses={401: {"model": HTTPError}} ) async def list_friends(current_user: User = Depends(get_current_user)): return serializers.friends_serialize(friends_collection.find({"user_id": current_user.uid}).to_list(), friends_collection.find({"friend_user_id": current_user.uid}).to_list())