feat: teacher invitation backend

This commit is contained in:
Gabriellvl 2025-04-12 17:55:39 +02:00
parent a91e4b2a73
commit 311e76149c
7 changed files with 151 additions and 5 deletions

View file

@ -0,0 +1,38 @@
import { Request, Response } from 'express';
import {requireFields} from "./error-helper";
import {createInvitation, deleteInvitationFor, getAllInvitations} from "../services/teacher-invitations";
import {TeacherInvitationData} from "@dwengo-1/common/interfaces/teacher-invitation";
export async function getAllInvitationsHandler(req: Request, res: Response): Promise<void> {
const username = req.params.username;
const by = req.query.by === 'true';
requireFields({ username });
const invitations = getAllInvitations(username, by);
res.json({ invitations });
}
export async function createInvitationHandler(req: Request, res: Response): Promise<void> {
const sender = req.body.sender;
const receiver = req.body.receiver;
const classId = req.body.class;
requireFields({ sender, receiver, classId });
const data = req.body as TeacherInvitationData;
const invitation = await createInvitation(data);
res.json({ invitation });
}
export async function deleteInvitationForHandler(req: Request, res: Response): Promise<void> {
const sender = req.params.sender;
const receiver = req.params.receiver;
const classId = req.params.class;
const accepted = req.body.accepted !== 'false';
requireFields({ sender, receiver, classId });
const invitation = deleteInvitationFor(sender, receiver, classId, accepted);
res.json({ invitation });
}

View file

@ -20,4 +20,11 @@ export class TeacherInvitationRepository extends DwengoEntityRepository<TeacherI
class: clazz,
});
}
public async findBy(clazz: Class, sender: Teacher, receiver: Teacher): Promise<TeacherInvitation | null> {
return this.findOne({
sender: sender,
receiver: receiver,
class: clazz,
})
}
}

View file

@ -2,6 +2,9 @@ import { TeacherInvitation } from '../entities/classes/teacher-invitation.entity
import { mapToClassDTO } from './class.js';
import { mapToUserDTO } from './user.js';
import { TeacherInvitationDTO } from '@dwengo-1/common/interfaces/teacher-invitation';
import {getTeacherInvitationRepository} from "../data/repositories";
import {Teacher} from "../entities/users/teacher.entity";
import {Class} from "../entities/classes/class.entity";
export function mapToTeacherInvitationDTO(invitation: TeacherInvitation): TeacherInvitationDTO {
return {
@ -18,3 +21,9 @@ export function mapToTeacherInvitationDTOIds(invitation: TeacherInvitation): Tea
class: invitation.class.classId!,
};
}
export function mapToInvitation(sender: Teacher, receiver: Teacher, cls: Class): TeacherInvitation {
return getTeacherInvitationRepository().create({
sender, receiver, class: cls
});
}

View file

@ -0,0 +1,17 @@
import express from "express";
import {
createInvitationHandler,
deleteInvitationForHandler,
getAllInvitationsHandler
} from "../controllers/teacher-invitations";
const router = express.Router({ mergeParams: true });
router.get('/:username', getAllInvitationsHandler);
router.post('/', createInvitationHandler);
router.delete('/:sender/:receiver/:classId', deleteInvitationForHandler);
export default router;

View file

@ -10,6 +10,8 @@ import {
getTeacherStudentHandler,
updateStudentJoinRequestHandler,
} from '../controllers/teachers.js';
import invitationRouter from './teacher-invitations.js';
const router = express.Router();
// Root endpoint used to search objects
@ -32,10 +34,6 @@ router.get('/:username/joinRequests/:classId', getStudentJoinRequestHandler);
router.put('/:username/joinRequests/:classId/:studentUsername', updateStudentJoinRequestHandler);
// Invitations to other classes a teacher received
router.get('/:id/invitations', (_req, res) => {
res.json({
invitations: ['0'],
});
});
router.get('/invitations', invitationRouter);
export default router;

View file

@ -0,0 +1,71 @@
import {fetchTeacher} from "./teachers";
import {getTeacherInvitationRepository} from "../data/repositories";
import {mapToInvitation, mapToTeacherInvitationDTO} from "../interfaces/teacher-invitation";
import {addClassTeacher, fetchClass} from "./classes";
import {TeacherInvitationData, TeacherInvitationDTO} from "@dwengo-1/common/interfaces/teacher-invitation";
import {ConflictException} from "../exceptions/conflict-exception";
import {Teacher} from "../entities/users/teacher.entity";
import {Class} from "../entities/classes/class.entity";
import {NotFoundException} from "../exceptions/not-found-exception";
import {TeacherInvitation} from "../entities/classes/teacher-invitation.entity";
export async function getAllInvitations(username: string, by: boolean): Promise<TeacherInvitationDTO[]> {
const teacher = await fetchTeacher(username);
const teacherInvitationRepository = getTeacherInvitationRepository();
let invitations;
if (by) {
invitations = await teacherInvitationRepository.findAllInvitationsBy(teacher);
} else {
invitations = await teacherInvitationRepository.findAllInvitationsFor(teacher);
}
return invitations.map(mapToTeacherInvitationDTO);
}
export async function createInvitation(data: TeacherInvitationData): Promise<TeacherInvitationDTO> {
const teacherInvitationRepository = getTeacherInvitationRepository();
const sender = await fetchTeacher(data.sender);
const receiver = await fetchTeacher(data.receiver);
const cls = await fetchClass(data.class);
if (!cls.teachers.contains(sender)){
throw new ConflictException("The teacher sending the invite is not part of the class");
}
const newInvitation = mapToInvitation(sender, receiver, cls);
await teacherInvitationRepository.save(newInvitation, {preventOverwrite: true});
return mapToTeacherInvitationDTO(newInvitation);
}
async function fetchInvitation(sender: Teacher, receiver: Teacher, cls: Class): Promise<TeacherInvitation> {
const teacherInvitationRepository = getTeacherInvitationRepository();
const invite = await teacherInvitationRepository.findBy(cls, sender, receiver);
if (!invite){
throw new NotFoundException("Teacher invite not found");
}
return invite;
}
export async function deleteInvitationFor(usernameSender: string, usernameReceiver: string, classId: string, accepted: boolean) {
const teacherInvitationRepository = getTeacherInvitationRepository();
const sender = await fetchTeacher(usernameSender);
const receiver = await fetchTeacher(usernameReceiver);
const cls = await fetchClass(classId);
const invitation = await fetchInvitation(sender, receiver, cls);
await teacherInvitationRepository.deleteBy(cls, sender, receiver);
if (accepted){
await addClassTeacher(classId, usernameReceiver);
}
return mapToTeacherInvitationDTO(invitation);
}

View file

@ -6,3 +6,9 @@ export interface TeacherInvitationDTO {
receiver: string | UserDTO;
class: string | ClassDTO;
}
export interface TeacherInvitationData {
sender: string;
receiver: string;
class: string;
}