feat: org scope collections

This commit is contained in:
swve 2023-06-19 17:51:21 +02:00
parent 759a144fe9
commit d4497e03fb
6 changed files with 126 additions and 54 deletions

View file

@ -9,10 +9,7 @@ import React from 'react'
const CollectionAdminEditsArea = (props: any) => { const CollectionAdminEditsArea = (props: any) => {
const org_roles_values = ["admin", "owner"]; const org_roles_values = ["admin", "owner"];
const user_roles_values = ["role_admin"]; const user_roles_values = ["role_admin"];
console.log("props: ", props);
const auth: any = React.useContext(AuthContext); const auth: any = React.useContext(AuthContext);
console.log("auth: ", auth);
// this is amazingly terrible code, but gotta release that MVP // this is amazingly terrible code, but gotta release that MVP

View file

@ -22,7 +22,6 @@ export async function generateMetadata(
const access_token_cookie: any = cookieStore.get('access_token_cookie'); const access_token_cookie: any = cookieStore.get('access_token_cookie');
// Get Org context information // Get Org context information
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
return { return {
title: `Collections — ${org.name}`, title: `Collections — ${org.name}`,
description: `Collections of courses from ${org.name}`, description: `Collections of courses from ${org.name}`,
@ -38,10 +37,9 @@ const CollectionsPage = async (params: any) => {
const cookieStore = cookies(); const cookieStore = cookies();
const access_token_cookie: any = cookieStore.get('access_token_cookie'); const access_token_cookie: any = cookieStore.get('access_token_cookie');
const orgslug = params.params.orgslug; const orgslug = params.params.orgslug;
const collections = await getOrgCollectionsWithAuthHeader(access_token_cookie ? access_token_cookie.value : null); const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] });
const org_id = org.org_id;
const collections = await getOrgCollectionsWithAuthHeader(org_id, access_token_cookie ? access_token_cookie.value : null);
return ( return (
<div className="max-w-7xl mx-auto px-4 py-10" > <div className="max-w-7xl mx-auto px-4 py-10" >

View file

@ -35,7 +35,8 @@ const OrgHomePage = async (params: any) => {
const access_token_cookie: any = cookieStore.get('access_token_cookie'); const access_token_cookie: any = cookieStore.get('access_token_cookie');
const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null); const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null);
const collections = await getOrgCollectionsWithAuthHeader(access_token_cookie ? access_token_cookie.value : null); const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] });
const collections = await getOrgCollectionsWithAuthHeader(org.org_id, access_token_cookie ? access_token_cookie.value : null);
// function to remove "course_" from the course_id // function to remove "course_" from the course_id

View file

@ -19,7 +19,6 @@ export async function createCollection(collection: any) {
return res; return res;
} }
// Get a colletion by id // Get a colletion by id
export async function getCollectionById(collection_id: any) { export async function getCollectionById(collection_id: any) {
const result: any = await fetch(`${getAPIUrl()}collections/${collection_id}`, { next: { revalidate: 10 } }); const result: any = await fetch(`${getAPIUrl()}collections/${collection_id}`, { next: { revalidate: 10 } });
@ -41,8 +40,8 @@ export async function getOrgCollections() {
return res; return res;
} }
export async function getOrgCollectionsWithAuthHeader(access_token: string) { export async function getOrgCollectionsWithAuthHeader(org_id: string, access_token: string) {
const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, RequestBodyWithAuthHeader("GET", null, { revalidate: 3 }, access_token)); const result: any = await fetch(`${getAPIUrl()}collections/org_id/${org_id}/page/1/limit/10`, RequestBodyWithAuthHeader("GET", null, { revalidate: 3 }, access_token));
const res = await errorHandling(result); const res = await errorHandling(result);
return res; return res;
} }

View file

