feat(backend): Eigen error handler toegevoegd.
Hiervoor was ook refactoring aan de exception-klassen nodig.
This commit is contained in:
		
							parent
							
								
									bc94b25a6a
								
							
						
					
					
						commit
						aaa71aa648
					
				
					 14 changed files with 103 additions and 55 deletions
				
			
		|  | @ -9,6 +9,7 @@ import { EnvVars, getNumericEnvVar } from './util/envvars.js'; | |||
| import apiRouter from './routes/router.js'; | ||||
| import swaggerMiddleware from './swagger.js'; | ||||
| import swaggerUi from 'swagger-ui-express'; | ||||
| import {errorHandler} from "./middleware/error-handling/error-handler"; | ||||
| 
 | ||||
| const logger: Logger = getLogger(); | ||||
| 
 | ||||
|  | @ -26,6 +27,8 @@ app.use('/api', apiRouter); | |||
| // Swagger
 | ||||
| app.use('/api-docs', swaggerUi.serve, swaggerMiddleware); | ||||
| 
 | ||||
| app.use(errorHandler); | ||||
| 
 | ||||
| async function startServer() { | ||||
|     await initORM(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,9 +4,9 @@ import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifie | |||
| import learningObjectService from '../services/learning-objects/learning-object-service.js'; | ||||
| import { EnvVars, getEnvVar } from '../util/envvars.js'; | ||||
| import { Language } from '../entities/content/language.js'; | ||||
| import { BadRequestException } from '../exceptions.js'; | ||||
| import attachmentService from '../services/learning-objects/attachment-service.js'; | ||||
| import { NotFoundError } from '@mikro-orm/core'; | ||||
| import {BadRequestException} from "../exceptions/badRequestException"; | ||||
| 
 | ||||
| function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIdentifier { | ||||
|     if (!req.params.hruid) { | ||||
|  |  | |||
|  | @ -2,13 +2,14 @@ import { Request, Response } from 'express'; | |||
| import { themes } from '../data/themes.js'; | ||||
| import { FALLBACK_LANG } from '../config.js'; | ||||
| import learningPathService from '../services/learning-paths/learning-path-service.js'; | ||||
| import { BadRequestException, NotFoundException } from '../exceptions.js'; | ||||
| import { Language } from '../entities/content/language.js'; | ||||
| import { | ||||
|     PersonalizationTarget, | ||||
|     personalizedForGroup, | ||||
|     personalizedForStudent, | ||||
| } from '../services/learning-paths/learning-path-personalization-util.js'; | ||||
| import {BadRequestException} from "../exceptions/badRequestException"; | ||||
| import {NotFoundException} from "../exceptions/not-found-exception"; | ||||
| 
 | ||||
| /** | ||||
|  * Fetch learning paths based on query parameters. | ||||
|  |  | |||
|  | @ -1,42 +0,0 @@ | |||
| /** | ||||
|  * Exception for HTTP 400 Bad Request | ||||
|  */ | ||||
| export class BadRequestException extends Error { | ||||
|     public status = 400; | ||||
| 
 | ||||
|     constructor(error: string) { | ||||
|         super(error); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 401 Unauthorized | ||||
|  */ | ||||
| export class UnauthorizedException extends Error { | ||||
|     status = 401; | ||||
|     constructor(message: string = 'Unauthorized') { | ||||
|         super(message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 403 Forbidden | ||||
|  */ | ||||
| export class ForbiddenException extends Error { | ||||
|     status = 403; | ||||
| 
 | ||||
|     constructor(message: string = 'Forbidden') { | ||||
|         super(message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 404 Not Found | ||||
|  */ | ||||
| export class NotFoundException extends Error { | ||||
|     public status = 404; | ||||
| 
 | ||||
|     constructor(error: string) { | ||||
|         super(error); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								backend/src/exceptions/bad-request-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								backend/src/exceptions/bad-request-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| import {ExceptionWithHttpState} from "./exception-with-http-state.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 400 Bad Request | ||||
|  */ | ||||
| export abstract class BadRequestException extends ExceptionWithHttpState { | ||||
|     constructor(error: string) { | ||||
|         super(400, error); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								backend/src/exceptions/conflict-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								backend/src/exceptions/conflict-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import {ExceptionWithHttpState} from "./exception-with-http-state.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 409 Conflict | ||||
|  */ | ||||
| export class ConflictException extends ExceptionWithHttpState { | ||||
|     public status = 409; | ||||
| 
 | ||||
|     constructor(error: string) { | ||||
|         super(409, error); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								backend/src/exceptions/exception-with-http-state.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								backend/src/exceptions/exception-with-http-state.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| /** | ||||
|  * Exceptions which are associated with a HTTP error code. | ||||
|  */ | ||||
| export abstract class ExceptionWithHttpState extends Error { | ||||
|     constructor(public status: number, public error: string) { | ||||
|         super(error); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										12
									
								
								backend/src/exceptions/forbidden-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								backend/src/exceptions/forbidden-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import {ExceptionWithHttpState} from "./exception-with-http-state.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 403 Forbidden | ||||
|  */ | ||||
| export class ForbiddenException extends ExceptionWithHttpState { | ||||
|     status = 403; | ||||
| 
 | ||||
|     constructor(message: string = 'Forbidden') { | ||||
|         super(403, message); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								backend/src/exceptions/not-found-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								backend/src/exceptions/not-found-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import {ExceptionWithHttpState} from "./exception-with-http-state.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 404 Not Found | ||||
|  */ | ||||
| export class NotFoundException extends ExceptionWithHttpState { | ||||
|     public status = 404; | ||||
| 
 | ||||
|     constructor(error: string) { | ||||
|         super(404, error); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								backend/src/exceptions/unauthorized-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								backend/src/exceptions/unauthorized-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| import {ExceptionWithHttpState} from "./exception-with-http-state.js"; | ||||
| 
 | ||||
| /** | ||||
|  * Exception for HTTP 401 Unauthorized | ||||
|  */ | ||||
| export class UnauthorizedException extends ExceptionWithHttpState { | ||||
|     constructor(message: string = 'Unauthorized') { | ||||
|         super(401, message); | ||||
|     } | ||||
| } | ||||
|  | @ -6,7 +6,8 @@ import * as express from 'express'; | |||
| import * as jwt from 'jsonwebtoken'; | ||||
| import { AuthenticatedRequest } from './authenticated-request.js'; | ||||
| import { AuthenticationInfo } from './authentication-info.js'; | ||||
| import { ForbiddenException, UnauthorizedException } from '../../exceptions.js'; | ||||
| import {UnauthorizedException} from "../../exceptions/unauthorized-exception"; | ||||
| import {ForbiddenException} from "../../exceptions/forbidden-exception"; | ||||
| 
 | ||||
| const JWKS_CACHE = true; | ||||
| const JWKS_RATE_LIMIT = true; | ||||
|  |  | |||
							
								
								
									
										15
									
								
								backend/src/middleware/error-handling/error-handler.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								backend/src/middleware/error-handling/error-handler.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| import {NextFunction, Request, Response} from "express"; | ||||
| import {getLogger, Logger} from "../../logging/initalize"; | ||||
| import {ExceptionWithHttpState} from "../../exceptions/exception-with-http-state"; | ||||
| 
 | ||||
| const logger: Logger = getLogger(); | ||||
| 
 | ||||
| export function errorHandler(err: unknown, req: Request, res: Response, _: NextFunction): void { | ||||
|     if (err instanceof ExceptionWithHttpState) { | ||||
|         logger.warn(`An error occurred while handling request ${JSON.stringify(req)}: ${err.error} (-> HTTP ${err.status})`); | ||||
|         res.status(err.status).json(err); | ||||
|     } else { | ||||
|         logger.error(`Unexpected error occurred while handling request ${JSON.stringify(req)}: ${JSON.stringify(err)}`); | ||||
|         res.status(500).json(err); | ||||
|     } | ||||
| } | ||||
|  | @ -5,6 +5,9 @@ import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js | |||
| import { mapToStudent, mapToStudentDTO, StudentDTO } from '../interfaces/student.js'; | ||||
| import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js'; | ||||
| import { getAllAssignments } from './assignments.js'; | ||||
| import {UniqueConstraintViolationException} from "@mikro-orm/core"; | ||||
| 
 | ||||
| import {ConflictException} from "../exceptions/conflict-exception"; | ||||
| 
 | ||||
| export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> { | ||||
|     const studentRepository = getStudentRepository(); | ||||
|  | @ -29,11 +32,12 @@ export async function createStudent(userData: StudentDTO): Promise<StudentDTO | | |||
|     try { | ||||
|         const newStudent = mapToStudent(userData); | ||||
|         await studentRepository.save(newStudent); | ||||
| 
 | ||||
|         return mapToStudentDTO(newStudent); | ||||
|     } catch (e) { | ||||
|         console.log(e); | ||||
|         return null; | ||||
|     } catch (e: unknown) { | ||||
|         if (e instanceof UniqueConstraintViolationException) { | ||||
|             throw new ConflictException(`There is already a user with username '${userData.username}'.`); | ||||
|         } | ||||
|         throw e; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,17 +2,16 @@ import { | |||
|     getClassRepository, | ||||
|     getLearningObjectRepository, | ||||
|     getQuestionRepository, | ||||
|     getStudentRepository, | ||||
|     getTeacherRepository, | ||||
| } from '../data/repositories.js'; | ||||
| import { Teacher } from '../entities/users/teacher.entity.js'; | ||||
| import { ClassDTO, mapToClassDTO } from '../interfaces/class.js'; | ||||
| import { getClassStudents } from './classes.js'; | ||||
| import { StudentDTO } from '../interfaces/student.js'; | ||||
| import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question.js'; | ||||
| import { mapToUser } from '../interfaces/user.js'; | ||||
| import { mapToTeacher, mapToTeacherDTO, TeacherDTO } from '../interfaces/teacher.js'; | ||||
| import { teachersOnly } from '../middleware/auth/auth.js'; | ||||
| import {UniqueConstraintViolationException} from "@mikro-orm/core"; | ||||
| 
 | ||||
| import {ConflictException} from "../exceptions/conflict-exception"; | ||||
| 
 | ||||
| export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
|  | @ -40,8 +39,10 @@ export async function createTeacher(userData: TeacherDTO): Promise<TeacherDTO | | |||
| 
 | ||||
|         return mapToTeacherDTO(newTeacher); | ||||
|     } catch (e) { | ||||
|         console.log(e); | ||||
|         return null; | ||||
|         if (e instanceof UniqueConstraintViolationException) { | ||||
|             throw new ConflictException(`There is already a user with username '${userData.username}'.`); | ||||
|         } | ||||
|         throw e; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger