diff --git a/src/main.py b/src/main.py index 9905571b..e1bec4b5 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,5 @@ from fastapi import APIRouter -from src.routers import courses, users, auth, houses, orgs, roles +from src.routers import collections, courses, users, auth, houses, orgs, roles from starlette.responses import FileResponse @@ -13,3 +13,5 @@ global_router.include_router(houses.router, prefix="/houses", tags=["houses"]) global_router.include_router(orgs.router, prefix="/orgs", tags=["orgs"]) global_router.include_router(roles.router, prefix="/roles", tags=["roles"]) global_router.include_router(courses.router, prefix="/courses", tags=["courses"]) +global_router.include_router(collections.router, prefix="/collections", tags=["collections"]) + diff --git a/src/routers/collections.py b/src/routers/collections.py new file mode 100644 index 00000000..dc8f30a0 --- /dev/null +++ b/src/routers/collections.py @@ -0,0 +1,48 @@ +from fastapi import APIRouter, Depends +from src.services.auth import get_current_user +from src.services.users import User +from src.services.collections import Collection, create_collection, get_collection, get_collections, update_collection, delete_collection + + +router = APIRouter() + + +@router.post("/") +async def api_create_collection(collection_object: Collection, current_user: User = Depends(get_current_user)): + """ + Create new Collection + """ + return await create_collection(collection_object, current_user) + + +@router.get("/{collection_id}") +async def api_get_collection(collection_id: str, current_user: User = Depends(get_current_user)): + """ + Get single collection by ID + """ + return await get_collection(collection_id, current_user) + + +@router.get("/page/{page}/limit/{limit}") +async def api_get_collection_by(page: int, limit: int, current_user: User = Depends(get_current_user)): + """ + Get collections by page and limit + """ + return await get_collections(page, limit, current_user) + + +@router.put("/{collection_id}") +async def api_update_collection(collection_object: Collection, collection_id: str, current_user: User = Depends(get_current_user)): + """ + Update collection by ID + """ + return await update_collection(collection_object, collection_id, current_user) + + +@router.delete("/{collection_id}") +async def api_delete_collection(collection_id: str, current_user: User = Depends(get_current_user)): + """ + Delete collection by ID + """ + + return await delete_collection(collection_id, current_user) diff --git a/src/services/collections.py b/src/services/collections.py new file mode 100644 index 00000000..fdfce148 --- /dev/null +++ b/src/services/collections.py @@ -0,0 +1,148 @@ +import json +from typing import List +from uuid import uuid4 +from pydantic import BaseModel +from src.services.users import User +from src.services.database import create_config_collection, check_database, create_database, learnhouseDB, learnhouseDB +from src.services.security import * +from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks +from datetime import datetime + +#### Classes #################################################### + + +class Collection(BaseModel): + name: str + description: str + elements: List[str] + + +class CollectionInDB(Collection): + collection_id: str + + +#### Classes #################################################### + + +async def get_collection(collection_id: str, current_user: User): + await check_database() + collections = learnhouseDB["collections"] + + collection = collections.find_one({"collection_id": collection_id}) + + # verify collection rights + await verify_collection_rights(collection_id, current_user, "read") + + if not collection: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") + + collection = Collection(**collection) + return collection + + +async def create_collection(collection_object: Collection, current_user: User): + await check_database() + collections = learnhouseDB["collections"] + + # find if collection already exists using name + isCollectionNameAvailable = collections.find_one({"name": collection_object.name}) + + await verify_collection_rights("*", current_user, "create") + + if isCollectionNameAvailable: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Collection name already exists") + + # generate collection_id with uuid4 + collection_id = str(f"collection_{uuid4()}") + + collection = CollectionInDB(collection_id=collection_id, **collection_object.dict()) + + collection_in_db = collections.insert_one(collection.dict()) + + if not collection_in_db: + raise HTTPException( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") + + return collection.dict() + + +async def update_collection(collection_object: Collection, collection_id: str, current_user: User): + await check_database() + + # verify collection rights + await verify_collection_rights(collection_id, current_user, "update") + + collections = learnhouseDB["collections"] + + collection = collections.find_one({"collection_id": collection_id}) + + + if not collection: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") + + updated_collection = CollectionInDB( + collection_id=collection_id, **collection_object.dict()) + + collections.update_one({"collection_id": collection_id}, {"$set": updated_collection.dict()}) + + return Collection(**updated_collection.dict()) + + +async def delete_collection(collection_id: str, current_user: User): + await check_database() + + await verify_collection_rights(collection_id, current_user,"delete") + + collections = learnhouseDB["collections"] + + collection = collections.find_one({"collection_id": collection_id}) + + if not collection: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") + + isDeleted = collections.delete_one({"collection_id": collection_id}) + + if isDeleted: + return {"detail": "collection deleted"} + else: + raise HTTPException( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") + + +async def get_collections(page: int = 1, limit: int = 10): + ## TODO : auth + await check_database() + collections = learnhouseDB["collections"] + + # get all collections from database + all_collections = collections.find().sort("name", 1).skip(10 * (page - 1)).limit(limit) + + # TODO : Check rights for each collection + return [json.loads(json.dumps(collection, default=str)) for collection in all_collections] + + +#### Security #################################################### + +async def verify_collection_rights(collection_id: str, current_user: User, action: str): + await check_database() + collections = learnhouseDB["collections"] + + collection = collections.find_one({"collection_id": collection_id}) + + if not collection: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") + + hasRoleRights = await verify_user_rights_with_roles(action, current_user.user_id, collection_id) + + if not hasRoleRights: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this Collection") + + return True + +#### Security ####################################################