@ -1,14 +1,25 @@
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from src.security.auth import get_current_user from src.security.auth import get_current_user
from src.services.users.users import PublicUser from src.services.users.users import PublicUser
from src.services.courses.collections import Collection, create_collection, get_collection, get_collections, update_collection, delete_collection from src.services.courses.collections import (
Collection,
create_collection,
get_collection,
get_collections,
update_collection,
delete_collection,
)
router = APIRouter() router = APIRouter()
@router.post("/") @router.post("/")
async def api_create_collection(request: Request,collection_object: Collection, current_user: PublicUser = Depends(get_current_user)): async def api_create_collection(
request: Request,
collection_object: Collection,
current_user: PublicUser = Depends(get_current_user),
):
""" """
Create new Collection Create new Collection
""" """
@ -16,31 +27,52 @@ async def api_create_collection(request: Request,collection_object: Collection,
@router.get("/{collection_id}") @router.get("/{collection_id}")
async def api_get_collection(request: Request,collection_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_get_collection(
request: Request,
collection_id: str,
current_user: PublicUser = Depends(get_current_user),
):
""" """
Get single collection by ID Get single collection by ID
""" """
return await get_collection(request, collection_id, current_user) return await get_collection(request, collection_id, current_user)
@router.get("/page/{page}/limit/{limit}") @router.get("/org_id/{org_id}/page/{page}/limit/{limit}")
async def api_get_collections_by(request: Request,page: int, limit: int, current_user: PublicUser = Depends(get_current_user)): async def api_get_collections_by(
request: Request,
page: int,
limit: int,
org_id: str,
current_user: PublicUser = Depends(get_current_user),
):
""" """
Get collections by page and limit Get collections by page and limit
""" """
return await get_collections(request, page, limit) return await get_collections(request, org_id, current_user, page, limit)
@router.put("/{collection_id}") @router.put("/{collection_id}")
async def api_update_collection(request: Request,collection_object: Collection, collection_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_update_collection(
request: Request,
collection_object: Collection,
collection_id: str,
current_user: PublicUser = Depends(get_current_user),
):
""" """
Update collection by ID Update collection by ID
""" """
return await update_collection(request, collection_object, collection_id, current_user) return await update_collection(
request, collection_object, collection_id, current_user
)
@router.delete("/{collection_id}") @router.delete("/{collection_id}")
async def api_delete_collection(request: Request,collection_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_delete_collection(
request: Request,
collection_id: str,
current_user: PublicUser = Depends(get_current_user),
):
""" """
Delete collection by ID Delete collection by ID
""" """

View file

@ -2,7 +2,7 @@ from typing import List
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from src.services.users.users import PublicUser from src.services.users.users import PublicUser
from src.security.security import * from src.security.security import verify_user_rights_with_roles
from fastapi import HTTPException, status, Request from fastapi import HTTPException, status, Request
#### Classes #################################################### #### Classes ####################################################
@ -25,7 +25,10 @@ class CollectionInDB(Collection):
# CRUD # CRUD
#################################################### ####################################################
async def get_collection(request: Request,collection_id: str, current_user: PublicUser):
async def get_collection(
request: Request, collection_id: str, current_user: PublicUser
):
collections = request.app.db["collections"] collections = request.app.db["collections"]
collection = await collections.find_one({"collection_id": collection_id}) collection = await collections.find_one({"collection_id": collection_id})
@ -35,7 +38,8 @@ async def get_collection(request: Request,collection_id: str, current_user: Publ
if not collection: if not collection:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
collection = Collection(**collection) collection = Collection(**collection)
@ -44,46 +48,56 @@ async def get_collection(request: Request,collection_id: str, current_user: Publ
courseids = [course for course in collection.courses] courseids = [course for course in collection.courses]
collection.courses = [] collection.courses = []
collection.courses = courses.find( collection.courses = courses.find({"course_id": {"$in": courseids}}, {"_id": 0})
{"course_id": {"$in": courseids}}, {'_id': 0})
collection.courses = [course for course in await collection.courses.to_list(length=100)]
collection.courses = [
course for course in await collection.courses.to_list(length=100)
]
return collection return collection
async def create_collection(request: Request,collection_object: Collection, current_user: PublicUser): async def create_collection(
request: Request, collection_object: Collection, current_user: PublicUser
):
collections = request.app.db["collections"] collections = request.app.db["collections"]
# find if collection already exists using name # find if collection already exists using name
isCollectionNameAvailable = await collections.find_one( isCollectionNameAvailable = await collections.find_one(
{"name": collection_object.name}) {"name": collection_object.name}
)
# TODO # TODO
# await verify_collection_rights("*", current_user, "create") # await verify_collection_rights("*", current_user, "create")
if isCollectionNameAvailable: if isCollectionNameAvailable:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection name already exists") status_code=status.HTTP_409_CONFLICT,
detail="Collection name already exists",
)
# generate collection_id with uuid4 # generate collection_id with uuid4
collection_id = str(f"collection_{uuid4()}") collection_id = str(f"collection_{uuid4()}")
collection = CollectionInDB( collection = CollectionInDB(collection_id=collection_id, **collection_object.dict())
collection_id=collection_id, **collection_object.dict())
collection_in_db = await collections.insert_one(collection.dict()) collection_in_db = await collections.insert_one(collection.dict())
if not collection_in_db: if not collection_in_db:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Unavailable database",
)
return collection.dict() return collection.dict()
async def update_collection(request: Request,collection_object: Collection, collection_id: str, current_user: PublicUser): async def update_collection(
request: Request,
collection_object: Collection,
collection_id: str,
current_user: PublicUser,
):
# verify collection rights # verify collection rights
await verify_collection_rights(request, collection_id, current_user, "update") await verify_collection_rights(request, collection_id, current_user, "update")
@ -93,19 +107,23 @@ async def update_collection(request: Request,collection_object: Collection, coll
if not collection: if not collection:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
updated_collection = CollectionInDB( updated_collection = CollectionInDB(
collection_id=collection_id, **collection_object.dict()) collection_id=collection_id, **collection_object.dict()
)
await collections.update_one({"collection_id": collection_id}, { await collections.update_one(
"$set": updated_collection.dict()}) {"collection_id": collection_id}, {"$set": updated_collection.dict()}
)
return Collection(**updated_collection.dict()) return Collection(**updated_collection.dict())
async def delete_collection(request: Request,collection_id: str, current_user: PublicUser): async def delete_collection(
request: Request, collection_id: str, current_user: PublicUser
):
await verify_collection_rights(request, collection_id, current_user, "delete") await verify_collection_rights(request, collection_id, current_user, "delete")
collections = request.app.db["collections"] collections = request.app.db["collections"]
@ -114,7 +132,8 @@ async def delete_collection(request: Request,collection_id: str, current_user: P
if not collection: if not collection:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
isDeleted = await collections.delete_one({"collection_id": collection_id}) isDeleted = await collections.delete_one({"collection_id": collection_id})
@ -122,20 +141,34 @@ async def delete_collection(request: Request,collection_id: str, current_user: P
return {"detail": "collection deleted"} return {"detail": "collection deleted"}
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Unavailable database",
)
#################################################### ####################################################
# Misc # Misc
#################################################### ####################################################
async def get_collections(request: Request,page: int = 1, limit: int = 10): async def get_collections(
## TODO : auth request: Request,
org_id: str,
current_user: PublicUser,
page: int = 1,
limit: int = 10,
):
collections = request.app.db["collections"] collections = request.app.db["collections"]
# get all collections from database without ObjectId # get all collections from database without ObjectId
all_collections = collections.find({}).sort( all_collections = (
"name", 1).skip(10 * (page - 1)).limit(limit) collections.find({"org_id": org_id})
.sort("name", 1)
.skip(10 * (page - 1))
.limit(limit)
)
await verify_collection_rights(request, "*", current_user, "read")
# create list of collections and include courses in each collection # create list of collections and include courses in each collection
collections_list = [] collections_list = []
@ -148,30 +181,42 @@ async def get_collections(request: Request,page: int = 1, limit: int = 10):
courses = request.app.db["courses"] courses = request.app.db["courses"]
collection.courses = [] collection.courses = []
collection.courses = courses.find( collection.courses = courses.find(
{"course_id": {"$in": collection_courses}}, {'_id': 0}) {"course_id": {"$in": collection_courses}}, {"_id": 0}
)
collection.courses = [course for course in await collection.courses.to_list(length=100)] collection.courses = [
course for course in await collection.courses.to_list(length=100)
]
return collections_list return collections_list
#### Security #################################################### #### Security ####################################################
async def verify_collection_rights(request: Request,collection_id: str, current_user: PublicUser, action: str): async def verify_collection_rights(
request: Request, collection_id: str, current_user: PublicUser, action: str
):
collections = request.app.db["collections"] collections = request.app.db["collections"]
collection = await collections.find_one({"collection_id": collection_id}) collection = await collections.find_one({"collection_id": collection_id})
if not collection and action != "create": if not collection and action != "create":
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, collection_id, collection["org_id"]) hasRoleRights = await verify_user_rights_with_roles(
request, action, current_user.user_id, collection_id, collection["org_id"]
)
if not hasRoleRights: if not hasRoleRights:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this Collection") status_code=status.HTTP_403_FORBIDDEN,
detail="You do not have rights to this Collection",
)
return True return True
#### Security #################################################### #### Security ####################################################