From 0b6ba6453740eaeb3cca3a9e0afb924f86da038f Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 17:42:35 +0800 Subject: [PATCH 1/9] create script for setting up the project and create a README for detailed instruction on how to run the project locally --- .gitignore | 4 +++- LICENSE | 21 +++++++++++++++++++++ frontend/package.json | 1 + package.json | 15 +++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 LICENSE create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 109a1c99..6b046f55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ # ignoring any env files and node_modules in each microservice, will be helpful for local development **/.env* -**/node_modules/** \ No newline at end of file +**/node_modules/** +node_modules +.env* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..4bc693f5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 CS3213 Project Group 9 - FMDevs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/package.json b/frontend/package.json index 09d419b9..f6ec6faf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "dev-all": "concurrently --kill-others \"yarn dev:frontend\" \"yarn dev:grading\" \"yarn dev:assignment\" \"yarn dev:user\"", "build": "next build", "start": "next start", + "start-all": "concurrently --kill-others \"yarn start\" \"yarn dev:grading\" \"yarn dev:assignment\" \"yarn dev:user\"", "lint": "next lint && prettier --check --ignore-path .gitignore .", "lint:autofix": "next lint --fix && prettier --write --ignore-path .gitignore .", "test": "jest", diff --git a/package.json b/package.json new file mode 100644 index 00000000..6b752ead --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "frontend-management-system", + "version": "1.0.0", + "author": "fmdevs", + "license": "MIT", + "scripts": { + "setup:assignment": "cd backend/assignment-service && yarn install && yarn prisma generate", + "setup:user": "cd backend/user-service && yarn install && yarn build", + "setup:grading": "cd backend/grading-service && yarn install && yarn prisma generate", + "setup:frontend": "cd frontend && yarn install && yarn build", + "setup": "yarn setup:assignment && yarn setup:user && yarn setup:grading && yarn setup:frontend", + "start": "cd frontend && yarn start-all", + "dev": "cd frontend && yarn dev-all" + } +} From 57de00837cb08d5c27be7158a489a33bfff205ea Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 19:24:48 +0800 Subject: [PATCH 2/9] add more lint rules for grading service and fix linting errors --- backend/grading-service/.eslintrc.json | 61 ++++++++++--------- backend/grading-service/package.json | 2 +- .../src/controllers/base-controller.ts | 2 +- .../src/controllers/grading-controller.ts | 14 +++-- .../utils/{errorUtils.ts => error-utils.ts} | 2 +- .../src/libs/utils/swagger-utils.ts | 2 +- backend/grading-service/src/models/db.ts | 2 + .../src/models/its/error-feedback.ts | 4 -- .../src/services/get-handler.ts | 3 +- .../src/services/its/api-wrapper.ts | 15 +++-- .../src/tests/payloads/code-error.ts | 4 +- .../src/tests/unit/api-wrapper.test.ts | 14 +++-- .../src/tests/unit/cors.test.ts | 1 - .../src/tests/unit/grading-controller.test.ts | 15 +++++ .../tests/utils/create-test-server-utils.ts | 2 +- .../src/types/grading-service.d.ts | 14 +++++ 16 files changed, 100 insertions(+), 57 deletions(-) rename backend/grading-service/src/libs/utils/{errorUtils.ts => error-utils.ts} (87%) delete mode 100644 backend/grading-service/src/models/its/error-feedback.ts create mode 100644 backend/grading-service/src/types/grading-service.d.ts diff --git a/backend/grading-service/.eslintrc.json b/backend/grading-service/.eslintrc.json index c3784b89..d58fe8b7 100644 --- a/backend/grading-service/.eslintrc.json +++ b/backend/grading-service/.eslintrc.json @@ -1,33 +1,34 @@ { - "env": { - "browser": true, - "es2021": true - }, - "extends": [ - "prettier", - "eslint:recommended", - "plugin:@typescript-eslint/recommended" + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "prettier", + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/no-unused-vars": [ + "error", + { + "args": "all", + "argsIgnorePattern": "^_", + "caughtErrors": "all", + "caughtErrorsIgnorePattern": "^_", + "destructuredArrayIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "ignoreRestSiblings": true + } ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "@typescript-eslint/no-unused-vars": [ - "error", - { - "args": "all", - "argsIgnorePattern": "^_", - "caughtErrors": "all", - "caughtErrorsIgnorePattern": "^_", - "destructuredArrayIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "ignoreRestSiblings": true - } - ] - } + "@typescript-eslint/explicit-function-return-type": 2, + "@typescript-eslint/explicit-module-boundary-types": 2, + "@typescript-eslint/no-explicit-any": 2 + } } diff --git a/backend/grading-service/package.json b/backend/grading-service/package.json index ba241542..ef73c53a 100644 --- a/backend/grading-service/package.json +++ b/backend/grading-service/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "prisma generate && nodemon src/app.ts", "pure-dev": "nodemon src/app.ts", - "build": "prisma generate --schema='src/models/prisma/schema.prisma'&& tsc -p .", + "build": "prisma generate && tsc -p .", "start": "node dist/app.js", "test": "jest --coverage --verbose", "db": "prisma studio", diff --git a/backend/grading-service/src/controllers/base-controller.ts b/backend/grading-service/src/controllers/base-controller.ts index 27f125c0..868147e8 100644 --- a/backend/grading-service/src/controllers/base-controller.ts +++ b/backend/grading-service/src/controllers/base-controller.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import HttpStatusCode from "../libs/enums/HttpStatusCode"; import db from "../models/db"; -const getHealth = async (_: Request, response: Response) => { +const getHealth = async (_: Request, response: Response): Promise => { try { await db.$queryRaw`SELECT 1`; diff --git a/backend/grading-service/src/controllers/grading-controller.ts b/backend/grading-service/src/controllers/grading-controller.ts index 7bc569bb..16131cca 100644 --- a/backend/grading-service/src/controllers/grading-controller.ts +++ b/backend/grading-service/src/controllers/grading-controller.ts @@ -9,7 +9,7 @@ import CodeFunctionNameError from "../libs/errors/CodeFunctionNameError"; import { GetHandler } from "../services/get-handler"; import { PostParserValidator } from "../libs/validators/post-parser-validator"; import { ZodError } from "zod"; -import { formatZodErrorMessage } from "../libs/utils/errorUtils"; +import { formatZodErrorMessage } from "../libs/utils/error-utils"; import { PostFeedbackValidator } from "../libs/validators/post-feedback-validator"; import NotExistingStudentError from "../libs/errors/NotExistingStudentError"; import { GetSubmissionQueryValidator } from "../libs/validators/get-submission-query-validator"; @@ -17,7 +17,7 @@ import { GetSubmissionQueryValidator } from "../libs/validators/get-submission-q const getSubmissionByQuestionIdAndStudentId = async ( request: Request, response: Response -) => { +): Promise => { try { const { questionId } = request.params; const { studentId } = GetSubmissionQueryValidator.parse(request.query); @@ -56,7 +56,10 @@ const getSubmissionByQuestionIdAndStudentId = async ( } }; -const postParser = async (request: Request, response: Response) => { +const postParser = async ( + request: Request, + response: Response +): Promise => { try { const { language, source_code } = PostParserValidator.parse(request.body); @@ -89,7 +92,10 @@ const postParser = async (request: Request, response: Response) => { } }; -const postFeedback = async (request: Request, response: Response) => { +const postFeedback = async ( + request: Request, + response: Response +): Promise => { try { // take in language, source_code, question_id, student_id const { language, source_code, question_id, student_id } = diff --git a/backend/grading-service/src/libs/utils/errorUtils.ts b/backend/grading-service/src/libs/utils/error-utils.ts similarity index 87% rename from backend/grading-service/src/libs/utils/errorUtils.ts rename to backend/grading-service/src/libs/utils/error-utils.ts index 4bcd08e5..f3cb61db 100644 --- a/backend/grading-service/src/libs/utils/errorUtils.ts +++ b/backend/grading-service/src/libs/utils/error-utils.ts @@ -1,6 +1,6 @@ import { ZodError } from "zod"; -export function formatZodErrorMessage(error: ZodError) { +export function formatZodErrorMessage(error: ZodError): string { let errorMessage = JSON.parse(error.message)[0]; if (errorMessage.message === "Required") { const errorField = JSON.parse(error.message)[0].path[0]; diff --git a/backend/grading-service/src/libs/utils/swagger-utils.ts b/backend/grading-service/src/libs/utils/swagger-utils.ts index 2e9d27e7..c75ddb90 100644 --- a/backend/grading-service/src/libs/utils/swagger-utils.ts +++ b/backend/grading-service/src/libs/utils/swagger-utils.ts @@ -4,4 +4,4 @@ import yaml from "yaml"; const swaggerYaml = fs.readFileSync("src/libs/docs/swagger.yaml", "utf8"); const swaggerDocument = yaml.parse(swaggerYaml); -export default swaggerDocument; \ No newline at end of file +export default swaggerDocument; diff --git a/backend/grading-service/src/models/db.ts b/backend/grading-service/src/models/db.ts index 222b267b..8caaefaa 100644 --- a/backend/grading-service/src/models/db.ts +++ b/backend/grading-service/src/models/db.ts @@ -4,6 +4,8 @@ import dotenv from "dotenv"; dotenv.config(); declare global { + // var is used to declare global variables + // eslint-disable-next-line no-var var cachedPrisma: PrismaClient; } diff --git a/backend/grading-service/src/models/its/error-feedback.ts b/backend/grading-service/src/models/its/error-feedback.ts deleted file mode 100644 index 2b900078..00000000 --- a/backend/grading-service/src/models/its/error-feedback.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type ErrorFeedback = { - line: number; - hints: string[]; -}; diff --git a/backend/grading-service/src/services/get-handler.ts b/backend/grading-service/src/services/get-handler.ts index eed8359b..817ed9e3 100644 --- a/backend/grading-service/src/services/get-handler.ts +++ b/backend/grading-service/src/services/get-handler.ts @@ -1,9 +1,10 @@ import db from "../models/db"; +import { Submission } from "../types/grading-service"; const getSubmissionByQuestionIdAndStudentId = async ( questionId: string, studentId: number -) => { +): Promise => { const submission = await db.submission.findFirst({ where: { questionId: questionId, diff --git a/backend/grading-service/src/services/its/api-wrapper.ts b/backend/grading-service/src/services/its/api-wrapper.ts index d74aff81..c091bdc4 100644 --- a/backend/grading-service/src/services/its/api-wrapper.ts +++ b/backend/grading-service/src/services/its/api-wrapper.ts @@ -5,10 +5,10 @@ import ITSPostParserError from "../../libs/errors/ITSPostParserError"; import NotExistingReferencedSolutionError from "../../libs/errors/NotExistingReferencedSolutionError"; import NotExistingTestCaseError from "../../libs/errors/NotExistingTestCaseError"; import ITSPostFeedbackError from "../../libs/errors/ITSPostFeedbackError"; -import { ErrorFeedback } from "../../models/its/error-feedback"; import CodeFunctionNameError from "../../libs/errors/CodeFunctionNameError"; import HttpStatusCode from "../../libs/enums/HttpStatusCode"; import NotExistingStudentError from "../../libs/errors/NotExistingStudentError"; +import { Feedback } from "../../types/grading-service"; dotenv.config(); @@ -21,7 +21,10 @@ export const api = axios.create({ }, }); -async function generateParserString(language: string, source_code: string) { +async function generateParserString( + language: string, + source_code: string +): Promise { try { const response = await api.post("/cs3213/parser", { language: language, @@ -54,7 +57,7 @@ async function generateErrorFeedback( studentCode: string, questionId: string, studentId: number -) { +): Promise { // ensure that student exists const student = await db.user.findUnique({ where: { @@ -138,7 +141,7 @@ async function generateErrorFeedback( args: args, }); - const feedbacks: ErrorFeedback[] = response.data.map( + const feedbacks: Feedback[] = response.data.map( (feedback: { lineNumber: number; hintStrings: string[] }) => { return { line: feedback.lineNumber, @@ -181,8 +184,8 @@ async function saveSubmissionWithFeedbacks( language: string, studentCode: string, studentSolutionParserString: string, - feedbacks: ErrorFeedback[] -) { + feedbacks: Feedback[] +): Promise { // if the submission already exists, delete it const existingSubmission = await db.submission.findFirst({ where: { diff --git a/backend/grading-service/src/tests/payloads/code-error.ts b/backend/grading-service/src/tests/payloads/code-error.ts index afebfd35..086c1807 100644 --- a/backend/grading-service/src/tests/payloads/code-error.ts +++ b/backend/grading-service/src/tests/payloads/code-error.ts @@ -1,4 +1,4 @@ -import { ErrorFeedback } from "../../models/its/error-feedback"; +import { Feedback } from "../../types/grading-service"; const CodeError = { hintStrings: ["Incorrect else-block for if ( ((x % 2) == 1) )"], @@ -7,7 +7,7 @@ const CodeError = { line: 2, hints: ["Incorrect else-block for if ( ((x % 2) == 1) )"], }, - ] as ErrorFeedback[], + ] as Feedback[], }; export default CodeError; diff --git a/backend/grading-service/src/tests/unit/api-wrapper.test.ts b/backend/grading-service/src/tests/unit/api-wrapper.test.ts index b8b80884..3787b983 100644 --- a/backend/grading-service/src/tests/unit/api-wrapper.test.ts +++ b/backend/grading-service/src/tests/unit/api-wrapper.test.ts @@ -12,8 +12,6 @@ import HttpStatusCode from "../../libs/enums/HttpStatusCode"; import CodeError from "../payloads/code-error"; import NotExistingStudentError from "../../libs/errors/NotExistingStudentError"; -const NODE_ENV = "test"; - const axiosMock = api as jest.Mocked; describe("Unit tests for generateParser service", () => { @@ -129,7 +127,7 @@ describe("Unit tests for generateFeedback service", () => { output: "False", }); - axiosMock.post = jest.fn().mockImplementation((url, data) => { + axiosMock.post = jest.fn().mockImplementation((url, _) => { if (url.includes("feedback_error")) { return Promise.resolve({ data: [ @@ -424,6 +422,9 @@ describe("Unit tests for generateFeedback service", () => { // Assert await expect(apiCall).rejects.toThrow(NotExistingTestCaseError); + + // reset the mocks + spy.mockRestore(); }); }); @@ -560,7 +561,12 @@ function setupMockedGenerateErrorFeedbackParams( studentExists: true, withoutTargetFunction: false, } -) { +): { + language: string; + studentCode: string; + questionId: string; + studentId: number; +} { let language: string; let studentCode: string; diff --git a/backend/grading-service/src/tests/unit/cors.test.ts b/backend/grading-service/src/tests/unit/cors.test.ts index 4e8f0818..2d3bbe11 100644 --- a/backend/grading-service/src/tests/unit/cors.test.ts +++ b/backend/grading-service/src/tests/unit/cors.test.ts @@ -1,7 +1,6 @@ import supertest from "supertest"; import createUnitTestServer from "../utils/create-test-server-utils"; import HttpStatusCode from "../../libs/enums/HttpStatusCode"; -import { BaseController } from "../../controllers/base-controller"; import { Router } from "express"; const app = createUnitTestServer(); diff --git a/backend/grading-service/src/tests/unit/grading-controller.test.ts b/backend/grading-service/src/tests/unit/grading-controller.test.ts index 34a5bcba..df56e82c 100644 --- a/backend/grading-service/src/tests/unit/grading-controller.test.ts +++ b/backend/grading-service/src/tests/unit/grading-controller.test.ts @@ -40,6 +40,9 @@ describe("Unit Tests for Grading Controller", () => { expect(response.body).toEqual({ parser: expectedParserString, }); + + // reset the mocks + spy.mockRestore(); }); }); @@ -115,6 +118,9 @@ describe("Unit Tests for Grading Controller", () => { message: "Invalid source_code. Failed to generate parser string from ITS API", }); + + // reset the mocks + spy.mockRestore(); }); }); }); @@ -259,6 +265,9 @@ describe("Unit Tests for Grading Controller", () => { message: "Invalid source_code. Failed to generate parser string from ITS API", }); + + // reset the mocks + spy.mockRestore(); }); }); @@ -449,6 +458,9 @@ describe("Unit Tests for Grading Controller", () => { error: "INTERNAL SERVER ERROR", message: "An unexpected error has occurred. Please try again later", }); + + // reset the mocks + spy.mockRestore(); }); }); }); @@ -567,6 +579,9 @@ describe("Unit Tests for Grading Controller", () => { error: "INTERNAL SERVER ERROR", message: "An unexpected error has occurred. Please try again later", }); + + // reset the mocks + spy.mockRestore(); }); }); }); diff --git a/backend/grading-service/src/tests/utils/create-test-server-utils.ts b/backend/grading-service/src/tests/utils/create-test-server-utils.ts index c2cdfde3..cb88f6af 100644 --- a/backend/grading-service/src/tests/utils/create-test-server-utils.ts +++ b/backend/grading-service/src/tests/utils/create-test-server-utils.ts @@ -3,7 +3,7 @@ import cors from "../../middlewares/cors"; import bodyParser from "body-parser"; import router from "../../routes/route"; -export default function createUnitTestServer() { +export default function createUnitTestServer(): express.Application { const app = express(); app.use(cors); diff --git a/backend/grading-service/src/types/grading-service.d.ts b/backend/grading-service/src/types/grading-service.d.ts new file mode 100644 index 00000000..151b1fab --- /dev/null +++ b/backend/grading-service/src/types/grading-service.d.ts @@ -0,0 +1,14 @@ +export interface Submission { + id: string; + questionId: string; + studentId: number; + language: string; + code: string; + feedbacks: Feedback[]; + createdOn: number; +} + +export interface Feedback { + line: number; + hints: string[]; +} From fe3a8de621ea5aa1347a2f60dccf2915ae4ad42e Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 19:32:14 +0800 Subject: [PATCH 3/9] move types folder to under src instead of models, add eslint rules --- backend/README.md | 10 ++--- backend/assignment-service/.eslintrc.json | 5 ++- .../src/controllers/assignment-controller.ts | 37 +++++++++++++------ .../src/controllers/base-controller.ts | 4 +- .../src/controllers/question-controller.ts | 34 +++++++++++------ .../src/libs/utils/error-message-utils.ts | 2 +- .../services/assignments/delete-handler.ts | 17 ++++++++- .../src/services/assignments/get-handler.ts | 8 ++-- .../src/services/assignments/post-handler.ts | 2 +- .../src/services/assignments/put-handler.ts | 2 +- .../src/services/questions/get-handler.ts | 6 +-- .../src/services/questions/post-handler.ts | 4 +- .../src/services/questions/put-handler.ts | 4 +- .../assignments/get-assignment-by-id.test.ts | 2 +- .../src/{models => }/types/assignment.d.ts | 0 .../src/{models => }/types/question.d.ts | 0 .../types/reference-solution.d.ts | 0 .../src/{models => }/types/test-case.d.ts | 0 18 files changed, 88 insertions(+), 49 deletions(-) rename backend/assignment-service/src/{models => }/types/assignment.d.ts (100%) rename backend/assignment-service/src/{models => }/types/question.d.ts (100%) rename backend/assignment-service/src/{models => }/types/reference-solution.d.ts (100%) rename backend/assignment-service/src/{models => }/types/test-case.d.ts (100%) diff --git a/backend/README.md b/backend/README.md index 62e779b5..e1373d20 100644 --- a/backend/README.md +++ b/backend/README.md @@ -4,13 +4,11 @@ This folder is intended to contain all the backend related microservices such as authentication, user, grading services, etc. -When working on this folder, ensure you have created a folder for the microservice. Eg: +When working on this folder, ensure you have created a folder for the microservice. Current microservices include: ``` backend - auth - - user - grading - ... + assignment-service + grading-service + user-service ``` diff --git a/backend/assignment-service/.eslintrc.json b/backend/assignment-service/.eslintrc.json index e056dcad..d58fe8b7 100644 --- a/backend/assignment-service/.eslintrc.json +++ b/backend/assignment-service/.eslintrc.json @@ -26,6 +26,9 @@ "varsIgnorePattern": "^_", "ignoreRestSiblings": true } - ] + ], + "@typescript-eslint/explicit-function-return-type": 2, + "@typescript-eslint/explicit-module-boundary-types": 2, + "@typescript-eslint/no-explicit-any": 2 } } diff --git a/backend/assignment-service/src/controllers/assignment-controller.ts b/backend/assignment-service/src/controllers/assignment-controller.ts index 5cb02a4e..7b9809ca 100644 --- a/backend/assignment-service/src/controllers/assignment-controller.ts +++ b/backend/assignment-service/src/controllers/assignment-controller.ts @@ -10,7 +10,10 @@ import { PutHandler } from "../services/assignments/put-handler"; import { formatZodErrorMessage } from "../libs/utils/error-message-utils"; import { GetAssignmentsQueryValidator } from "../libs/validators/assignments/get-assignments-validator"; -const getAssignmentsByUserId = async (request: Request, response: Response) => { +const getAssignmentsByUserId = async ( + request: Request, + response: Response +): Promise => { try { // obtain userId from the query param @@ -24,12 +27,12 @@ const getAssignmentsByUserId = async (request: Request, response: Response) => { const { userId, includePast, isPublished } = GetAssignmentsQueryValidator.parse(request.query); - - const assignments = await GetHandler.getAssignmentsByUserId( - userId, - includePast, - isPublished - ); + + const assignments = await GetHandler.getAssignmentsByUserId( + userId, + includePast, + isPublished + ); if (!assignments) { response.status(HttpStatusCode.NOT_FOUND).json({ @@ -56,7 +59,10 @@ const getAssignmentsByUserId = async (request: Request, response: Response) => { } }; -const getAssignmentById = async (request: Request, response: Response) => { +const getAssignmentById = async ( + request: Request, + response: Response +): Promise => { try { const assignmentId = request.params.id; @@ -79,7 +85,10 @@ const getAssignmentById = async (request: Request, response: Response) => { } }; -const createAssignment = async (request: Request, response: Response) => { +const createAssignment = async ( + request: Request, + response: Response +): Promise => { try { // request body cannot be empty if (!request.body || Object.keys(request.body).length === 0) { @@ -123,7 +132,10 @@ const createAssignment = async (request: Request, response: Response) => { } }; -const updateAssignmentById = async (request: Request, response: Response) => { +const updateAssignmentById = async ( + request: Request, + response: Response +): Promise => { try { if (!request.body || Object.keys(request.body).length === 0) { response.status(HttpStatusCode.BAD_REQUEST).json({ @@ -177,7 +189,10 @@ const updateAssignmentById = async (request: Request, response: Response) => { } }; -const deleteAssignmentById = async (request: Request, response: Response) => { +const deleteAssignmentById = async ( + request: Request, + response: Response +): Promise => { try { const assignmentId = request.params.id; diff --git a/backend/assignment-service/src/controllers/base-controller.ts b/backend/assignment-service/src/controllers/base-controller.ts index 4fc5ee29..3ba5491f 100644 --- a/backend/assignment-service/src/controllers/base-controller.ts +++ b/backend/assignment-service/src/controllers/base-controller.ts @@ -2,9 +2,9 @@ import { Request, Response } from "express"; import HttpStatusCode from "../libs/enums/HttpStatusCode"; import db from "../models/db"; -const getHealth = async (_: Request, response: Response) => { +const getHealth = async (_: Request, response: Response): Promise => { try { - const result = await db.$queryRaw`SELECT 1`; + await db.$queryRaw`SELECT 1`; response.status(HttpStatusCode.OK).json({ message: "Healthy" }); } catch (_error) { diff --git a/backend/assignment-service/src/controllers/question-controller.ts b/backend/assignment-service/src/controllers/question-controller.ts index a53e6042..6be74016 100644 --- a/backend/assignment-service/src/controllers/question-controller.ts +++ b/backend/assignment-service/src/controllers/question-controller.ts @@ -14,7 +14,10 @@ import { CreateQuestionValidator } from "../libs/validators/questions/create-que import { formatZodErrorMessage } from "../libs/utils/error-message-utils"; import DuplicateReferenceSolutionError from "../libs/errors/DuplicateReferenceSolutionError"; -const getQuestionById = async (request: Request, response: Response) => { +const getQuestionById = async ( + request: Request, + response: Response +): Promise => { try { const questionId = request.params.questionId; @@ -40,7 +43,7 @@ const getQuestionById = async (request: Request, response: Response) => { const getQuestionTestCasesById = async ( request: Request, response: Response -) => { +): Promise => { try { const questionId = request.params.questionId; @@ -66,7 +69,7 @@ const getQuestionTestCasesById = async ( const getQuestionReferenceSolutionById = async ( request: Request, response: Response -) => { +): Promise => { try { const questionId = request.params.questionId; @@ -90,7 +93,10 @@ const getQuestionReferenceSolutionById = async ( } }; -const createQuestion = async (request: Request, response: Response) => { +const createQuestion = async ( + request: Request, + response: Response +): Promise => { try { if (!request.body || Object.keys(request.body).length === 0) { response.status(HttpStatusCode.BAD_REQUEST).json({ @@ -147,7 +153,7 @@ const createQuestion = async (request: Request, response: Response) => { const createQuestionReferenceSolution = async ( request: Request, response: Response -) => { +): Promise => { try { if (!request.body || Object.keys(request.body).length === 0) { response.status(HttpStatusCode.BAD_REQUEST).json({ @@ -216,7 +222,7 @@ const createQuestionReferenceSolution = async ( const createQuestionTestCases = async ( request: Request, response: Response -) => { +): Promise => { try { if (!request.body || Object.keys(request.body).length === 0) { response.status(HttpStatusCode.BAD_REQUEST).json({ @@ -273,7 +279,10 @@ const createQuestionTestCases = async ( } }; -const updateQuestionById = async (request: Request, response: Response) => { +const updateQuestionById = async ( + request: Request, + response: Response +): Promise => { try { if (!request.body || Object.keys(request.body).length === 0) { response.status(HttpStatusCode.BAD_REQUEST).json({ @@ -331,7 +340,7 @@ const updateQuestionById = async (request: Request, response: Response) => { const updateQuestionReferenceSolution = async ( request: Request, response: Response -) => { +): Promise => { try { const questionId = request.params.questionId; @@ -390,7 +399,10 @@ const updateQuestionReferenceSolution = async ( } }; -const deleteQuestionById = async (request: Request, response: Response) => { +const deleteQuestionById = async ( + request: Request, + response: Response +): Promise => { try { const questionId = request.params.questionId; @@ -416,7 +428,7 @@ const deleteQuestionById = async (request: Request, response: Response) => { const deleteQuestionReferenceSolutionById = async ( request: Request, response: Response -) => { +): Promise => { try { const questionId = request.params.questionId; @@ -443,7 +455,7 @@ const deleteQuestionReferenceSolutionById = async ( const deleteQuestionTestCasesById = async ( request: Request, response: Response -) => { +): Promise => { try { if (!request.body || Object.keys(request.body).length === 0) { response.status(HttpStatusCode.BAD_REQUEST).json({ diff --git a/backend/assignment-service/src/libs/utils/error-message-utils.ts b/backend/assignment-service/src/libs/utils/error-message-utils.ts index e5b61abd..aa77667e 100644 --- a/backend/assignment-service/src/libs/utils/error-message-utils.ts +++ b/backend/assignment-service/src/libs/utils/error-message-utils.ts @@ -1,6 +1,6 @@ import { ZodError } from "zod"; -export function formatZodErrorMessage(error: ZodError) { +export function formatZodErrorMessage(error: ZodError): string { let errorMessage = JSON.parse(error.message)[0]; if (errorMessage.message === "Required") { diff --git a/backend/assignment-service/src/services/assignments/delete-handler.ts b/backend/assignment-service/src/services/assignments/delete-handler.ts index 28a71541..90a07ed4 100644 --- a/backend/assignment-service/src/services/assignments/delete-handler.ts +++ b/backend/assignment-service/src/services/assignments/delete-handler.ts @@ -1,6 +1,7 @@ import db from "../../models/db"; +import { Assignment } from "../../types/assignment"; -const deleteAssignmentById = async (id: string) => { +const deleteAssignmentById = async (id: string): Promise => { const assignmentExists = await db.assignment.findUnique({ where: { id: id, @@ -17,7 +18,19 @@ const deleteAssignmentById = async (id: string) => { }, }); - return assignment; + const deletedAssignment: Assignment = { + id: assignment.id, + title: assignment.title, + deadline: assignment.deadline.getTime(), + description: assignment.description ?? undefined, + isPublished: assignment.isPublished, + numberOfQuestions: assignment.numberOfQuestions, + authors: assignment.authors, + createdOn: assignment.createdOn.getTime(), + updatedOn: assignment.updatedOn.getTime(), + }; + + return deletedAssignment; }; export const DeleteHandler = { diff --git a/backend/assignment-service/src/services/assignments/get-handler.ts b/backend/assignment-service/src/services/assignments/get-handler.ts index 00dae374..2d8214d8 100644 --- a/backend/assignment-service/src/services/assignments/get-handler.ts +++ b/backend/assignment-service/src/services/assignments/get-handler.ts @@ -1,12 +1,12 @@ import db from "../../models/db"; -import { Assignment } from "../../models/types/assignment"; -import { Question } from "../../models/types/question"; +import { Assignment } from "../../types/assignment"; +import { Question } from "../../types/question"; const getAssignmentsByUserId = async ( userId: number, includePast?: boolean, isPublishedOnly?: boolean -) => { +): Promise => { // check if the user exists const user = await db.user.findUnique({ where: { @@ -29,8 +29,6 @@ const getAssignmentsByUserId = async ( }, }); - // TODO: search user under courses, what assignments are there - const assignmentsDto: Assignment[] = assignments.map((assignment) => { return { id: assignment.id, diff --git a/backend/assignment-service/src/services/assignments/post-handler.ts b/backend/assignment-service/src/services/assignments/post-handler.ts index de859615..25df083c 100644 --- a/backend/assignment-service/src/services/assignments/post-handler.ts +++ b/backend/assignment-service/src/services/assignments/post-handler.ts @@ -1,6 +1,6 @@ import { CreateAssignmentBody } from "../../libs/validators/assignments/create-assignment-validator"; import db from "../../models/db"; -import { Assignment } from "../../models/types/assignment"; +import { Assignment } from "../../types/assignment"; const createAssignment = async (createAssignmentBody: CreateAssignmentBody) => { // convert deadline to Date object diff --git a/backend/assignment-service/src/services/assignments/put-handler.ts b/backend/assignment-service/src/services/assignments/put-handler.ts index 9407fdf4..b1d9acb0 100644 --- a/backend/assignment-service/src/services/assignments/put-handler.ts +++ b/backend/assignment-service/src/services/assignments/put-handler.ts @@ -1,6 +1,6 @@ import { UpdateAssignmentBody } from "../../libs/validators/assignments/update-assignment-validator"; import db from "../../models/db"; -import { Assignment } from "../../models/types/assignment"; +import { Assignment } from "../../types/assignment"; const updateAssignment = async (updateAssignmentBody: UpdateAssignmentBody) => { const updatedAssignment = await db.assignment.update({ diff --git a/backend/assignment-service/src/services/questions/get-handler.ts b/backend/assignment-service/src/services/questions/get-handler.ts index b3d079bd..2694047c 100644 --- a/backend/assignment-service/src/services/questions/get-handler.ts +++ b/backend/assignment-service/src/services/questions/get-handler.ts @@ -1,7 +1,7 @@ import db from "../../models/db"; -import { Question } from "../../models/types/question"; -import { ReferenceSolution } from "../../models/types/reference-solution"; -import { TestCase } from "../../models/types/test-case"; +import { Question } from "../../types/question"; +import { ReferenceSolution } from "../../types/reference-solution"; +import { TestCase } from "../../types/test-case"; const getQuestionById = async (questionId: string) => { const question = await db.question.findUnique({ diff --git a/backend/assignment-service/src/services/questions/post-handler.ts b/backend/assignment-service/src/services/questions/post-handler.ts index b34e1f81..a9681f3a 100644 --- a/backend/assignment-service/src/services/questions/post-handler.ts +++ b/backend/assignment-service/src/services/questions/post-handler.ts @@ -3,8 +3,8 @@ import { CreateQuestionBody } from "../../libs/validators/questions/create-quest import { CreateQuestionReferenceSolutionBody } from "../../libs/validators/questions/create-reference-solution-validator"; import { CreateQuestionTestCasesBody } from "../../libs/validators/questions/create-test-cases-validator"; import db from "../../models/db"; -import { Question } from "../../models/types/question"; -import { ReferenceSolution } from "../../models/types/reference-solution"; +import { Question } from "../../types/question"; +import { ReferenceSolution } from "../../types/reference-solution"; const createQuestion = async (createQuestionBody: CreateQuestionBody) => { // check if assignment exists diff --git a/backend/assignment-service/src/services/questions/put-handler.ts b/backend/assignment-service/src/services/questions/put-handler.ts index 79edc12f..87235301 100644 --- a/backend/assignment-service/src/services/questions/put-handler.ts +++ b/backend/assignment-service/src/services/questions/put-handler.ts @@ -1,8 +1,8 @@ import { UpdateQuestionReferenceSolutionBody } from "../../libs/validators/questions/update-reference-solution-validator"; import { UpdateQuestionBody } from "../../libs/validators/questions/update-question-validator"; import db from "../../models/db"; -import { Question } from "../../models/types/question"; -import { ReferenceSolution } from "../../models/types/reference-solution"; +import { Question } from "../../types/question"; +import { ReferenceSolution } from "../../types/reference-solution"; const updateQuestionById = async (updateQuestionBody: UpdateQuestionBody) => { const updatedQuestion = await db.question.update({ diff --git a/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts b/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts index 3dc779f2..434a2e42 100644 --- a/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts +++ b/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts @@ -4,7 +4,7 @@ import HttpStatusCode from "../../../libs/enums/HttpStatusCode"; import createUnitTestServer from "../../utils/create-test-server-utils"; import supertest from "supertest"; import * as Response from "../../payloads/responses"; -import { Assignment } from "../../../models/types/assignment"; +import { Assignment } from "../../../types/assignment"; const API_PREFIX = "/assignment/api"; diff --git a/backend/assignment-service/src/models/types/assignment.d.ts b/backend/assignment-service/src/types/assignment.d.ts similarity index 100% rename from backend/assignment-service/src/models/types/assignment.d.ts rename to backend/assignment-service/src/types/assignment.d.ts diff --git a/backend/assignment-service/src/models/types/question.d.ts b/backend/assignment-service/src/types/question.d.ts similarity index 100% rename from backend/assignment-service/src/models/types/question.d.ts rename to backend/assignment-service/src/types/question.d.ts diff --git a/backend/assignment-service/src/models/types/reference-solution.d.ts b/backend/assignment-service/src/types/reference-solution.d.ts similarity index 100% rename from backend/assignment-service/src/models/types/reference-solution.d.ts rename to backend/assignment-service/src/types/reference-solution.d.ts diff --git a/backend/assignment-service/src/models/types/test-case.d.ts b/backend/assignment-service/src/types/test-case.d.ts similarity index 100% rename from backend/assignment-service/src/models/types/test-case.d.ts rename to backend/assignment-service/src/types/test-case.d.ts From 7cc01c68c6c438c025224dbe24cc5e496d149599 Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 20:17:27 +0800 Subject: [PATCH 4/9] fix assignment service eslint errors --- .../src/services/assignments/get-handler.ts | 2 +- .../src/services/assignments/post-handler.ts | 4 +++- .../src/services/assignments/put-handler.ts | 4 +++- .../src/services/questions/delete-handler.ts | 21 +++++++++++++++---- .../src/services/questions/get-handler.ts | 12 ++++++++--- .../src/services/questions/post-handler.ts | 15 ++++++++++--- .../src/services/questions/put-handler.ts | 6 ++++-- .../update-assignment-request-body.ts | 7 ++++++- ...uestion-reference-solution-request-body.ts | 5 ++++- .../questions/create-question-request-body.ts | 15 ++++++++++++- ...create-question-test-cases-request-body.ts | 8 ++++++- ...uestion-reference-solution-request-body.ts | 5 ++++- .../questions/update-question-request-body.ts | 6 +++++- .../get-assignment-by-id-response.ts | 2 ++ .../assignments/get-assignments-response.ts | 2 ++ .../get-created-assignment-response.ts | 2 ++ .../get-updated-assignment-response.ts | 2 ++ .../get-created-question-response.ts | 2 ++ ...get-created-reference-solution-response.ts | 2 ++ .../get-created-test-cases-response.ts | 2 ++ .../questions/get-question-by-id-response.ts | 2 ++ ...et-question-reference-solution-response.ts | 2 ++ .../get-question-test-cases-response.ts | 2 ++ .../get-updated-question-response.ts | 2 ++ ...get-updated-reference-solution-response.ts | 2 ++ .../delete-assignment-by-id.test.ts | 7 ++++++- .../assignments/get-assignment-by-id.test.ts | 3 +-- .../get-assignments-by-user-id.test.ts | 3 +-- .../questions/delete-question-by-id.test.ts | 15 ++++++++----- ...delete-question-reference-solution.test.ts | 1 - .../tests/utils/create-test-server-utils.ts | 3 +-- 31 files changed, 132 insertions(+), 34 deletions(-) diff --git a/backend/assignment-service/src/services/assignments/get-handler.ts b/backend/assignment-service/src/services/assignments/get-handler.ts index 2d8214d8..5cdcf76c 100644 --- a/backend/assignment-service/src/services/assignments/get-handler.ts +++ b/backend/assignment-service/src/services/assignments/get-handler.ts @@ -45,7 +45,7 @@ const getAssignmentsByUserId = async ( return assignmentsDto; }; -const getAssignmentById = async (id: string) => { +const getAssignmentById = async (id: string): Promise => { const assignment = await db.assignment.findUnique({ where: { id: id, diff --git a/backend/assignment-service/src/services/assignments/post-handler.ts b/backend/assignment-service/src/services/assignments/post-handler.ts index 25df083c..4d2e9297 100644 --- a/backend/assignment-service/src/services/assignments/post-handler.ts +++ b/backend/assignment-service/src/services/assignments/post-handler.ts @@ -2,7 +2,9 @@ import { CreateAssignmentBody } from "../../libs/validators/assignments/create-a import db from "../../models/db"; import { Assignment } from "../../types/assignment"; -const createAssignment = async (createAssignmentBody: CreateAssignmentBody) => { +const createAssignment = async ( + createAssignmentBody: CreateAssignmentBody +): Promise => { // convert deadline to Date object const assignment = await db.assignment.create({ data: { diff --git a/backend/assignment-service/src/services/assignments/put-handler.ts b/backend/assignment-service/src/services/assignments/put-handler.ts index b1d9acb0..5a8bbca5 100644 --- a/backend/assignment-service/src/services/assignments/put-handler.ts +++ b/backend/assignment-service/src/services/assignments/put-handler.ts @@ -2,7 +2,9 @@ import { UpdateAssignmentBody } from "../../libs/validators/assignments/update-a import db from "../../models/db"; import { Assignment } from "../../types/assignment"; -const updateAssignment = async (updateAssignmentBody: UpdateAssignmentBody) => { +const updateAssignment = async ( + updateAssignmentBody: UpdateAssignmentBody +): Promise => { const updatedAssignment = await db.assignment.update({ where: { id: updateAssignmentBody.assignmentId, diff --git a/backend/assignment-service/src/services/questions/delete-handler.ts b/backend/assignment-service/src/services/questions/delete-handler.ts index 3573bf30..d79989f8 100644 --- a/backend/assignment-service/src/services/questions/delete-handler.ts +++ b/backend/assignment-service/src/services/questions/delete-handler.ts @@ -1,6 +1,9 @@ import db from "../../models/db"; +import { Question } from "../../types/question"; +import { ReferenceSolution } from "../../types/reference-solution"; +import { Prisma } from "@prisma/client"; -const deleteQuestion = async (questionId: string) => { +const deleteQuestion = async (questionId: string): Promise => { const questionExists = await db.question.findUnique({ where: { id: questionId, @@ -29,10 +32,20 @@ const deleteQuestion = async (questionId: string) => { }, }); - return question; + const deletedQuestion: Question = { + id: question.id, + title: question.title, + description: question.description ?? undefined, + numberOfTestCases: question.numberOfTestCases, + createdOn: question.createdOn.getTime(), + }; + + return deletedQuestion; }; -const deleteQuestionReferenceSolution = async (questionId: string) => { +const deleteQuestionReferenceSolution = async ( + questionId: string +): Promise => { const questionExists = await db.question.findUnique({ where: { id: questionId, @@ -75,7 +88,7 @@ const deleteQuestionReferenceSolution = async (questionId: string) => { const deleteQuestionTestCases = async ( questionId: string, testCaseIds: string[] -) => { +): Promise => { const questionExists = await db.question.findUnique({ where: { id: questionId, diff --git a/backend/assignment-service/src/services/questions/get-handler.ts b/backend/assignment-service/src/services/questions/get-handler.ts index 2694047c..3bafcd2b 100644 --- a/backend/assignment-service/src/services/questions/get-handler.ts +++ b/backend/assignment-service/src/services/questions/get-handler.ts @@ -3,7 +3,9 @@ import { Question } from "../../types/question"; import { ReferenceSolution } from "../../types/reference-solution"; import { TestCase } from "../../types/test-case"; -const getQuestionById = async (questionId: string) => { +const getQuestionById = async ( + questionId: string +): Promise => { const question = await db.question.findUnique({ where: { id: questionId, @@ -41,7 +43,9 @@ const getQuestionById = async (questionId: string) => { return questionDto; }; -const getQuestionReferenceSolution = async (questionId: string) => { +const getQuestionReferenceSolution = async ( + questionId: string +): Promise => { const referenceSolution = await db.referenceSolution.findFirst({ where: { questionId: questionId, @@ -62,7 +66,9 @@ const getQuestionReferenceSolution = async (questionId: string) => { return referenceSolutionDto; }; -const getQuestionTestCases = async (questionId: string) => { +const getQuestionTestCases = async ( + questionId: string +): Promise => { const testCases = await db.testCase.findMany({ where: { questionId: questionId, diff --git a/backend/assignment-service/src/services/questions/post-handler.ts b/backend/assignment-service/src/services/questions/post-handler.ts index a9681f3a..e36f16aa 100644 --- a/backend/assignment-service/src/services/questions/post-handler.ts +++ b/backend/assignment-service/src/services/questions/post-handler.ts @@ -6,7 +6,9 @@ import db from "../../models/db"; import { Question } from "../../types/question"; import { ReferenceSolution } from "../../types/reference-solution"; -const createQuestion = async (createQuestionBody: CreateQuestionBody) => { +const createQuestion = async ( + createQuestionBody: CreateQuestionBody +): Promise => { // check if assignment exists const assignment = await db.assignment.findUnique({ where: { @@ -84,7 +86,7 @@ const createQuestion = async (createQuestionBody: CreateQuestionBody) => { const createQuestionReferenceSolution = async ( createQuestionReferenceSolutionBody: CreateQuestionReferenceSolutionBody -) => { +): Promise => { const questionExists = await db.question.findUnique({ where: { id: createQuestionReferenceSolutionBody.id, @@ -128,7 +130,14 @@ const createQuestionReferenceSolution = async ( const createQuestionTestCases = async ( createQuestionTestCasesBody: CreateQuestionTestCasesBody -) => { +): Promise<{ + count: number; + testCases: { + input: string; + output: string; + isPublic?: boolean; + }[]; +} | null> => { const questionExists = await db.question.findUnique({ where: { id: createQuestionTestCasesBody.questionId, diff --git a/backend/assignment-service/src/services/questions/put-handler.ts b/backend/assignment-service/src/services/questions/put-handler.ts index 87235301..df40fab8 100644 --- a/backend/assignment-service/src/services/questions/put-handler.ts +++ b/backend/assignment-service/src/services/questions/put-handler.ts @@ -4,7 +4,9 @@ import db from "../../models/db"; import { Question } from "../../types/question"; import { ReferenceSolution } from "../../types/reference-solution"; -const updateQuestionById = async (updateQuestionBody: UpdateQuestionBody) => { +const updateQuestionById = async ( + updateQuestionBody: UpdateQuestionBody +): Promise => { const updatedQuestion = await db.question.update({ where: { id: updateQuestionBody.questionId, @@ -51,7 +53,7 @@ const updateQuestionById = async (updateQuestionBody: UpdateQuestionBody) => { const updateQuestionReferenceSolution = async ( updateQuestionReferenceSolutionBody: UpdateQuestionReferenceSolutionBody -) => { +): Promise => { const questionExists = await db.question.findUnique({ where: { id: updateQuestionReferenceSolutionBody.id, diff --git a/backend/assignment-service/src/tests/payloads/requests/assignments/update-assignment-request-body.ts b/backend/assignment-service/src/tests/payloads/requests/assignments/update-assignment-request-body.ts index 43447275..c3ee09ad 100644 --- a/backend/assignment-service/src/tests/payloads/requests/assignments/update-assignment-request-body.ts +++ b/backend/assignment-service/src/tests/payloads/requests/assignments/update-assignment-request-body.ts @@ -1,4 +1,9 @@ -export const getUpdateAssignmentRequestBody = () => { +export const getUpdateAssignmentRequestBody = (): { + title: string; + deadline: number; + authors: number[]; + isPublished: boolean; +} => { return { title: "Updated Assignment 1", deadline: new Date("2024-12-31T23:59:59.998Z").getTime(), diff --git a/backend/assignment-service/src/tests/payloads/requests/questions/create-question-reference-solution-request-body.ts b/backend/assignment-service/src/tests/payloads/requests/questions/create-question-reference-solution-request-body.ts index 8cdb479e..bf2ebb59 100644 --- a/backend/assignment-service/src/tests/payloads/requests/questions/create-question-reference-solution-request-body.ts +++ b/backend/assignment-service/src/tests/payloads/requests/questions/create-question-reference-solution-request-body.ts @@ -1,4 +1,7 @@ -export const getCreateQuestionReferenceSolutionRequestBody = () => { +export const getCreateQuestionReferenceSolutionRequestBody = (): { + language: "python" | "c"; + code: string; +} => { return { language: "python" as "python" | "c", code: "print('Hello, World!')", diff --git a/backend/assignment-service/src/tests/payloads/requests/questions/create-question-request-body.ts b/backend/assignment-service/src/tests/payloads/requests/questions/create-question-request-body.ts index 513e7c39..2fcbb455 100644 --- a/backend/assignment-service/src/tests/payloads/requests/questions/create-question-request-body.ts +++ b/backend/assignment-service/src/tests/payloads/requests/questions/create-question-request-body.ts @@ -4,7 +4,20 @@ export const getCreateQuestionRequestBody = ({ }: { includeReferenceSolution?: boolean; includeTestCases?: boolean; -} = {}) => { +} = {}): { + title: string; + description: string; + deadline: number; + testCases?: { + input: string; + output: string; + isPublic: boolean; + }[]; + referenceSolution?: { + language: "python" | "c"; + code: string; + }; +} => { includeReferenceSolution = includeReferenceSolution ?? true; includeTestCases = includeTestCases ?? true; diff --git a/backend/assignment-service/src/tests/payloads/requests/questions/create-question-test-cases-request-body.ts b/backend/assignment-service/src/tests/payloads/requests/questions/create-question-test-cases-request-body.ts index c9aefb68..f78ff9f5 100644 --- a/backend/assignment-service/src/tests/payloads/requests/questions/create-question-test-cases-request-body.ts +++ b/backend/assignment-service/src/tests/payloads/requests/questions/create-question-test-cases-request-body.ts @@ -1,4 +1,10 @@ -export const getCreateQuestionTestCasesRequestBody = () => { +export const getCreateQuestionTestCasesRequestBody = (): { + testCases: { + input: string; + output: string; + isPublic: boolean; + }[]; +} => { return { testCases: [ { diff --git a/backend/assignment-service/src/tests/payloads/requests/questions/update-question-reference-solution-request-body.ts b/backend/assignment-service/src/tests/payloads/requests/questions/update-question-reference-solution-request-body.ts index 77edd483..dda28be4 100644 --- a/backend/assignment-service/src/tests/payloads/requests/questions/update-question-reference-solution-request-body.ts +++ b/backend/assignment-service/src/tests/payloads/requests/questions/update-question-reference-solution-request-body.ts @@ -1,4 +1,7 @@ -export const getUpdateQuestionReferenceSolutionRequestBody = () => { +export const getUpdateQuestionReferenceSolutionRequestBody = (): { + language: "python" | "c"; + code: string; +} => { return { language: "c" as "python" | "c", code: '#include \n\nint main() {\n printf("Hello, World!");\n return 0;\n}', diff --git a/backend/assignment-service/src/tests/payloads/requests/questions/update-question-request-body.ts b/backend/assignment-service/src/tests/payloads/requests/questions/update-question-request-body.ts index 66b5f49e..37887c35 100644 --- a/backend/assignment-service/src/tests/payloads/requests/questions/update-question-request-body.ts +++ b/backend/assignment-service/src/tests/payloads/requests/questions/update-question-request-body.ts @@ -1,4 +1,8 @@ -export const getUpdateQuestionRequestBody = () => { +export const getUpdateQuestionRequestBody = (): { + title: string; + description: string; + deadline: number; +} => { return { title: "Updated Question Title", description: "

