diff --git a/backend/src/controllers/course.controller.ts b/backend/src/controllers/course.controller.ts index 5582fd2e4..9c0898135 100644 --- a/backend/src/controllers/course.controller.ts +++ b/backend/src/controllers/course.controller.ts @@ -3,6 +3,13 @@ import { formatError, getLogger } from "../utils/logger"; import { IController } from "../interfaces/IController"; import { CourseService } from "../services/course.service"; import verifyToken from "../api/middlewares/auth"; +import validationMiddleware from "../api/middlewares/validation"; +import { + BookmarkCourse, + BookmarkCourseSchema, +} from "../api/schemas/course.schema"; +import { HTTPError } from "../utils/errors"; +import { badRequest } from "../utils/constants"; export class CourseController implements IController { private readonly logger = getLogger(); @@ -188,6 +195,32 @@ export class CourseController implements IController { } }, ) + .post( + "/courses/bookmark", + [verifyToken, validationMiddleware(BookmarkCourseSchema, "body")], + async ( + req: Request, unknown, BookmarkCourse>, + res: Response, + next: NextFunction, + ) => { + this.logger.debug(`Received request in POST /courses/bookmark`); + try { + const courseDetails = req.body; + if (!courseDetails) throw new HTTPError(badRequest); + const result = + await this.courseService.bookmarkCourse(courseDetails); + this.logger.info(`Responding to client in POST /courses/bookmark`); + return res.status(200).json(result); + } catch (err: any) { + this.logger.warn( + `An error occurred when trying to POST /courses/bookmark ${formatError( + err, + )}`, + ); + return next(err); + } + }, + ) .delete( "/cached/flush", async ( diff --git a/backend/src/repositories/user.repository.ts b/backend/src/repositories/user.repository.ts index f86b012e4..6c39cc251 100644 --- a/backend/src/repositories/user.repository.ts +++ b/backend/src/repositories/user.repository.ts @@ -27,14 +27,29 @@ export class UserRepository { return rawUser; } - async updateUser(user: { zid: string; bookmarkedReviews: string[] }) { + async updateUser(user: { + zid: string; + bookmarkedReviews?: string[]; + bookmarkedCourses?: string[]; + }) { + const updateData: { + bookmarkedReviews?: string[]; + bookmarkedCourses?: string[]; + } = {}; + + if (user.bookmarkedReviews !== undefined) { + updateData.bookmarkedReviews = user.bookmarkedReviews; + } + + if (user.bookmarkedCourses !== undefined) { + updateData.bookmarkedCourses = user.bookmarkedCourses; + } + const updatedUser = await this.prisma.users.update({ where: { zid: user.zid, }, - data: { - bookmarkedReviews: user.bookmarkedReviews, - }, + data: updateData, }); return updatedUser; } diff --git a/backend/src/services/course.service.ts b/backend/src/services/course.service.ts index 96c8f8f9a..0bf5a423c 100644 --- a/backend/src/services/course.service.ts +++ b/backend/src/services/course.service.ts @@ -9,6 +9,7 @@ import { CourseRepository } from "../repositories/course.repository"; import { UserRepository } from "../repositories/user.repository"; import RedisClient from "../modules/redis"; import { + BookmarkCourse, Course, CourseBody, CoursesSuccessResponse, @@ -218,4 +219,57 @@ export class CourseService { await this.redis.flushAll(); } + + async bookmarkCourse( + courseDetails: BookmarkCourse, + ): Promise { + const course = await this.courseRepository.getCourse( + courseDetails.courseCode, + ); + + if (!course) { + this.logger.error( + `There is no course with courseCode ${courseDetails.courseCode}.`, + ); + throw new HTTPError(badRequest); + } + + const user = await this.userRepository.getUser(courseDetails.zid); + + if (!user) { + this.logger.error(`There is no user with zid ${courseDetails.zid}.`); + throw new HTTPError(badRequest); + } + + if (!user.bookmarkedCourses) { + user.bookmarkedCourses = []; + } + + if (courseDetails.bookmark) { + // Only add if not already bookmarked + if (!user.bookmarkedCourses.includes(course.courseCode)) { + user.bookmarkedCourses.push(course.courseCode); + } + } else { + user.bookmarkedCourses = user.bookmarkedCourses.filter( + (courseCode) => courseCode !== courseDetails.courseCode, + ); + } + + await this.userRepository.updateUser({ + zid: user.zid, + bookmarkedCourses: user.bookmarkedCourses, + }); + + this.logger.info( + `Successfully ${ + courseDetails.bookmark ? "bookmarked" : "removed bookmarked" + } course with courseCode ${courseDetails.courseCode} for user with zID ${ + courseDetails.zid + }.`, + ); + return { + course: course, + }; + } }