diff --git a/backend/src/controllers/error-helper.ts b/backend/src/controllers/error-helper.ts index 0bc3a9d4..da627d72 100644 --- a/backend/src/controllers/error-helper.ts +++ b/backend/src/controllers/error-helper.ts @@ -1,4 +1,4 @@ -import { BadRequestException } from '../exceptions.js'; +import {BadRequestException} from "../exceptions/bad-request-exception"; /** * Checks for the presence of required fields and throws a BadRequestException diff --git a/backend/src/controllers/students.ts b/backend/src/controllers/students.ts index 92e774e2..2c200c9f 100644 --- a/backend/src/controllers/students.ts +++ b/backend/src/controllers/students.ts @@ -7,8 +7,7 @@ import { getStudent, getStudentAssignments, getStudentClasses, - getStudentGroups, - getStudentQuestions, + getStudentGroups, getStudentQuestions, getStudentSubmissions, } from '../services/students.js'; import { StudentDTO } from '../interfaces/student.js'; diff --git a/backend/src/interfaces/student-request.ts b/backend/src/interfaces/student-request.ts index 9f496046..6ced158d 100644 --- a/backend/src/interfaces/student-request.ts +++ b/backend/src/interfaces/student-request.ts @@ -1,5 +1,8 @@ import {mapToStudentDTO, StudentDTO} from "./student"; import {ClassJoinRequest, ClassJoinRequestStatus} from "../entities/classes/class-join-request.entity"; +import {getClassJoinRequestRepository} from "../data/repositories"; +import {Student} from "../entities/users/student.entity"; +import {Class} from "../entities/classes/class.entity"; export interface StudentRequestDTO { requester: StudentDTO; @@ -14,3 +17,11 @@ export function mapToStudentRequestDTO(request: ClassJoinRequest): StudentReques status: request.status, }; } + +export function mapToStudentRequest(student: Student, cls: Class) { + return getClassJoinRequestRepository().create({ + requester: student, + class: cls, + status: ClassJoinRequestStatus.Open, + }); +} diff --git a/backend/src/services/classes.ts b/backend/src/services/classes.ts index 5b1e3cfc..6581cb93 100644 --- a/backend/src/services/classes.ts +++ b/backend/src/services/classes.ts @@ -3,9 +3,22 @@ import { ClassDTO, mapToClassDTO } from '../interfaces/class.js'; import { mapToStudentDTO, StudentDTO } from '../interfaces/student.js'; import { mapToTeacherInvitationDTO, mapToTeacherInvitationDTOIds, TeacherInvitationDTO } from '../interfaces/teacher-invitation.js'; import { getLogger } from '../logging/initalize.js'; +import {NotFoundException} from "../exceptions/not-found-exception"; +import {Class} from "../entities/classes/class.entity"; const logger = getLogger(); +export async function fetchClass(classId: string): Promise { + const classRepository = getClassRepository(); + const cls = await classRepository.findById(classId); + + if (!cls) { + throw new NotFoundException("Class with id not found"); + } + + return cls; +} + export async function getAllClasses(full: boolean): Promise { const classRepository = getClassRepository(); const classes = await classRepository.find({}, { populate: ['students', 'teachers'] }); diff --git a/backend/src/services/students.ts b/backend/src/services/students.ts index e3c1ceba..2b410f46 100644 --- a/backend/src/services/students.ts +++ b/backend/src/services/students.ts @@ -1,4 +1,11 @@ -import { getClassRepository, getGroupRepository, getStudentRepository, getSubmissionRepository } from '../data/repositories.js'; +import { + getClassJoinRequestRepository, + getClassRepository, + getGroupRepository, + getQuestionRepository, + getStudentRepository, + getSubmissionRepository +} from '../data/repositories.js'; import { AssignmentDTO } from '../interfaces/assignment.js'; import { ClassDTO, mapToClassDTO } from '../interfaces/class.js'; import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js'; @@ -6,9 +13,10 @@ import { mapToStudent, mapToStudentDTO, StudentDTO } from '../interfaces/student import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js'; import { getAllAssignments } from './assignments.js'; import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question'; -import {ClassJoinRequestStatus} from "../entities/classes/class-join-request.entity"; -import {ConflictException, NotFoundException} from "../exceptions"; -import {mapToStudentRequestDTO} from "../interfaces/student-request"; +import {mapToStudentRequest, mapToStudentRequestDTO} from "../interfaces/student-request"; +import {Student} from "../entities/users/student.entity"; +import {NotFoundException} from "../exceptions/not-found-exception"; +import {fetchClass} from "./classes"; export async function getAllStudents(full: boolean): Promise { const studentRepository = getStudentRepository(); @@ -98,3 +106,53 @@ export async function getStudentSubmissions(username: string, full: boolean): Pr return submissions.map(mapToSubmissionDTOId); } + +export async function getStudentQuestions(username: string, full: boolean): Promise { + const student = await fetchStudent(username); + + const questionRepository = getQuestionRepository(); + const questions = await questionRepository.findAllByAuthor(student); + + const questionsDTO = questions.map(mapToQuestionDTO); + + if (full) + return questionsDTO; + + return questionsDTO.map(mapToQuestionId); +} + +export async function createClassJoinRequest(studentUsername: string, classId: string) { + const classRepo = getClassRepository(); + const requestRepo = getClassJoinRequestRepository(); + + const student = await fetchStudent(studentUsername); // throws error if student not found + const cls = await fetchClass(classId); + + const request = mapToStudentRequest(student, cls); + await requestRepo.save(request, { preventOverwrite: true }); +} + +export async function getJoinRequestsByStudent(studentUsername: string) { + const requestRepo = getClassJoinRequestRepository(); + + const student = await fetchStudent(studentUsername); + + const requests = await requestRepo.findAllRequestsBy(student); + return requests.map(mapToStudentRequestDTO); +} + +export async function deleteClassJoinRequest(studentUsername: string, classId: string) { + const requestRepo = getClassJoinRequestRepository(); + + const student = await fetchStudent(studentUsername); + const cls = await fetchClass(classId); + + + const request = await requestRepo.findByStudentAndClass(student, cls); + + if (!request) { + throw new NotFoundException('Join request not found'); + } + + await requestRepo.deleteBy(student, cls); +} diff --git a/backend/src/services/teachers.ts b/backend/src/services/teachers.ts index 397c08ac..0acbce3d 100644 --- a/backend/src/services/teachers.ts +++ b/backend/src/services/teachers.ts @@ -6,10 +6,8 @@ import { getTeacherRepository, } from '../data/repositories.js'; import { ClassDTO, mapToClassDTO } from '../interfaces/class.js'; -import { getClassStudents } from './class.js'; import {mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId} from '../interfaces/question.js'; import { mapToTeacher, mapToTeacherDTO, TeacherDTO } from '../interfaces/teacher.js'; -import {ConflictException, NotFoundException} from "../exceptions"; import {Teacher} from "../entities/users/teacher.entity"; import {fetchStudent} from "./students"; import {ClassJoinRequest, ClassJoinRequestStatus} from "../entities/classes/class-join-request.entity"; @@ -24,6 +22,8 @@ import {QuestionRepository} from "../data/questions/question-repository"; import {Question} from "../entities/questions/question.entity"; import {ClassJoinRequestRepository} from "../data/classes/class-join-request-repository"; import {Student} from "../entities/users/student.entity"; +import {NotFoundException} from "../exceptions/not-found-exception"; +import {getClassStudents} from "./classes"; export async function getAllTeachers(full: boolean): Promise { const teacherRepository: TeacherRepository = getTeacherRepository(); diff --git a/backend/tests/controllers/student.test.ts b/backend/tests/controllers/student.test.ts index 4dfa12c1..e360460c 100644 --- a/backend/tests/controllers/student.test.ts +++ b/backend/tests/controllers/student.test.ts @@ -12,11 +12,13 @@ import { getStudentQuestionsHandler, createStudentRequestHandler, getStudentRequestHandler, - updateClassJoinRequestHandler, deleteClassJoinRequestHandler } from '../../src/controllers/students.js'; import {TEST_STUDENTS} from "../test_assets/users/students.testdata"; -import {BadRequestException, ConflictException, NotFoundException} from "../../src/exceptions"; +import {NotFoundException} from "../../src/exceptions/not-found-exception"; +import {BadRequestException} from "../../src/exceptions/bad-request-exception"; +import {ConflictException} from "../../src/exceptions/conflict-exception"; +import {EntityAlreadyExistsException} from "../../src/exceptions/entity-already-exists-exception"; describe('Student controllers', () => { let req: Partial; @@ -57,8 +59,8 @@ describe('Student controllers', () => { it('Create student', async () => { req = { body: { - username: 'coolstudent', - firstName: 'Cool', + username: 'NewstudentId21', + firstName: 'New', lastName: 'Student' } }; @@ -69,7 +71,6 @@ describe('Student controllers', () => { expect(jsonMock).toHaveBeenCalled(); }); - // TODO create duplicate student id it('Create duplicate student', async () => { req = { @@ -82,7 +83,7 @@ describe('Student controllers', () => { await expect(() => createStudentHandler(req as Request, res as Response)) .rejects - .toThrowError(ConflictException); + .toThrowError(EntityAlreadyExistsException); }); it('Create student no body', async () => {