diff --git a/apps/api/src/db/activities.py b/apps/api/src/db/activities.py index e5ae9a53..bae65eb4 100644 --- a/apps/api/src/db/activities.py +++ b/apps/api/src/db/activities.py @@ -53,7 +53,6 @@ class ActivityCreate(ActivityBase): class ActivityUpdate(ActivityBase): - activity_id: int name: Optional[str] activity_type: Optional[ActivityTypeEnum] activity_sub_type: Optional[ActivitySubTypeEnum] diff --git a/apps/api/src/db/chapters.py b/apps/api/src/db/chapters.py index ce5c19d1..d44efe67 100644 --- a/apps/api/src/db/chapters.py +++ b/apps/api/src/db/chapters.py @@ -21,6 +21,7 @@ class Chapter(ChapterBase, table=True): update_date: str = "" + class ChapterCreate(ChapterBase): # referenced order here will be ignored and just used for validation # used order will be the next available. @@ -28,7 +29,6 @@ class ChapterCreate(ChapterBase): class ChapterUpdate(ChapterBase): - chapter_id: int name: Optional[str] description: Optional[str] thumbnail_image: Optional[str] diff --git a/apps/api/src/db/courses.py b/apps/api/src/db/courses.py index 4d29251c..bb726803 100644 --- a/apps/api/src/db/courses.py +++ b/apps/api/src/db/courses.py @@ -22,13 +22,13 @@ class Course(CourseBase, table=True): update_date: str = "" + class CourseCreate(CourseBase): org_id: int = Field(default=None, foreign_key="organization.id") pass class CourseUpdate(CourseBase): - course_id: int name: str description: Optional[str] about: Optional[str] diff --git a/apps/api/src/db/organizations.py b/apps/api/src/db/organizations.py index 9d3d1412..c16d3809 100644 --- a/apps/api/src/db/organizations.py +++ b/apps/api/src/db/organizations.py @@ -17,7 +17,7 @@ class Organization(OrganizationBase, table=True): update_date: str = "" class OrganizationUpdate(OrganizationBase): - org_id: int + pass class OrganizationCreate(OrganizationBase): pass diff --git a/apps/api/src/db/trails.py b/apps/api/src/db/trails.py index c830bbd2..c59697ef 100644 --- a/apps/api/src/db/trails.py +++ b/apps/api/src/db/trails.py @@ -4,7 +4,6 @@ from sqlmodel import Field, SQLModel from src.db.trail_runs import TrailRunRead - class TrailBase(SQLModel): org_id: int = Field(default=None, foreign_key="organization.id") user_id: int = Field(default=None, foreign_key="user.id") @@ -14,7 +13,7 @@ class Trail(TrailBase, table=True): id: Optional[int] = Field(default=None, primary_key=True) trail_uuid: str = "" creation_date: str = "" - update_date: str = "" + update_date: str = "" class TrailCreate(TrailBase): @@ -30,3 +29,6 @@ class TrailRead(BaseModel): creation_date: str update_date: str runs: list[TrailRunRead] + + class Config: + orm_mode = True diff --git a/apps/api/src/db/users.py b/apps/api/src/db/users.py index 0c29799f..79ee9788 100644 --- a/apps/api/src/db/users.py +++ b/apps/api/src/db/users.py @@ -25,7 +25,6 @@ class UserUpdate(UserBase): class UserUpdatePassword(SQLModel): - user_id: int old_password: str new_password: str diff --git a/apps/api/src/routers/courses/activities.py b/apps/api/src/routers/courses/activities.py index 3b609b2d..2ac43897 100644 --- a/apps/api/src/routers/courses/activities.py +++ b/apps/api/src/routers/courses/activities.py @@ -49,30 +49,33 @@ async def api_get_activity( ) -@router.get("/coursechapter/{coursechapter_id}") -async def api_get_activities( +@router.get("/chapter/{chapter_id}") +async def api_get_chapter_activities( request: Request, - coursechapter_id: str, + chapter_id: int, current_user: PublicUser = Depends(get_current_user), db_session=Depends(get_db_session), ) -> List[ActivityRead]: """ - Get CourseChapter activities + Get Activities for a chapter """ - return await get_activities(request, coursechapter_id, current_user, db_session) + return await get_activities(request, chapter_id, current_user, db_session) -@router.put("/") +@router.put("/{activity_id}") async def api_update_activity( request: Request, activity_object: ActivityUpdate, + activity_id: int, current_user: PublicUser = Depends(get_current_user), db_session=Depends(get_db_session), ) -> ActivityRead: """ Update activity by activity_id """ - return await update_activity(request, activity_object, current_user, db_session) + return await update_activity( + request, activity_object, activity_id, current_user, db_session + ) @router.delete("/{activity_id}") diff --git a/apps/api/src/routers/courses/chapters.py b/apps/api/src/routers/courses/chapters.py index b4e4574b..bcd3c34c 100644 --- a/apps/api/src/routers/courses/chapters.py +++ b/apps/api/src/routers/courses/chapters.py @@ -50,7 +50,7 @@ async def api_get_coursechapter( return await get_chapter(request, chapter_id, current_user, db_session) -@router.get("/meta/{course_id}") +@router.get("/course/{course_id}/meta") async def api_get_chapter_meta( request: Request, course_id: int, @@ -65,7 +65,7 @@ async def api_get_chapter_meta( ) -@router.put("/order/{course_id}") +@router.put("/course/{course_id}/order") async def api_update_chapter_meta( request: Request, course_id: int, @@ -81,7 +81,7 @@ async def api_update_chapter_meta( ) -@router.get("/{course_id}/page/{page}/limit/{limit}") +@router.get("/course/{course_id}/page/{page}/limit/{limit}") async def api_get_chapter_by( request: Request, course_id: int, @@ -98,24 +98,26 @@ async def api_get_chapter_by( ) -@router.put("/{coursechapter_id}") +@router.put("/{chapter_id}") async def api_update_coursechapter( request: Request, coursechapter_object: ChapterUpdate, - coursechapter_id: str, + chapter_id: int, current_user: PublicUser = Depends(get_current_user), db_session=Depends(get_db_session), ) -> ChapterRead: """ Update CourseChapters by course_id """ - return await update_chapter(request, coursechapter_object, current_user, db_session) + return await update_chapter( + request, coursechapter_object, chapter_id, current_user, db_session + ) -@router.delete("/{coursechapter_id}") +@router.delete("/{chapter_id}") async def api_delete_coursechapter( request: Request, - coursechapter_id: str, + chapter_id: int, current_user: PublicUser = Depends(get_current_user), db_session=Depends(get_db_session), ): @@ -123,4 +125,4 @@ async def api_delete_coursechapter( Delete CourseChapters by ID """ - return await delete_chapter(request, coursechapter_id, current_user, db_session) + return await delete_chapter(request, chapter_id, current_user, db_session) diff --git a/apps/api/src/routers/courses/collections.py b/apps/api/src/routers/courses/collections.py index 2b0fd572..ba960848 100644 --- a/apps/api/src/routers/courses/collections.py +++ b/apps/api/src/routers/courses/collections.py @@ -42,7 +42,7 @@ async def api_get_collection( return await get_collection(request, collection_id, current_user, db_session) -@router.get("/org_id/{org_id}/page/{page}/limit/{limit}") +@router.get("/org/{org_id}/page/{page}/limit/{limit}") async def api_get_collections_by( request: Request, page: int, diff --git a/apps/api/src/routers/courses/courses.py b/apps/api/src/routers/courses/courses.py index 5914647d..e6669738 100644 --- a/apps/api/src/routers/courses/courses.py +++ b/apps/api/src/routers/courses/courses.py @@ -3,7 +3,12 @@ from fastapi import APIRouter, Depends, UploadFile, Form, Request from sqlmodel import Session from src.core.events.database import get_db_session from src.db.users import PublicUser -from src.db.courses import CourseCreate, CourseRead, CourseUpdate, FullCourseReadWithTrail +from src.db.courses import ( + CourseCreate, + CourseRead, + CourseUpdate, + FullCourseReadWithTrail, +) from src.security.auth import get_current_user from src.services.courses.courses import ( create_course, @@ -49,7 +54,7 @@ async def api_create_course( return await create_course(request, course, current_user, db_session, thumbnail) -@router.put("/thumbnail/{course_id}") +@router.put("/{course_id}/thumbnail") async def api_create_course_thumbnail( request: Request, course_id: str, @@ -80,7 +85,7 @@ async def api_get_course( ) -@router.get("/meta/{course_id}") +@router.get("/{course_id}/meta") async def api_get_course_meta( request: Request, course_id: int, @@ -112,17 +117,20 @@ async def api_get_course_by_orgslug( ) -@router.put("/") +@router.put("/{course_id}") async def api_update_course( request: Request, course_object: CourseUpdate, + course_id: int, db_session: Session = Depends(get_db_session), current_user: PublicUser = Depends(get_current_user), ) -> CourseRead: """ Update Course by course_id """ - return await update_course(request, course_object, current_user, db_session) + return await update_course( + request, course_object, course_id, current_user, db_session + ) @router.delete("/{course_id}") diff --git a/apps/api/src/routers/orgs.py b/apps/api/src/routers/orgs.py index 0f29efb1..1b39c4e8 100644 --- a/apps/api/src/routers/orgs.py +++ b/apps/api/src/routers/orgs.py @@ -92,24 +92,25 @@ async def api_user_orgs( db_session: Session = Depends(get_db_session), ) -> List[Organization]: """ - Get orgs by page and limit by user + Get orgs by page and limit by current user """ return await get_orgs_by_user( request, db_session, str(current_user.id), page, limit ) -@router.put("/") +@router.put("/{org_id}") async def api_update_org( request: Request, org_object: OrganizationUpdate, + org_id: int, current_user: PublicUser = Depends(get_current_user), db_session: Session = Depends(get_db_session), ) -> OrganizationRead: """ Update Org by ID """ - return await update_org(request, org_object, current_user, db_session) + return await update_org(request, org_object,org_id, current_user, db_session) @router.delete("/{org_id}") diff --git a/apps/api/src/routers/trail.py b/apps/api/src/routers/trail.py index 7e935486..0c6455e5 100644 --- a/apps/api/src/routers/trail.py +++ b/apps/api/src/routers/trail.py @@ -41,7 +41,7 @@ async def api_get_user_trail( return await get_user_trails(request, user=user, db_session=db_session) -@router.get("/org_slug/{org_id}/trail") +@router.get("/org/{org_id}/trail") async def api_get_trail_by_org_id( request: Request, org_id: int, @@ -69,7 +69,7 @@ async def api_add_course_to_trail( return await add_course_to_trail(request, user, course_id, db_session) -@router.post("/remove_course/{course_id}") +@router.delete("/remove_course/{course_id}") async def api_remove_course_to_trail( request: Request, course_id: str, @@ -82,11 +82,10 @@ async def api_remove_course_to_trail( return await remove_course_from_trail(request, user, course_id, db_session) -@router.post("/add_activity/course_id/{course_id}/activity_id/{activity_id}") +@router.post("/add_activity/{activity_id}") async def api_add_activity_to_trail( request: Request, activity_id: int, - course_id: int, user=Depends(get_current_user), db_session=Depends(get_db_session), ) -> TrailRead: @@ -94,5 +93,5 @@ async def api_add_activity_to_trail( Add Course to trail """ return await add_activity_to_trail( - request, user, course_id, activity_id, db_session + request, user, activity_id, db_session ) diff --git a/apps/api/src/routers/users.py b/apps/api/src/routers/users.py index efab5dda..cf6e81f9 100644 --- a/apps/api/src/routers/users.py +++ b/apps/api/src/routers/users.py @@ -33,7 +33,7 @@ async def api_get_current_user(current_user: User = Depends(get_current_user)): return current_user.dict() -@router.post("/org_id/{org_id}", response_model=UserRead, tags=["users"]) +@router.post("/{org_id}", response_model=UserRead, tags=["users"]) async def api_create_user_with_orgid( *, request: Request, @@ -62,7 +62,7 @@ async def api_create_user_without_org( return await create_user_without_org(request, db_session, current_user, user_object) -@router.get("/user_id/{user_id}", response_model=UserRead, tags=["users"]) +@router.get("/id/{user_id}", response_model=UserRead, tags=["users"]) async def api_get_user_by_id( *, request: Request, @@ -76,7 +76,7 @@ async def api_get_user_by_id( return await read_user_by_id(request, db_session, current_user, user_id) -@router.get("/user_uuid/{user_uuid}", response_model=UserRead, tags=["users"]) +@router.get("/uuid/{user_uuid}", response_model=UserRead, tags=["users"]) async def api_get_user_by_uuid( *, request: Request, @@ -90,32 +90,34 @@ async def api_get_user_by_uuid( return await read_user_by_uuid(request, db_session, current_user, user_uuid) -@router.put("/", response_model=UserRead, tags=["users"]) +@router.put("/{user_id}", response_model=UserRead, tags=["users"]) async def api_update_user( *, request: Request, db_session: Session = Depends(get_db_session), current_user: PublicUser = Depends(get_current_user), + user_id: int, user_object: UserUpdate, ) -> UserRead: """ Update User """ - return await update_user(request, db_session, current_user, user_object) + return await update_user(request, db_session, user_id, current_user, user_object) -@router.put("/change_password/", response_model=UserRead, tags=["users"]) +@router.put("/change_password/{user_id}", response_model=UserRead, tags=["users"]) async def api_update_user_password( *, request: Request, db_session: Session = Depends(get_db_session), current_user: PublicUser = Depends(get_current_user), + user_id: int, form: UserUpdatePassword, ) -> UserRead: """ Update User Password """ - return await update_user_password(request, db_session, current_user, form) + return await update_user_password(request, db_session, current_user, user_id, form) @router.delete("/user_id/{user_id}", tags=["users"]) @@ -125,7 +127,7 @@ async def api_delete_user( db_session: Session = Depends(get_db_session), current_user: PublicUser = Depends(get_current_user), user_id: int, -) : +): """ Delete User """ diff --git a/apps/api/src/security/rbac/rbac.py b/apps/api/src/security/rbac/rbac.py index 66e3d81f..3d6872b5 100644 --- a/apps/api/src/security/rbac/rbac.py +++ b/apps/api/src/security/rbac/rbac.py @@ -16,7 +16,7 @@ async def authorization_verify_if_element_is_public( element_uuid: str, action: Literal["read"], db_session: Session, -): +): element_nature = await check_element_type(element_uuid) # Verifies if the element is public if element_nature == ("courses" or "collections") and action == "read": diff --git a/apps/api/src/security/rbac/utils.py b/apps/api/src/security/rbac/utils.py index 3ef48a09..51835ee9 100644 --- a/apps/api/src/security/rbac/utils.py +++ b/apps/api/src/security/rbac/utils.py @@ -5,6 +5,7 @@ async def check_element_type(element_id): """ Check if the element is a course, a user, a house or a collection, by checking its prefix """ + print("element_id", element_id) if element_id.startswith("course_"): return "courses" elif element_id.startswith("user_"): @@ -13,12 +14,14 @@ async def check_element_type(element_id): return "houses" elif element_id.startswith("org_"): return "organizations" - elif element_id.startswith("coursechapter_"): + elif element_id.startswith("chapter_"): return "coursechapters" elif element_id.startswith("collection_"): return "collections" elif element_id.startswith("activity_"): return "activities" + elif element_id.startswith("role_"): + return "roles" else: raise HTTPException( status_code=status.HTTP_409_CONFLICT, diff --git a/apps/api/src/services/courses/activities/activities.py b/apps/api/src/services/courses/activities/activities.py index 5eeb7656..dc4dfda5 100644 --- a/apps/api/src/services/courses/activities/activities.py +++ b/apps/api/src/services/courses/activities/activities.py @@ -104,10 +104,11 @@ async def get_activity( async def update_activity( request: Request, activity_object: ActivityUpdate, + activity_id: int, current_user: PublicUser | AnonymousUser, db_session: Session, ): - statement = select(Activity).where(Activity.id == activity_object.activity_id) + statement = select(Activity).where(Activity.id == activity_id) activity = db_session.exec(statement).first() if not activity: @@ -121,8 +122,6 @@ async def update_activity( request, activity.activity_uuid, current_user, "update", db_session ) - del activity_object.activity_id - # Update only the fields that were passed in for var, value in vars(activity_object).items(): if value is not None: @@ -183,7 +182,7 @@ async def delete_activity( async def get_activities( request: Request, - coursechapter_id: str, + coursechapter_id: int, current_user: PublicUser | AnonymousUser, db_session: Session, ) -> list[ActivityRead]: diff --git a/apps/api/src/services/courses/chapters.py b/apps/api/src/services/courses/chapters.py index 1fa3aae9..51830815 100644 --- a/apps/api/src/services/courses/chapters.py +++ b/apps/api/src/services/courses/chapters.py @@ -130,10 +130,11 @@ async def get_chapter( async def update_chapter( request: Request, chapter_object: ChapterUpdate, + chapter_id: int, current_user: PublicUser | AnonymousUser, db_session: Session, ) -> ChapterRead: - statement = select(Chapter).where(Chapter.id == chapter_object.chapter_id) + statement = select(Chapter).where(Chapter.id == chapter_id) chapter = db_session.exec(statement).first() if not chapter: @@ -161,7 +162,7 @@ async def update_chapter( async def delete_chapter( request: Request, - chapter_id: str, + chapter_id: int, current_user: PublicUser | AnonymousUser, db_session: Session, ): @@ -190,7 +191,6 @@ async def delete_chapter( return {"detail": "chapter deleted"} - async def get_course_chapters( request: Request, course_id: int, diff --git a/apps/api/src/services/courses/courses.py b/apps/api/src/services/courses/courses.py index 6f7fd613..e36eb93e 100644 --- a/apps/api/src/services/courses/courses.py +++ b/apps/api/src/services/courses/courses.py @@ -65,6 +65,8 @@ async def get_course_meta( detail="Course not found", ) + print('cd',course.course_uuid) + # RBAC check await rbac_check(request, course.course_uuid, current_user, "read", db_session) @@ -189,10 +191,11 @@ async def update_course_thumbnail( async def update_course( request: Request, course_object: CourseUpdate, + course_id: int, current_user: PublicUser | AnonymousUser, db_session: Session, ): - statement = select(Course).where(Course.id == course_object.course_id) + statement = select(Course).where(Course.id == course_id) course = db_session.exec(statement).first() if not course: @@ -204,8 +207,6 @@ async def update_course( # RBAC check await rbac_check(request, course.course_uuid, current_user, "update", db_session) - del course_object.course_id - # Update only the fields that were passed in for var, value in vars(course_object).items(): if value is not None: diff --git a/apps/api/src/services/orgs/orgs.py b/apps/api/src/services/orgs/orgs.py index b1701802..0f67d08c 100644 --- a/apps/api/src/services/orgs/orgs.py +++ b/apps/api/src/services/orgs/orgs.py @@ -118,10 +118,11 @@ async def create_org( async def update_org( request: Request, org_object: OrganizationUpdate, + org_id: int, current_user: PublicUser | AnonymousUser, db_session: Session, ): - statement = select(Organization).where(Organization.id == org_object.org_id) + statement = select(Organization).where(Organization.id == org_id) result = db_session.exec(statement) org = result.first() @@ -149,9 +150,6 @@ async def update_org( detail="Organization slug already exists", ) - # Remove the org_id from the org_object - del org_object.org_id - # Update only the fields that were passed in for var, value in vars(org_object).items(): if value is not None: @@ -203,7 +201,6 @@ async def update_org_logo( db_session.commit() db_session.refresh(org) - return {"detail": "Logo updated"} diff --git a/apps/api/src/services/trail/trail.py b/apps/api/src/services/trail/trail.py index c79d4d0d..019520a2 100644 --- a/apps/api/src/services/trail/trail.py +++ b/apps/api/src/services/trail/trail.py @@ -2,6 +2,7 @@ from datetime import datetime from uuid import uuid4 from fastapi import HTTPException, Request, status from sqlmodel import Session, select +from src.db.activities import Activity from src.db.courses import Course from src.db.trail_runs import TrailRun, TrailRunRead from src.db.trail_steps import TrailStep @@ -120,13 +121,20 @@ async def get_user_trail_with_orgid( async def add_activity_to_trail( request: Request, user: PublicUser, - course_id: int, activity_id: int, db_session: Session, ) -> TrailRead: - + # Look for the activity + statement = select(Activity).where(Activity.id == activity_id) + activity = db_session.exec(statement).first() + + if not activity: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Activity not found" + ) + # check if run already exists - statement = select(TrailRun).where(TrailRun.course_id == course_id) + statement = select(TrailRun).where(TrailRun.course_id == activity.course_id) trailrun = db_session.exec(statement).first() if trailrun: @@ -134,7 +142,7 @@ async def add_activity_to_trail( status_code=status.HTTP_400_BAD_REQUEST, detail="TrailRun already exists" ) - statement = select(Course).where(Course.id == course_id) + statement = select(Course).where(Course.id == activity.course_id) course = db_session.exec(statement).first() if not course: @@ -160,7 +168,7 @@ async def add_activity_to_trail( if not trailrun: trailrun = TrailRun( trail_id=trail.id if trail.id is not None else 0, - course_id=course.id if course.id is not None else 0 , + course_id=course.id if course.id is not None else 0, org_id=course.org_id, user_id=user.id, creation_date=str(datetime.now()), @@ -177,7 +185,7 @@ async def add_activity_to_trail( if not trailstep: trailstep = TrailStep( - trailrun_id=trailrun.id if trailrun.id is not None else 0 , + trailrun_id=trailrun.id if trailrun.id is not None else 0, activity_id=activity_id, course_id=course.id if course.id is not None else 0, org_id=course.org_id, @@ -225,7 +233,6 @@ async def add_course_to_trail( course_id: str, db_session: Session, ) -> TrailRead: - # check if run already exists statement = select(TrailRun).where(TrailRun.course_id == course_id) trailrun = db_session.exec(statement).first() @@ -234,7 +241,7 @@ async def add_course_to_trail( raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="TrailRun already exists" ) - + statement = select(Course).where(Course.id == course_id) course = db_session.exec(statement).first() diff --git a/apps/api/src/services/users/users.py b/apps/api/src/services/users/users.py index bc137af7..e36b7040 100644 --- a/apps/api/src/services/users/users.py +++ b/apps/api/src/services/users/users.py @@ -158,11 +158,12 @@ async def create_user_without_org( async def update_user( request: Request, db_session: Session, + user_id: int, current_user: PublicUser | AnonymousUser, user_object: UserUpdate, ): # Get user - statement = select(User).where(User.username == user_object.username) + statement = select(User).where(User.id == user_id) user = db_session.exec(statement).first() if not user: @@ -170,7 +171,7 @@ async def update_user( status_code=400, detail="User does not exist", ) - + # RBAC check await rbac_check(request, current_user, "update", user.user_uuid, db_session) @@ -195,10 +196,11 @@ async def update_user_password( request: Request, db_session: Session, current_user: PublicUser | AnonymousUser, + user_id: int, form: UserUpdatePassword, ): # Get user - statement = select(User).where(User.username == form.user_id) + statement = select(User).where(User.id == user_id) user = db_session.exec(statement).first() if not user: @@ -206,7 +208,7 @@ async def update_user_password( status_code=400, detail="User does not exist", ) - + # RBAC check await rbac_check(request, current_user, "update", user.user_uuid, db_session) @@ -339,7 +341,6 @@ async def rbac_check( await authorization_verify_based_on_roles_and_authorship( request, current_user.id, "create", "user_x", db_session ) - else: await authorization_verify_if_user_is_anon(current_user.id)