feat(backend): Eigen error handler toegevoegd.

Hiervoor was ook refactoring aan de exception-klassen nodig.
This commit is contained in:
Gerald Schmittinger 2025-03-30 12:25:41 +02:00
parent bc94b25a6a
commit aaa71aa648
14 changed files with 103 additions and 55 deletions

View file

@ -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();

View file

@ -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) {

View file

@ -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.

View file

@ -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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View file

@ -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;

View 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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}