Updated Question Description

", diff --git a/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignment-by-id-response.ts b/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignment-by-id-response.ts index b0a706e1..fed57c9f 100644 --- a/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignment-by-id-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignment-by-id-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getAssignmentByIdDbResponse = ( assignmentId?: string, userId?: number diff --git a/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignments-response.ts b/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignments-response.ts index 2644c968..a45c247f 100644 --- a/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignments-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/assignments/get-assignments-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const GetAssignmentsResponse = (userId: string) => { const deadline = new Date("2024-12-31T00:00:00.000Z").getTime(); const createdOn = new Date("2024-03-12T00:00:00.000Z").getTime(); diff --git a/backend/assignment-service/src/tests/payloads/responses/assignments/get-created-assignment-response.ts b/backend/assignment-service/src/tests/payloads/responses/assignments/get-created-assignment-response.ts index 768e2412..79ebc85c 100644 --- a/backend/assignment-service/src/tests/payloads/responses/assignments/get-created-assignment-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/assignments/get-created-assignment-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getCreatedAssignmentExpectedResponse = ( assignmentId?: string, userId?: number diff --git a/backend/assignment-service/src/tests/payloads/responses/assignments/get-updated-assignment-response.ts b/backend/assignment-service/src/tests/payloads/responses/assignments/get-updated-assignment-response.ts index 22d92da6..cbc29ca7 100644 --- a/backend/assignment-service/src/tests/payloads/responses/assignments/get-updated-assignment-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/assignments/get-updated-assignment-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getUpdatedAssignmentDbResponse = () => { return { id: "existing-assignment-id", diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-created-question-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-created-question-response.ts index 5346fa0e..0b439ce6 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-created-question-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-created-question-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getCreatedQuestionDbResponse = ( includeReferenceSolution: boolean = true ) => { diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-created-reference-solution-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-created-reference-solution-response.ts index 89d41b91..702b6682 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-created-reference-solution-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-created-reference-solution-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getCreatedReferenceSolutionDbResponse = () => { return { id: "new-reference-solution-id", diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-created-test-cases-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-created-test-cases-response.ts index 1a8ae447..10460d41 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-created-test-cases-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-created-test-cases-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getCreatedTestCasesDbResponse = () => { return [ { diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-question-by-id-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-question-by-id-response.ts index 85bfed80..e34c4395 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-question-by-id-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-question-by-id-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getQuestionByIdDbResponse = (questionId?: string) => { return { id: questionId ?? "existing-question-id", diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-question-reference-solution-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-question-reference-solution-response.ts index 177bb657..89aad49a 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-question-reference-solution-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-question-reference-solution-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getQuestionReferenceSolutionDbResponse = (questionId?: string) => { return { id: "existing-solution-id-1", diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-question-test-cases-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-question-test-cases-response.ts index 0c6fe889..5aef5475 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-question-test-cases-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-question-test-cases-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getQuestionTestCasesDbResponse = () => [ { id: "test-case-id-1", diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-question-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-question-response.ts index d7452b65..30c24a01 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-question-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-question-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getUpdatedQuestionDbResponse = () => { return { id: "existing-question-id", diff --git a/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-reference-solution-response.ts b/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-reference-solution-response.ts index 878bf21d..7258eac6 100644 --- a/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-reference-solution-response.ts +++ b/backend/assignment-service/src/tests/payloads/responses/questions/get-updated-reference-solution-response.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type */ + export const getUpdatedReferenceSolutionDbResponse = () => { return { id: "existing-reference-solution-id", diff --git a/backend/assignment-service/src/tests/unit/assignments/delete-assignment-by-id.test.ts b/backend/assignment-service/src/tests/unit/assignments/delete-assignment-by-id.test.ts index 9dbd412e..4381e061 100644 --- a/backend/assignment-service/src/tests/unit/assignments/delete-assignment-by-id.test.ts +++ b/backend/assignment-service/src/tests/unit/assignments/delete-assignment-by-id.test.ts @@ -77,7 +77,9 @@ describe("Unit Tests for DELETE /assignment/api/assignments/:id", () => { const assignmentId = "existing-assignment-id"; const spy = jest .spyOn(DeleteHandler, "deleteAssignmentById") - .mockResolvedValue(Response.getAssignmentByIdDbResponse(assignmentId)); + .mockResolvedValue( + Response.getAssignmentByIdExpectedResponse(assignmentId) + ); // Act const response = await supertest(app).delete( @@ -136,6 +138,9 @@ describe("Unit Tests for DELETE /assignment/api/assignments/:id", () => { error: "INTERNAL SERVER ERROR", message: "An unexpected error has occurred. Please try again later", }); + + // reset the mocks + spy.mockRestore(); }); }); }); diff --git a/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts b/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts index 434a2e42..f04e4dec 100644 --- a/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts +++ b/backend/assignment-service/src/tests/unit/assignments/get-assignment-by-id.test.ts @@ -105,7 +105,6 @@ describe("Unit Tests for getAssignmentById", () => { describe("Unit Tests for GET /assignment/api/assignments/:id", () => { const app = createUnitTestServer(); - const assignmentServiceMock = GetHandler as jest.Mocked; describe("Given an existing assignment id", () => { it("should return 200 and the assignment with questions", async () => { @@ -182,7 +181,7 @@ describe("Unit Tests for GET /assignment/api/assignments/:id", () => { }); }); -function assertAssignment(assignment: Assignment | null) { +function assertAssignment(assignment: Assignment | null): void { expect(assignment).not.toBeNull(); if (assignment) { diff --git a/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts b/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts index 6b632eb8..6360cb66 100644 --- a/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts +++ b/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts @@ -40,10 +40,9 @@ describe("Unit Tests for getAssignmentsByUserId", () => { }); describe("Unit Tests for GET /assignments?userId=:userId", () => { - let app: Express; + const app = createUnitTestServer(); beforeAll(() => { - app = createUnitTestServer(); jest.mock("../../../services/assignments/get-handler", () => ({ GetHandler: { getAssignmentsByUserId: jest.fn(), diff --git a/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts b/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts index db2e27c0..9c8e25e3 100644 --- a/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts +++ b/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts @@ -76,11 +76,7 @@ describe("Unit Tests for DELETE /assignment/api/questions/:id", () => { const questionId = "existing-question-id"; const spy = jest .spyOn(DeleteHandler, "deleteQuestion") - .mockResolvedValue({ - ...Response.getQuestionByIdExpectedResponse(), - deadline: new Date(), - createdOn: new Date(), - }); + .mockResolvedValue(Response.getQuestionByIdExpectedResponse()); // Act const response = await supertest(app) @@ -90,6 +86,9 @@ describe("Unit Tests for DELETE /assignment/api/questions/:id", () => { // Assert expect(response.status).toBe(HttpStatusCode.NO_CONTENT); expect(response.body).toEqual({}); + + // reset the mocks + spy.mockRestore(); }); }); @@ -112,6 +111,9 @@ describe("Unit Tests for DELETE /assignment/api/questions/:id", () => { error: "NOT FOUND", message: "Question not found", }); + + // reset the mocks + spy.mockRestore(); }); }); @@ -134,6 +136,9 @@ describe("Unit Tests for DELETE /assignment/api/questions/:id", () => { error: "INTERNAL SERVER ERROR", message: "An unexpected error has occurred. Please try again later", }); + + // reset the mocks + spy.mockRestore(); }); }); }); diff --git a/backend/assignment-service/src/tests/unit/questions/delete-question-reference-solution.test.ts b/backend/assignment-service/src/tests/unit/questions/delete-question-reference-solution.test.ts index 8715ac62..42dae974 100644 --- a/backend/assignment-service/src/tests/unit/questions/delete-question-reference-solution.test.ts +++ b/backend/assignment-service/src/tests/unit/questions/delete-question-reference-solution.test.ts @@ -102,7 +102,6 @@ describe("Unit Tests for DELETE /assignment/api/questions/:id/solution", () => { questionId: questionId, language: "python", code: "print('Hello, World!')", - codeParser: "some code parser value", }); // Act diff --git a/backend/assignment-service/src/tests/utils/create-test-server-utils.ts b/backend/assignment-service/src/tests/utils/create-test-server-utils.ts index 696e5783..3a9aa300 100644 --- a/backend/assignment-service/src/tests/utils/create-test-server-utils.ts +++ b/backend/assignment-service/src/tests/utils/create-test-server-utils.ts @@ -2,9 +2,8 @@ import express from "express"; import cors from "../../middlewares/cors"; import bodyParser from "body-parser"; import router from "../../routes"; -import HttpStatusCode from "../../libs/enums/HttpStatusCode"; -export default function createUnitTestServer() { +export default function createUnitTestServer(): express.Application { const app = express(); app.use(cors); From 70c28a66c434651bc0212f466b2b90513e0640bc Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 20:21:09 +0800 Subject: [PATCH 5/9] include a README for guideline on how to set up the project --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..1e931846 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Intelligent Tutoring System (ITS) + +## Description + +Intelligent Tutoring System (ITS) is a platform that can provide high-level feedbacks to coding assignment automatically. It aims to alleviate the workload required for grading coding assignments. + +## Project Structure + +This project represents the frontend user interfaces of the ITS platform. We are running microservice architecture on the project: + +``` +├── backend +│ ├── assignment-service +│ ├── user-service +| └── grading-service +├── frontend +├── package.json +├── LICENSE +├── docker-compose.yml +├── .gitignore +└── README.md +``` + +## Prerequisites + +Before running the project, make sure you have access to the following: + +- [NodeJS](https://nodejs.org/en/download) environment with version 20 and above +- [Environmental secrets](https://drive.google.com/drive/folders/1yuXEM5f18HtmWPlxYBW2Lmy--jwZ68ck?usp=sharing) for each microservice + +## Local Development + +Follow these steps to set up and run the project for local development: + +1. Clone the repository: `git clone https://github.com/tryyang2001/CS3213-Frontend-Management-System.git` +2. Copy `.env` files obtained from the drive into directories: `frontend`, `backend/assignment-service`, `backend/user-service`, and `backend/grading-service` respectively +3. At the root directory, run `yarn setup` to install all dependencies for all microservices. Take note that this process might take longer to finish (< 10 mins) +4. At the root directory, you may start the development server: `yarn dev` + +To run service individually, you will need to change path directory to the target directory, and follow by `yarn dev`. For example, to run only `assignment-service`: + +``` +cd backend/assignment-service +yarn dev +``` + +## Production Build and Deployment + +Follow these steps to build and run the project in production environment: + +1. Follow steps 1, 2, and 3 in local development for setting up the project +2. At the root directory, you may start the server by running `yarn start` + +## Contact + +If you encountered any problem or need clarification from the developers, kindly contact tanruiyang01@u.nus.edu. From 140c5e0605f4afe0ffd5c96be3e6db747cb9db5f Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 20:32:36 +0800 Subject: [PATCH 6/9] fix assignment test errors and update scripts --- backend/assignment-service/package.json | 2 +- .../unit/assignments/get-assignments-by-user-id.test.ts | 1 - .../src/tests/unit/questions/delete-question-by-id.test.ts | 4 ++++ backend/grading-service/package.json | 2 +- frontend/package.json | 5 ++++- package.json | 4 ++-- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/backend/assignment-service/package.json b/backend/assignment-service/package.json index 852925ff..cc64f16e 100644 --- a/backend/assignment-service/package.json +++ b/backend/assignment-service/package.json @@ -9,7 +9,7 @@ "scripts": { "dev": "prisma generate && nodemon src/app.ts", "pure-dev": "nodemon src/app.ts", - "build": "prisma generate && tsc -p .", + "build": "prisma generate && yarn lint && tsc -p .", "start": "node dist/app.js", "test": "jest --coverage --verbose", "db": "prisma studio", diff --git a/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts b/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts index 6360cb66..3e3027c2 100644 --- a/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts +++ b/backend/assignment-service/src/tests/unit/assignments/get-assignments-by-user-id.test.ts @@ -1,4 +1,3 @@ -import { Express } from "express"; import { PrismaClient } from "@prisma/client"; import db from "../../../models/db"; import { GetHandler } from "../../../services/assignments/get-handler"; diff --git a/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts b/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts index 9c8e25e3..0ced1dda 100644 --- a/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts +++ b/backend/assignment-service/src/tests/unit/questions/delete-question-by-id.test.ts @@ -19,6 +19,10 @@ describe("Unit Tests for deleteQuestion", () => { }); dbMock.question.delete = jest.fn().mockResolvedValue({ id: questionId, + title: "Question Title", + description: "Question Description", + numberOfTestCases: 1, + createdOn: new Date(), }); dbMock.assignment.update = jest.fn().mockResolvedValue({ id: "assignment-id", diff --git a/backend/grading-service/package.json b/backend/grading-service/package.json index ef73c53a..941ea1ae 100644 --- a/backend/grading-service/package.json +++ b/backend/grading-service/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "prisma generate && nodemon src/app.ts", "pure-dev": "nodemon src/app.ts", - "build": "prisma generate && tsc -p .", + "build": "prisma generate && yarn lint && tsc -p .", "start": "node dist/app.js", "test": "jest --coverage --verbose", "db": "prisma studio", diff --git a/frontend/package.json b/frontend/package.json index f6ec6faf..a390b30d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,10 @@ "dev-all": "concurrently --kill-others \"yarn dev:frontend\" \"yarn dev:grading\" \"yarn dev:assignment\" \"yarn dev:user\"", "build": "next build", "start": "next start", - "start-all": "concurrently --kill-others \"yarn start\" \"yarn dev:grading\" \"yarn dev:assignment\" \"yarn dev:user\"", + "start:user": "cd ../backend/user-service && yarn start", + "start:assignment": "cd ../backend/assignment-service && yarn start", + "start:grading": "cd ../backend/grading-service && yarn start", + "start-all": "concurrently --kill-others \"yarn start\" \"yarn start:grading\" \"yarn start:assignment\" \"yarn start:user\"", "lint": "next lint && prettier --check --ignore-path .gitignore .", "lint:autofix": "next lint --fix && prettier --write --ignore-path .gitignore .", "test": "jest", diff --git a/package.json b/package.json index 6b752ead..49ff5d84 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "author": "fmdevs", "license": "MIT", "scripts": { - "setup:assignment": "cd backend/assignment-service && yarn install && yarn prisma generate", + "setup:assignment": "cd backend/assignment-service && yarn install && yarn build", "setup:user": "cd backend/user-service && yarn install && yarn build", - "setup:grading": "cd backend/grading-service && yarn install && yarn prisma generate", + "setup:grading": "cd backend/grading-service && yarn install && yarn build", "setup:frontend": "cd frontend && yarn install && yarn build", "setup": "yarn setup:assignment && yarn setup:user && yarn setup:grading && yarn setup:frontend", "start": "cd frontend && yarn start-all", From 1e6e2318b85482075d379dbe49ded96b8d8c5fe3 Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 21:46:48 +0800 Subject: [PATCH 7/9] fix hydration errors by using useEffect with combination of isLoading state --- frontend/src/app/assignments/[id]/page.tsx | 24 ++++++++++++++----- frontend/src/app/dashboard/page.tsx | 5 +--- .../components/assignment/AssignmentList.tsx | 15 +++++++++++- frontend/src/components/common/SideBar.tsx | 10 +++++++- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/assignments/[id]/page.tsx b/frontend/src/app/assignments/[id]/page.tsx index b0a87c9f..99051901 100644 --- a/frontend/src/app/assignments/[id]/page.tsx +++ b/frontend/src/app/assignments/[id]/page.tsx @@ -23,6 +23,7 @@ import { } from "@nextui-org/react"; import { useQuery } from "@tanstack/react-query"; import { notFound, useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; interface Props { params: { @@ -39,14 +40,21 @@ export default function Page({ params }: Props) { const { toast } = useToast(); - // TODO: replace below code with actual user context to check for user role const { user } = useUserContext(); + const userId = user?.uid ?? 0; - const userRole = user?.role ?? "student"; + const userRole = user?.role; + + const [isLoadingUserRole, setIsLoadingUserRole] = useState(true); + + useEffect(() => { + if (userRole) { + setIsLoadingUserRole(false); + } + }, [userRole]); const { data: assignment, - isLoading, isFetched, isError, } = useQuery({ @@ -117,11 +125,13 @@ export default function Page({ params }: Props) { } }; + if (isLoadingUserRole) { + return ; + } + return (
- {isLoading ? ( - - ) : ( + {isFetched ? (
{/* Assignment details */} @@ -254,6 +264,8 @@ export default function Page({ params }: Props) { return ; })}
+ ) : ( + )}
); diff --git a/frontend/src/app/dashboard/page.tsx b/frontend/src/app/dashboard/page.tsx index c4aa493d..9359bf4b 100644 --- a/frontend/src/app/dashboard/page.tsx +++ b/frontend/src/app/dashboard/page.tsx @@ -35,10 +35,7 @@ export default function DashBoard() { {isLoading ? ( ) : ( - + )}
); diff --git a/frontend/src/components/assignment/AssignmentList.tsx b/frontend/src/components/assignment/AssignmentList.tsx index bccf60d2..326de00c 100644 --- a/frontend/src/components/assignment/AssignmentList.tsx +++ b/frontend/src/components/assignment/AssignmentList.tsx @@ -3,23 +3,36 @@ import { Button, Card, CardBody, Spacer } from "@nextui-org/react"; import DateUtils from "@/utils/dateUtils"; import { notFound, useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import LogoLoading from "../common/LogoLoading"; interface Props { assignments: Assignment[] | undefined; - userRole: string; + userRole: string | undefined; } function AssignmentList({ assignments, userRole }: Props) { const router = useRouter(); + const [isLoading, setIsLoading] = useState(true); if (!assignments) { return notFound(); } + useEffect(() => { + if (userRole) { + setIsLoading(false); + } + }, [userRole]); + const handleButtonClick = (id: string) => { router.push(`/assignments/${id}`); }; + if (isLoading) { + return ; + } + return (
Assignments diff --git a/frontend/src/components/common/SideBar.tsx b/frontend/src/components/common/SideBar.tsx index 15e509aa..89c5c51f 100644 --- a/frontend/src/components/common/SideBar.tsx +++ b/frontend/src/components/common/SideBar.tsx @@ -51,6 +51,8 @@ export default function SideBar() { } ); + const [isLoadingUserRole, setIsLoadingUserRole] = useState(true); + const onMouseOver = () => { setIsCollapsible(!isCollapsible); }; @@ -81,6 +83,8 @@ export default function SideBar() { fetchUserInfo().catch((_err) => { return; }); + + setIsLoadingUserRole(false); } // router does not change // eslint-disable-next-line react-hooks/exhaustive-deps @@ -89,7 +93,11 @@ export default function SideBar() { // obtain current path, if is login/sign up, don't render SideBar const currentPath = usePathname(); - if (currentPath === "/login" || currentPath === "/sign-up") { + if ( + currentPath === "/login" || + currentPath === "/sign-up" || + isLoadingUserRole + ) { return <>; } From fad4e0bbad1a1f4687b3eeb41ee52f5e07b53ee8 Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 21:49:02 +0800 Subject: [PATCH 8/9] fix conditional useEffect problem --- frontend/src/components/assignment/AssignmentList.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/assignment/AssignmentList.tsx b/frontend/src/components/assignment/AssignmentList.tsx index 326de00c..5391b05f 100644 --- a/frontend/src/components/assignment/AssignmentList.tsx +++ b/frontend/src/components/assignment/AssignmentList.tsx @@ -15,16 +15,16 @@ function AssignmentList({ assignments, userRole }: Props) { const router = useRouter(); const [isLoading, setIsLoading] = useState(true); - if (!assignments) { - return notFound(); - } - useEffect(() => { if (userRole) { setIsLoading(false); } }, [userRole]); + if (!assignments) { + return notFound(); + } + const handleButtonClick = (id: string) => { router.push(`/assignments/${id}`); }; From 800d4f9b387060bf4bc1da84b25c2c7b9ae316d8 Mon Sep 17 00:00:00 2001 From: tryyang2001 Date: Sun, 14 Apr 2024 21:52:08 +0800 Subject: [PATCH 9/9] update frontend test due to assignment page rendering changes --- .../__snapshots__/pages.test.tsx.snap | 106 +++--------------- 1 file changed, 18 insertions(+), 88 deletions(-) diff --git a/frontend/src/app/__tests__/__snapshots__/pages.test.tsx.snap b/frontend/src/app/__tests__/__snapshots__/pages.test.tsx.snap index 880c3e30..68163577 100644 --- a/frontend/src/app/__tests__/__snapshots__/pages.test.tsx.snap +++ b/frontend/src/app/__tests__/__snapshots__/pages.test.tsx.snap @@ -154,107 +154,37 @@ exports[`Page Snapshot tests Loading Assignment Page Snapshot test 1`] = `
-
-

- Assignment 1 -

-
-

- Due on: - - - Thursday, January 1, 1970 at 07:30 AM - -

-

- Number of questions: - - - 2 - -

-
-
-
-
-
-
-
- Question 1 -
-
- -
-
- description 1 -
-
-
-
- Question 2 -
-
- -
-
- description 2 -
-
+ Loading...
+