Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backend/src/config/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ export class EntityNotFoundError extends Error {
export class DatabaseEntryNotFoundError extends Error {
message: string;

constructor(message: string) {
super(message);
this.message = message;
}
}

/**
* Error class for cases like failure to create a Class.
*/
export class DatabaseError extends Error {
message: string;

constructor(message: string) {
super(message);
this.message = message;
Expand Down
7 changes: 3 additions & 4 deletions backend/src/config/service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@

export interface ServiceParams {
//TODO can we abstract the fromObject method in services to for a Params as abstracted class or interface?
// fromObject: (repository: any) => Promise<any>;
// toObject: () => object;
}

//TODO if application layer always asks for an object in return to we need to specify ReturnType?
export interface Service<T extends ServiceParams, ReturnType> {
execute: (input: T) => Promise<ReturnType>;
export interface Service<T extends ServiceParams> {
execute: (input: T) => Promise<object>;
}

//TODO can we abstract the params type in services to for a Params as abstracted class or interface?
export type Services = Record<string, any>;
export type Services = Record<string, Service<ServiceParams>>;
8 changes: 8 additions & 0 deletions backend/src/core/repositories/classRepositoryInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export abstract class IClassRepository extends AbstractRepository {
*/
public abstract createClass(newClass: Class): Promise<Class>;

/**
* Updates an existing class.
* @param classId the id of the class to be updated.
* @param updatedClass the params to be updated.
* @returns the new version of the class.
*/
public abstract updateClass(classId: string, updatedClass: Partial<Class>): Promise<Class>;

/**
* Get a class by its id.
* @param id The id of the class.
Expand Down
2 changes: 1 addition & 1 deletion backend/src/core/services/class/baseClassService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IClassRepository } from "../../repositories/classRepositoryInterface";
/**
* Class used for every usecase-implementation. They all need a class repository.
*/
export abstract class ClassBaseService<T extends ServiceParams,P> implements Service<T, object>{
export abstract class ClassBaseService<T extends ServiceParams> implements Service<T>{
constructor(protected classRepository: IClassRepository){}
abstract execute(input: T):Promise<object>;
}
31 changes: 26 additions & 5 deletions backend/src/core/services/class/createClass.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import { Service, ServiceParams } from "../../../config/service";
import { ServiceParams } from "../../../config/service";
import { ClassBaseService } from "./baseClassService";
import { Class } from "../../entities/class";

export class CreateClass implements Service<ServiceParams, object> {
constructor() {}
export class CreateClassParams implements ServiceParams {
constructor(
private _name: string,
private _description: string,
private _targetAudience: string
) {}

async execute(input: ServiceParams): Promise<object> {
return {};
get name(): string {
return this._name;
}

get description(): string {
return this._description;
}

get targetAudience(): string {
return this._targetAudience;
}
}

export class CreateClass extends ClassBaseService<CreateClassParams> {
async execute(input: CreateClassParams): Promise<Class> {
const newClass = new Class(input.name, input.description, input.targetAudience);
return this.classRepository.createClass(newClass);
}
}
19 changes: 15 additions & 4 deletions backend/src/core/services/class/deleteClass.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { Service, ServiceParams } from "../../../config/service";
import { ServiceParams } from "../../../config/service";
import { ClassBaseService } from "./baseClassService";

export class DeleteClass implements Service<ServiceParams, object> {
constructor() {}
export class DeleteClassParams implements ServiceParams {
constructor(
private _id: string //id of the class to delete
){}

async execute(input: ServiceParams): Promise<object> {
public get id(): string {
return this._id;
}
}

export class DeleteClass extends ClassBaseService<DeleteClassParams>{

async execute(input: DeleteClassParams): Promise<object> {
await this.classRepository.deleteClassById(input.id);
return {};
}
}
59 changes: 25 additions & 34 deletions backend/src/core/services/class/getClass.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,53 @@
import { ServiceParams } from "../../../config/service";
import { Class } from "../../entities/class";
import { ClassBaseService } from "./baseClassService";

//TODO - change after refactor
export class GetClassByClassIdParams implements ServiceParams {
classId: string;
className: string;
teacherId: string;
studentId: string;
constructor(classId: string, className: string, teacherId: string, studentId: string) {
this.classId = classId;
this.className = className;
this.teacherId = teacherId;
this.studentId = studentId;
}

getClassId(): string {
return this.classId;
export class GetClassParams implements ServiceParams {
// fields may be null: GetClassParams for GetClassByName only requires name, GetClassByClassId only requires classId...
constructor(private _classId?: string, private _className?: string, private _teacherId?: string, private _studentId?: string) {}

public get classId(): string|undefined {
return this._classId;
}

getClassName(): string {
return this.className;
public get className(): string|undefined {
return this._className;
}

getTeacherId(): string {
return this.teacherId;
public get teacherId(): string|undefined {
return this._teacherId;
}

getStudentId(): string {
return this.studentId;
public get studentId(): string|undefined {
return this._studentId;
}
}

export class GetClassByClassId extends ClassBaseService<GetClassByClassIdParams, object> {
export class GetClassByClassId extends ClassBaseService<GetClassParams> {
/**
* Gets a class from the DB given its ID.
* @param input ID of the class to get from the DB.
* @returns the class with the given id.
* @throws {EntityNotFoundError} if the class could not be found.
*/
execute(input: GetClassByClassIdParams): Promise<object> {
return this.classRepository.getClassById(input.getClassId());
execute(input: GetClassParams): Promise<object> {
return this.classRepository.getClassById(input.classId!);
}
}

export class GetClassByName extends ClassBaseService<GetClassByClassIdParams, object> {
export class GetClassByName extends ClassBaseService<GetClassParams> {
/**
* Gets a class from the DB given its name.
* @param input name of the class to get.
* @returns the class with the given name.
* @throws {EntityNotFoundError} if the class could not be found.
*/
async execute(input: GetClassByClassIdParams): Promise<object> {
return this.classRepository.getClassByName(input.getClassName());
async execute(input: GetClassParams): Promise<object> {
return this.classRepository.getClassByName(input.className!);
}
}

export class GetAllClasses extends ClassBaseService<GetClassByClassIdParams, object>{
export class GetAllClasses extends ClassBaseService<GetClassParams>{
/**
* Get all classes,
* @returns every class stored inside the database.
Expand All @@ -67,26 +58,26 @@ export class GetAllClasses extends ClassBaseService<GetClassByClassIdParams, obj
}
}

export class GetClassesByTeacherId extends ClassBaseService<GetClassByClassIdParams, object>{
export class GetClassesByTeacherId extends ClassBaseService<GetClassParams>{
/**
* Get all classes for a teacher.
* @param input the id of the teacher.
* @returns every class for a teacher.
* @throws {EntityNotFoundError} if the teacher could not be found.
*/
async execute(input: GetClassByClassIdParams): Promise<object> {
return this.classRepository.getAllClassesByTeacherId(input.getTeacherId());
async execute(input: GetClassParams): Promise<object> {
return this.classRepository.getAllClassesByTeacherId(input.teacherId!);
}
}

export class GetClassesByStudentId extends ClassBaseService<GetClassByClassIdParams, object>{
export class GetClassesByStudentId extends ClassBaseService<GetClassParams>{
/**
* Get all classes for a student.
* @param input the id of the student.
* @returns every class where a student is part of.
* @throws {EntityNotFoundError} if the student could not be found.
*/
async execute(input: GetClassByClassIdParams): Promise<object> {
return this.classRepository.getAllClassesByStudentId(input.getStudentId());
async execute(input: GetClassParams): Promise<object> {
return this.classRepository.getAllClassesByStudentId(input.studentId!);
}
}
9 changes: 0 additions & 9 deletions backend/src/core/services/class/getUserClasses.ts

This file was deleted.

1 change: 0 additions & 1 deletion backend/src/core/services/class/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './createClass';
export * from './deleteClass';
export * from './getClass';
export * from './getUserClasses';
export * from './updateClass';
9 changes: 0 additions & 9 deletions backend/src/core/services/class/inviteStudentToClass.ts

This file was deleted.

9 changes: 0 additions & 9 deletions backend/src/core/services/class/inviteTeacherToClass.ts

This file was deleted.

10 changes: 0 additions & 10 deletions backend/src/core/services/class/setupClass.ts

This file was deleted.

41 changes: 35 additions & 6 deletions backend/src/core/services/class/updateClass.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
import { Service, ServiceParams } from "../../../config/service";
import { ServiceParams } from "../../../config/service";
import { ClassBaseService } from "./baseClassService";
import { Class } from "../../entities/class";

export class UpdateClass implements Service<ServiceParams, object> {
constructor() {}
export class UpdateClassParams implements ServiceParams {
constructor(
private _id: string,
private _name?: string,
private _description?: string,
private _targetAudience?: string
) { }

async execute(input: ServiceParams): Promise<object> {
return {};
public get id(): string {
return this._id;
}
}
public get name(): string | undefined {
return this._name;
}
public get description(): string | undefined {
return this._description;
}
public get targetAudience(): string | undefined {
return this._targetAudience;
}
}

export class UpdateClass extends ClassBaseService<UpdateClassParams> {
async execute(input: UpdateClassParams): Promise<Class> {

// Object met alleen de velden die worden bijgewerkt
const updatedFields: Partial<Class> = {};
if (input.name) updatedFields.name = input.name;
if (input.description) updatedFields.description = input.description;
if (input.targetAudience) updatedFields.targetAudience = input.targetAudience;

return this.classRepository.updateClass(input.id, updatedFields);
}
}
38 changes: 38 additions & 0 deletions backend/test/core/services/class/createClass.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { DatabaseError } from '../../../../src/config/error';
import { Class } from '../../../../src/core/entities/class';
import { CreateClass, CreateClassParams } from '../../../../src/core/services/class/createClass';

// Mock repository
const mockClassRepository = {
createClass: jest.fn(),
};

describe('CreateClass', () => {
let createClass: CreateClass;

beforeEach(() => {
createClass = new CreateClass(mockClassRepository as any);
jest.clearAllMocks(); // Reset mocks voor elke test
});

test('Should create a class and return it with an ID', async () => {
const inputClass = new CreateClassParams("Math 101", "Basic math class", "Primary School");
const createdClass = new Class("Math 101", "Basic math class", "Primary School", "mock-class-id");

mockClassRepository.createClass.mockResolvedValue(createdClass);

const result = await createClass.execute(inputClass);

expect(result).toEqual(createdClass);
expect(mockClassRepository.createClass).toHaveBeenCalledWith(inputClass);
});

test('Should throw a DatabaseError if creation fails', async () => {
const inputClass = new CreateClassParams("Math 101", "Basic math class", "Primary School");

mockClassRepository.createClass.mockRejectedValue(new DatabaseError('Creation failed'));

await expect(createClass.execute(inputClass)).rejects.toThrow(DatabaseError);
expect(mockClassRepository.createClass).toHaveBeenCalledWith(inputClass);
});
});
Loading