feat: teacher invitation backend
This commit is contained in:
parent
a91e4b2a73
commit
311e76149c
7 changed files with 151 additions and 5 deletions
38
backend/src/controllers/teacher-invitations.ts
Normal file
38
backend/src/controllers/teacher-invitations.ts
Normal 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 });
|
||||||
|
}
|
|
@ -20,4 +20,11 @@ export class TeacherInvitationRepository extends DwengoEntityRepository<TeacherI
|
||||||
class: clazz,
|
class: clazz,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public async findBy(clazz: Class, sender: Teacher, receiver: Teacher): Promise<TeacherInvitation | null> {
|
||||||
|
return this.findOne({
|
||||||
|
sender: sender,
|
||||||
|
receiver: receiver,
|
||||||
|
class: clazz,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ import { TeacherInvitation } from '../entities/classes/teacher-invitation.entity
|
||||||
import { mapToClassDTO } from './class.js';
|
import { mapToClassDTO } from './class.js';
|
||||||
import { mapToUserDTO } from './user.js';
|
import { mapToUserDTO } from './user.js';
|
||||||
import { TeacherInvitationDTO } from '@dwengo-1/common/interfaces/teacher-invitation';
|
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 {
|
export function mapToTeacherInvitationDTO(invitation: TeacherInvitation): TeacherInvitationDTO {
|
||||||
return {
|
return {
|
||||||
|
@ -18,3 +21,9 @@ export function mapToTeacherInvitationDTOIds(invitation: TeacherInvitation): Tea
|
||||||
class: invitation.class.classId!,
|
class: invitation.class.classId!,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mapToInvitation(sender: Teacher, receiver: Teacher, cls: Class): TeacherInvitation {
|
||||||
|
return getTeacherInvitationRepository().create({
|
||||||
|
sender, receiver, class: cls
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
17
backend/src/routes/teacher-invitations.ts
Normal file
17
backend/src/routes/teacher-invitations.ts
Normal 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;
|
|
@ -10,6 +10,8 @@ import {
|
||||||
getTeacherStudentHandler,
|
getTeacherStudentHandler,
|
||||||
updateStudentJoinRequestHandler,
|
updateStudentJoinRequestHandler,
|
||||||
} from '../controllers/teachers.js';
|
} from '../controllers/teachers.js';
|
||||||
|
import invitationRouter from './teacher-invitations.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Root endpoint used to search objects
|
// Root endpoint used to search objects
|
||||||
|
@ -32,10 +34,6 @@ router.get('/:username/joinRequests/:classId', getStudentJoinRequestHandler);
|
||||||
router.put('/:username/joinRequests/:classId/:studentUsername', updateStudentJoinRequestHandler);
|
router.put('/:username/joinRequests/:classId/:studentUsername', updateStudentJoinRequestHandler);
|
||||||
|
|
||||||
// Invitations to other classes a teacher received
|
// Invitations to other classes a teacher received
|
||||||
router.get('/:id/invitations', (_req, res) => {
|
router.get('/invitations', invitationRouter);
|
||||||
res.json({
|
|
||||||
invitations: ['0'],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
71
backend/src/services/teacher-invitations.ts
Normal file
71
backend/src/services/teacher-invitations.ts
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,9 @@ export interface TeacherInvitationDTO {
|
||||||
receiver: string | UserDTO;
|
receiver: string | UserDTO;
|
||||||
class: string | ClassDTO;
|
class: string | ClassDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TeacherInvitationData {
|
||||||
|
sender: string;
|
||||||
|
receiver: string;
|
||||||
|
class: string;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue