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, | ||||
|         }); | ||||
|     } | ||||
|     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 { 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 | ||||
|     }); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										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, | ||||
|     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; | ||||
|  |  | |||
							
								
								
									
										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); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
		Reference in a new issue
	
	 Gabriellvl
						Gabriellvl