From 4f424713fa7dcbbf831b7bdd54bb83e806d654b4 Mon Sep 17 00:00:00 2001 From: Artsiom Aliakseyenka Date: Thu, 14 Sep 2023 15:09:03 +0200 Subject: [PATCH] feat: add cross-check message notification (#2293) --- .../course/student/cross-check-review.tsx | 15 +++++-- server/src/models/notification.ts | 3 +- .../routes/course/crossCheck/createMessage.ts | 43 ++++++++++++++----- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/client/src/pages/course/student/cross-check-review.tsx b/client/src/pages/course/student/cross-check-review.tsx index f91af16ef0..3a3bf97b21 100644 --- a/client/src/pages/course/student/cross-check-review.tsx +++ b/client/src/pages/course/student/cross-check-review.tsx @@ -38,6 +38,7 @@ function Page() { const session = useContext(SessionContext); const router = useRouter(); const queryTaskId = router.query.taskId ? +router.query.taskId : null; + const queryGithubId = router.query.githubId ?? null; const [form] = Form.useForm(); const [loading, withLoading] = useLoading(false); @@ -117,10 +118,17 @@ function Page() { }); useEffect(() => { - if (queryTaskId && courseTasks.length) { - handleTaskChange(queryTaskId); + setup(); + + async function setup() { + if (queryTaskId && courseTasks.length) { + await handleTaskChange(queryTaskId); + if (queryGithubId) { + await handleStudentChange(queryGithubId as string); + } + } } - }, [queryTaskId, courseTasks]); + }, [queryTaskId, courseTasks.length, queryGithubId]); useEffect(() => { if (historicalCommentSelected !== '') { @@ -242,6 +250,7 @@ function Page() { onChange={handleStudentChange} disabled={!courseTaskId} defaultValues={assignments.map(({ student }) => student)} + value={githubId} /> diff --git a/server/src/models/notification.ts b/server/src/models/notification.ts index 5d5ff3e671..e164f876c3 100644 --- a/server/src/models/notification.ts +++ b/server/src/models/notification.ts @@ -25,7 +25,8 @@ export type NotificationId = | 'taskDeadline' | 'interviewerAssigned' | 'emailConfirmation' - | 'mentor:assigned'; + | 'mentor:assigned' + | 'messages'; @Entity() export class Notification { diff --git a/server/src/routes/course/crossCheck/createMessage.ts b/server/src/routes/course/crossCheck/createMessage.ts index 50cd4faea5..1f775ad106 100644 --- a/server/src/routes/course/crossCheck/createMessage.ts +++ b/server/src/routes/course/crossCheck/createMessage.ts @@ -1,28 +1,35 @@ import Router from '@koa/router'; -import { BAD_REQUEST, OK } from 'http-status-codes'; +import { StatusCodes } from 'http-status-codes'; import { ILogger } from '../../../logger'; import { IUserSession } from '../../../models'; import { CrossCheckMessageAuthorRole } from '../../../models/taskSolutionResult'; -import { courseService, CrossCheckService } from '../../../services'; +import { courseService, CrossCheckService, notificationService } from '../../../services'; import { getTaskSolutionResultById } from '../../../services/taskResults.service'; import { setErrorResponse, setResponse } from '../../utils'; +import { getCourseTask } from '../../../services/tasks.service'; export const createMessage = (_: ILogger) => async (ctx: Router.RouterContext) => { const { courseId, taskSolutionResultId, courseTaskId } = ctx.params; const { user } = ctx.state as { user: IUserSession }; const crossCheckService = new CrossCheckService(courseTaskId); - const student = await courseService.queryStudentByGithubId(courseId, user.githubId); + const [student, taskSolutionResult, courseTask] = await Promise.all([ + courseService.queryStudentByGithubId(courseId, user.githubId), + getTaskSolutionResultById(taskSolutionResultId), + getCourseTask(courseTaskId, true), + ]); if (!student) { - setErrorResponse(ctx, BAD_REQUEST, 'not valid student or course'); + setErrorResponse(ctx, StatusCodes.BAD_REQUEST, 'not valid student or course'); + return; + } + if (!courseTask) { + setErrorResponse(ctx, StatusCodes.BAD_REQUEST, 'not valid task'); return; } - - const taskSolutionResult = await getTaskSolutionResultById(taskSolutionResultId); if (!taskSolutionResult) { - setErrorResponse(ctx, BAD_REQUEST, 'task solution result is not exist'); + setErrorResponse(ctx, StatusCodes.BAD_REQUEST, 'task solution result is not exist'); return; } @@ -34,20 +41,20 @@ export const createMessage = (_: ILogger) => async (ctx: Router.RouterContext) = switch (inputData.role) { case CrossCheckMessageAuthorRole.Reviewer: if (student.id !== taskSolutionResult.checkerId) { - setErrorResponse(ctx, BAD_REQUEST, 'user is not checker'); + setErrorResponse(ctx, StatusCodes.BAD_REQUEST, 'user is not checker'); return; } break; case CrossCheckMessageAuthorRole.Student: if (student.id !== taskSolutionResult.studentId) { - setErrorResponse(ctx, BAD_REQUEST, 'user is not student'); + setErrorResponse(ctx, StatusCodes.BAD_REQUEST, 'user is not student'); return; } break; default: - setErrorResponse(ctx, BAD_REQUEST, 'incorrect message role'); + setErrorResponse(ctx, StatusCodes.BAD_REQUEST, 'incorrect message role'); return; } @@ -60,5 +67,19 @@ export const createMessage = (_: ILogger) => async (ctx: Router.RouterContext) = user: user, }); - setResponse(ctx, OK); + await notificationService + .sendNotification({ + userId: inputData.role === CrossCheckMessageAuthorRole.Reviewer ? student.userId : taskSolutionResult.checkerId, + notificationId: 'messages', + data: { + isReviewerMessage: inputData.role === CrossCheckMessageAuthorRole.Reviewer, + courseAlias: courseTask.course.alias, + courseTaskId, + taskName: courseTask.task.name, + studentGithubId: student.githubId, + }, + }) + .catch(() => null); + + setResponse(ctx, StatusCodes.OK); };