diff --git a/README.md b/README.md
index db5b63a4..fcc4d3ba 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Projectopgave
-- Projectleider: Fransisco Van Langenhove (@Gabriellvl)
+- Projectleider: Fransisco Gabriel Van Langenhove (@Gabriellvl)
- Technische lead: Tibo De Peuter (@tdpeuter)
- Systeembeheerder: Timo De Meyst (@kloep1)
- Customer relations officer: Adriaan Jacquet (@WhisperinCheetah)
@@ -21,17 +21,28 @@ en lessen kunnen samenstellen hun leerlingen en hun vooruitgang kunnen opvolgen.
## Installatie
+Om de applicatie in te stellen voor een productieomgeving, volg de [installatiehandleiding](https://github.com/SELab-2/Dwengo-1/wiki/Administrator:-Productie-omgeving).
+
+Alternatief kan je één van de volgende methodes gebruiken om de applicatie lokaal te draaien.
+
### Quick start
1. Installeer Docker en Docker Compose op je systeem (zie [Docker](https://docs.docker.com/get-docker/) en [Docker Compose](https://docs.docker.com/compose/)).
2. Clone deze repository.
-3. Voer `docker compose up` uit in de root van de repository.
+3. In de backend, kopieer `.env.example` (of `.env.development.example`) naar `.env` en pas de variabelen aan waar nodig.
+4. Voer `docker compose up` uit in de root van de repository.
+5. Optioneel: Configureer de applicatie aan de hand van de [configuratiehandleiding](https://github.com/SELab-2/Dwengo-1/wiki/Administrator:-Productie-omgeving#dwengo-1-configuratie).
```bash
docker compose version
git clone https://github.com/SELab-2/Dwengo-1.git
-cd Dwengo-1
+cd Dwengo-1/backend
+cp .env.example .env
+# Pas .env aan
+nano .env
+cd ..
docker compose up
+# Configureer de applicatie
```
### Handmatige installatie
@@ -46,8 +57,9 @@ De tech-stack bestaat uit:
- **Frontend**: TypeScript + Vue.js + Vuetify
- **Backend**: TypeScript + Node.js + Express.js + TypeORM + PostgreSQL
+- **Identity provider**: Keycloak
-Voor meer informatie over de keuze van deze tech-stack, zie [designkeuzes](https://github.com/SELab-2/Dwengo-1/wiki/Design-keuzes).
+Voor meer informatie over de keuze van deze tech-stack, zie [designkeuzes](https://github.com/SELab-2/Dwengo-1/wiki/Developer:-Design-keuzes).
## Bijdragen aan Dwengo-1
diff --git a/assets/img/keycloak.png b/assets/img/keycloak.png
new file mode 100644
index 00000000..6a79a7a2
Binary files /dev/null and b/assets/img/keycloak.png differ
diff --git a/backend/README.md b/backend/README.md
index 76bc8eae..4b2083b6 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -20,3 +20,10 @@ npm run dev
npm run build
npm run start
```
+
+## Keycloak configuratie
+
+Tijdens development is het voldoende om gebruik te maken van de keycloak configuratie die automatisch ingeladen wordt.
+
+Voor productie is het ten sterkste aangeraden om keycloak manueel te configureren.
+Voor meer informatie, zie de [administrator-handleiding](https://github.com/SELab-2/Dwengo-1/wiki/Administrator:-Productie-omgeving#installatie-en-server-configuratie).
diff --git a/backend/config.ts b/backend/config.ts
deleted file mode 100644
index 8fd8ec3f..00000000
--- a/backend/config.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// Can be placed in dotenv but found it redundant
-
-// Import dotenv from "dotenv";
-
-// Load .env file
-// Dotenv.config();
-
-export const DWENGO_API_BASE = 'https://dwengo.org/backend/api';
-
-export const FALLBACK_LANG = 'nl';
diff --git a/backend/src/config.ts b/backend/src/config.ts
index b972a1bd..ad8cf0be 100644
--- a/backend/src/config.ts
+++ b/backend/src/config.ts
@@ -6,7 +6,5 @@ export const DWENGO_API_BASE: string = 'https://dwengo.org/backend/api';
// Logging
-export const LOG_LEVEL: string =
- 'development' === process.env.NODE_ENV ? 'debug' : 'info';
-export const LOKI_HOST: string =
- process.env.LOKI_HOST || 'http://localhost:3102';
+export const LOG_LEVEL: string = 'development' === process.env.NODE_ENV ? 'debug' : 'info';
+export const LOKI_HOST: string = process.env.LOKI_HOST || 'http://localhost:3102';
diff --git a/backend/src/controllers/learningObjects.ts b/backend/src/controllers/learningObjects.ts
index 6fde1208..0d41044b 100644
--- a/backend/src/controllers/learningObjects.ts
+++ b/backend/src/controllers/learningObjects.ts
@@ -1,17 +1,10 @@
import { Request, Response } from 'express';
-import {
- getLearningObjectById,
- getLearningObjectIdsFromPath,
- getLearningObjectsFromPath,
-} from '../services/learningObjects.js';
+import { getLearningObjectById, getLearningObjectIdsFromPath, getLearningObjectsFromPath } from '../services/learningObjects.js';
import { FALLBACK_LANG } from '../config.js';
import { FilteredLearningObject } from '../interfaces/learningPath.js';
import { getLogger } from '../logging/initalize.js';
-export async function getAllLearningObjects(
- req: Request,
- res: Response
-): Promise {
+export async function getAllLearningObjects(req: Request, res: Response): Promise {
try {
const hruid = req.query.hruid as string;
const full = req.query.full === 'true';
@@ -26,10 +19,7 @@ export async function getAllLearningObjects(
if (full) {
learningObjects = await getLearningObjectsFromPath(hruid, language);
} else {
- learningObjects = await getLearningObjectIdsFromPath(
- hruid,
- language
- );
+ learningObjects = await getLearningObjectIdsFromPath(hruid, language);
}
res.json(learningObjects);
@@ -39,10 +29,7 @@ export async function getAllLearningObjects(
}
}
-export async function getLearningObject(
- req: Request,
- res: Response
-): Promise {
+export async function getLearningObject(req: Request, res: Response): Promise {
try {
const { hruid } = req.params;
const language = (req.query.language as string) || FALLBACK_LANG;
diff --git a/backend/src/controllers/learningPaths.ts b/backend/src/controllers/learningPaths.ts
index 247877e7..50299d0f 100644
--- a/backend/src/controllers/learningPaths.ts
+++ b/backend/src/controllers/learningPaths.ts
@@ -1,18 +1,12 @@
import { Request, Response } from 'express';
import { themes } from '../data/themes.js';
import { FALLBACK_LANG } from '../config.js';
-import {
- fetchLearningPaths,
- searchLearningPaths,
-} from '../services/learningPaths.js';
+import { fetchLearningPaths, searchLearningPaths } from '../services/learningPaths.js';
import { getLogger } from '../logging/initalize.js';
/**
* Fetch learning paths based on query parameters.
*/
-export async function getLearningPaths(
- req: Request,
- res: Response
-): Promise {
+export async function getLearningPaths(req: Request, res: Response): Promise {
try {
const hruids = req.query.hruid;
const themeKey = req.query.theme as string;
@@ -22,13 +16,9 @@ export async function getLearningPaths(
let hruidList;
if (hruids) {
- hruidList = Array.isArray(hruids)
- ? hruids.map(String)
- : [String(hruids)];
+ hruidList = Array.isArray(hruids) ? hruids.map(String) : [String(hruids)];
} else if (themeKey) {
- const theme = themes.find((t) => {
- return t.title === themeKey;
- });
+ const theme = themes.find((t) => t.title === themeKey);
if (theme) {
hruidList = theme.hruids;
} else {
@@ -38,29 +28,17 @@ export async function getLearningPaths(
return;
}
} else if (searchQuery) {
- const searchResults = await searchLearningPaths(
- searchQuery,
- language
- );
+ const searchResults = await searchLearningPaths(searchQuery, language);
res.json(searchResults);
return;
} else {
- hruidList = themes.flatMap((theme) => {
- return theme.hruids;
- });
+ hruidList = themes.flatMap((theme) => theme.hruids);
}
- const learningPaths = await fetchLearningPaths(
- hruidList,
- language,
- `HRUIDs: ${hruidList.join(', ')}`
- );
+ const learningPaths = await fetchLearningPaths(hruidList, language, `HRUIDs: ${hruidList.join(', ')}`);
res.json(learningPaths.data);
} catch (error) {
- getLogger().error(
- '❌ Unexpected error fetching learning paths:',
- error
- );
+ getLogger().error('❌ Unexpected error fetching learning paths:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
diff --git a/backend/src/controllers/themes.ts b/backend/src/controllers/themes.ts
index a85cac21..fe1eb818 100644
--- a/backend/src/controllers/themes.ts
+++ b/backend/src/controllers/themes.ts
@@ -11,24 +11,19 @@ interface Translations {
export function getThemes(req: Request, res: Response) {
const language = (req.query.language as string)?.toLowerCase() || 'nl';
const translations = loadTranslations(language);
- const themeList = themes.map((theme) => {
- return {
- key: theme.title,
- title:
- translations.curricula_page[theme.title]?.title || theme.title,
- description: translations.curricula_page[theme.title]?.description,
- image: `https://dwengo.org/images/curricula/logo_${theme.title}.png`,
- };
- });
+ const themeList = themes.map((theme) => ({
+ key: theme.title,
+ title: translations.curricula_page[theme.title]?.title || theme.title,
+ description: translations.curricula_page[theme.title]?.description,
+ image: `https://dwengo.org/images/curricula/logo_${theme.title}.png`,
+ }));
res.json(themeList);
}
export function getThemeByTitle(req: Request, res: Response) {
const themeKey = req.params.theme;
- const theme = themes.find((t) => {
- return t.title === themeKey;
- });
+ const theme = themes.find((t) => t.title === themeKey);
if (theme) {
res.json(theme.hruids);
diff --git a/backend/src/data/assignments/assignment-repository.ts b/backend/src/data/assignments/assignment-repository.ts
index c55bee00..c3c457d3 100644
--- a/backend/src/data/assignments/assignment-repository.ts
+++ b/backend/src/data/assignments/assignment-repository.ts
@@ -3,10 +3,7 @@ import { Assignment } from '../../entities/assignments/assignment.entity.js';
import { Class } from '../../entities/classes/class.entity.js';
export class AssignmentRepository extends DwengoEntityRepository {
- public findByClassAndId(
- within: Class,
- id: number
- ): Promise {
+ public findByClassAndId(within: Class, id: number): Promise {
return this.findOne({ within: within, id: id });
}
public findAllAssignmentsInClass(within: Class): Promise {
diff --git a/backend/src/data/assignments/group-repository.ts b/backend/src/data/assignments/group-repository.ts
index ff8ca507..df92eaae 100644
--- a/backend/src/data/assignments/group-repository.ts
+++ b/backend/src/data/assignments/group-repository.ts
@@ -3,24 +3,16 @@ import { Group } from '../../entities/assignments/group.entity.js';
import { Assignment } from '../../entities/assignments/assignment.entity.js';
export class GroupRepository extends DwengoEntityRepository {
- public findByAssignmentAndGroupNumber(
- assignment: Assignment,
- groupNumber: number
- ): Promise {
+ public findByAssignmentAndGroupNumber(assignment: Assignment, groupNumber: number): Promise {
return this.findOne({
assignment: assignment,
groupNumber: groupNumber,
});
}
- public findAllGroupsForAssignment(
- assignment: Assignment
- ): Promise {
+ public findAllGroupsForAssignment(assignment: Assignment): Promise {
return this.findAll({ where: { assignment: assignment } });
}
- public deleteByAssignmentAndGroupNumber(
- assignment: Assignment,
- groupNumber: number
- ) {
+ public deleteByAssignmentAndGroupNumber(assignment: Assignment, groupNumber: number) {
return this.deleteWhere({
assignment: assignment,
groupNumber: groupNumber,
diff --git a/backend/src/data/assignments/submission-repository.ts b/backend/src/data/assignments/submission-repository.ts
index 5332d050..faa9fef1 100644
--- a/backend/src/data/assignments/submission-repository.ts
+++ b/backend/src/data/assignments/submission-repository.ts
@@ -5,10 +5,7 @@ import { LearningObjectIdentifier } from '../../entities/content/learning-object
import { Student } from '../../entities/users/student.entity.js';
export class SubmissionRepository extends DwengoEntityRepository {
- public findSubmissionByLearningObjectAndSubmissionNumber(
- loId: LearningObjectIdentifier,
- submissionNumber: number
- ): Promise {
+ public findSubmissionByLearningObjectAndSubmissionNumber(loId: LearningObjectIdentifier, submissionNumber: number): Promise {
return this.findOne({
learningObjectHruid: loId.hruid,
learningObjectLanguage: loId.language,
@@ -17,10 +14,7 @@ export class SubmissionRepository extends DwengoEntityRepository {
});
}
- public findMostRecentSubmissionForStudent(
- loId: LearningObjectIdentifier,
- submitter: Student
- ): Promise {
+ public findMostRecentSubmissionForStudent(loId: LearningObjectIdentifier, submitter: Student): Promise {
return this.findOne(
{
learningObjectHruid: loId.hruid,
@@ -32,10 +26,7 @@ export class SubmissionRepository extends DwengoEntityRepository {
);
}
- public findMostRecentSubmissionForGroup(
- loId: LearningObjectIdentifier,
- group: Group
- ): Promise {
+ public findMostRecentSubmissionForGroup(loId: LearningObjectIdentifier, group: Group): Promise {
return this.findOne(
{
learningObjectHruid: loId.hruid,
@@ -47,10 +38,7 @@ export class SubmissionRepository extends DwengoEntityRepository {
);
}
- public deleteSubmissionByLearningObjectAndSubmissionNumber(
- loId: LearningObjectIdentifier,
- submissionNumber: number
- ): Promise {
+ public deleteSubmissionByLearningObjectAndSubmissionNumber(loId: LearningObjectIdentifier, submissionNumber: number): Promise {
return this.deleteWhere({
learningObjectHruid: loId.hruid,
learningObjectLanguage: loId.language,
diff --git a/backend/src/data/classes/teacher-invitation-repository.ts b/backend/src/data/classes/teacher-invitation-repository.ts
index ae2713c8..6b94deec 100644
--- a/backend/src/data/classes/teacher-invitation-repository.ts
+++ b/backend/src/data/classes/teacher-invitation-repository.ts
@@ -4,24 +4,16 @@ import { TeacherInvitation } from '../../entities/classes/teacher-invitation.ent
import { Teacher } from '../../entities/users/teacher.entity.js';
export class TeacherInvitationRepository extends DwengoEntityRepository {
- public findAllInvitationsForClass(
- clazz: Class
- ): Promise {
+ public findAllInvitationsForClass(clazz: Class): Promise {
return this.findAll({ where: { class: clazz } });
}
public findAllInvitationsBy(sender: Teacher): Promise {
return this.findAll({ where: { sender: sender } });
}
- public findAllInvitationsFor(
- receiver: Teacher
- ): Promise {
+ public findAllInvitationsFor(receiver: Teacher): Promise {
return this.findAll({ where: { receiver: receiver } });
}
- public deleteBy(
- clazz: Class,
- sender: Teacher,
- receiver: Teacher
- ): Promise {
+ public deleteBy(clazz: Class, sender: Teacher, receiver: Teacher): Promise {
return this.deleteWhere({
sender: sender,
receiver: receiver,
diff --git a/backend/src/data/content/attachment-repository.ts b/backend/src/data/content/attachment-repository.ts
index 3268be90..bec874c9 100644
--- a/backend/src/data/content/attachment-repository.ts
+++ b/backend/src/data/content/attachment-repository.ts
@@ -3,10 +3,7 @@ import { Attachment } from '../../entities/content/attachment.entity.js';
import { LearningObject } from '../../entities/content/learning-object.entity.js';
export class AttachmentRepository extends DwengoEntityRepository {
- public findByLearningObjectAndNumber(
- learningObject: LearningObject,
- sequenceNumber: number
- ) {
+ public findByLearningObjectAndNumber(learningObject: LearningObject, sequenceNumber: number) {
return this.findOne({
learningObject: learningObject,
sequenceNumber: sequenceNumber,
diff --git a/backend/src/data/content/learning-object-repository.ts b/backend/src/data/content/learning-object-repository.ts
index 5d30b956..a0d74184 100644
--- a/backend/src/data/content/learning-object-repository.ts
+++ b/backend/src/data/content/learning-object-repository.ts
@@ -3,9 +3,7 @@ import { LearningObject } from '../../entities/content/learning-object.entity.js
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
export class LearningObjectRepository extends DwengoEntityRepository {
- public findByIdentifier(
- identifier: LearningObjectIdentifier
- ): Promise {
+ public findByIdentifier(identifier: LearningObjectIdentifier): Promise {
return this.findOne({
hruid: identifier.hruid,
language: identifier.language,
diff --git a/backend/src/data/content/learning-path-repository.ts b/backend/src/data/content/learning-path-repository.ts
index 3ffb1e7f..ab1f1b46 100644
--- a/backend/src/data/content/learning-path-repository.ts
+++ b/backend/src/data/content/learning-path-repository.ts
@@ -3,10 +3,7 @@ import { LearningPath } from '../../entities/content/learning-path.entity.js';
import { Language } from '../../entities/content/language.js';
export class LearningPathRepository extends DwengoEntityRepository {
- public findByHruidAndLanguage(
- hruid: string,
- language: Language
- ): Promise {
+ public findByHruidAndLanguage(hruid: string, language: Language): Promise {
return this.findOne({ hruid: hruid, language: language });
}
// This repository is read-only for now since creating own learning object is an extension feature.
diff --git a/backend/src/data/dwengo-entity-repository.ts b/backend/src/data/dwengo-entity-repository.ts
index e29d9ede..6538d6f5 100644
--- a/backend/src/data/dwengo-entity-repository.ts
+++ b/backend/src/data/dwengo-entity-repository.ts
@@ -1,8 +1,6 @@
import { EntityRepository, FilterQuery } from '@mikro-orm/core';
-export abstract class DwengoEntityRepository<
- T extends object,
-> extends EntityRepository {
+export abstract class DwengoEntityRepository extends EntityRepository {
public async save(entity: T) {
const em = this.getEntityManager();
em.persist(entity);
diff --git a/backend/src/data/questions/answer-repository.ts b/backend/src/data/questions/answer-repository.ts
index 6c45211c..bab34a65 100644
--- a/backend/src/data/questions/answer-repository.ts
+++ b/backend/src/data/questions/answer-repository.ts
@@ -4,11 +4,7 @@ import { Question } from '../../entities/questions/question.entity.js';
import { Teacher } from '../../entities/users/teacher.entity.js';
export class AnswerRepository extends DwengoEntityRepository {
- public createAnswer(answer: {
- toQuestion: Question;
- author: Teacher;
- content: string;
- }): Promise {
+ public createAnswer(answer: { toQuestion: Question; author: Teacher; content: string }): Promise {
const answerEntity = new Answer();
answerEntity.toQuestion = answer.toQuestion;
answerEntity.author = answer.author;
@@ -21,10 +17,7 @@ export class AnswerRepository extends DwengoEntityRepository {
orderBy: { sequenceNumber: 'ASC' },
});
}
- public removeAnswerByQuestionAndSequenceNumber(
- question: Question,
- sequenceNumber: number
- ): Promise {
+ public removeAnswerByQuestionAndSequenceNumber(question: Question, sequenceNumber: number): Promise {
return this.deleteWhere({
toQuestion: question,
sequenceNumber: sequenceNumber,
diff --git a/backend/src/data/questions/question-repository.ts b/backend/src/data/questions/question-repository.ts
index 8852a9ba..d41cc490 100644
--- a/backend/src/data/questions/question-repository.ts
+++ b/backend/src/data/questions/question-repository.ts
@@ -4,11 +4,7 @@ import { LearningObjectIdentifier } from '../../entities/content/learning-object
import { Student } from '../../entities/users/student.entity.js';
export class QuestionRepository extends DwengoEntityRepository {
- public createQuestion(question: {
- loId: LearningObjectIdentifier;
- author: Student;
- content: string;
- }): Promise {
+ public createQuestion(question: { loId: LearningObjectIdentifier; author: Student; content: string }): Promise {
const questionEntity = new Question();
questionEntity.learningObjectHruid = question.loId.hruid;
questionEntity.learningObjectLanguage = question.loId.language;
@@ -17,9 +13,7 @@ export class QuestionRepository extends DwengoEntityRepository {
questionEntity.content = question.content;
return this.insert(questionEntity);
}
- public findAllQuestionsAboutLearningObject(
- loId: LearningObjectIdentifier
- ): Promise {
+ public findAllQuestionsAboutLearningObject(loId: LearningObjectIdentifier): Promise {
return this.findAll({
where: {
learningObjectHruid: loId.hruid,
@@ -31,10 +25,7 @@ export class QuestionRepository extends DwengoEntityRepository {
},
});
}
- public removeQuestionByLearningObjectAndSequenceNumber(
- loId: LearningObjectIdentifier,
- sequenceNumber: number
- ): Promise {
+ public removeQuestionByLearningObjectAndSequenceNumber(loId: LearningObjectIdentifier, sequenceNumber: number): Promise {
return this.deleteWhere({
learningObjectHruid: loId.hruid,
learningObjectLanguage: loId.language,
diff --git a/backend/src/data/repositories.ts b/backend/src/data/repositories.ts
index 843eb1ac..6d67a693 100644
--- a/backend/src/data/repositories.ts
+++ b/backend/src/data/repositories.ts
@@ -1,9 +1,4 @@
-import {
- AnyEntity,
- EntityManager,
- EntityName,
- EntityRepository,
-} from '@mikro-orm/core';
+import { AnyEntity, EntityManager, EntityName, EntityRepository } from '@mikro-orm/core';
import { forkEntityManager } from '../orm.js';
import { StudentRepository } from './users/student-repository.js';
import { Student } from '../entities/users/student.entity.js';
@@ -43,9 +38,7 @@ export function transactional(f: () => Promise) {
entityManager?.transactional(f);
}
-function repositoryGetter>(
- entity: EntityName
-): () => R {
+function repositoryGetter>(entity: EntityName): () => R {
let cachedRepo: R | undefined;
return (): R => {
if (!cachedRepo) {
@@ -60,60 +53,24 @@ function repositoryGetter>(
/* Users */
export const getUserRepository = repositoryGetter(User);
-export const getStudentRepository = repositoryGetter<
- Student,
- StudentRepository
->(Student);
-export const getTeacherRepository = repositoryGetter<
- Teacher,
- TeacherRepository
->(Teacher);
+export const getStudentRepository = repositoryGetter(Student);
+export const getTeacherRepository = repositoryGetter(Teacher);
/* Classes */
-export const getClassRepository = repositoryGetter(
- Class
-);
-export const getClassJoinRequestRepository = repositoryGetter<
- ClassJoinRequest,
- ClassJoinRequestRepository
->(ClassJoinRequest);
-export const getTeacherInvitationRepository = repositoryGetter<
- TeacherInvitation,
- TeacherInvitationRepository
->(TeacherInvitationRepository);
+export const getClassRepository = repositoryGetter(Class);
+export const getClassJoinRequestRepository = repositoryGetter(ClassJoinRequest);
+export const getTeacherInvitationRepository = repositoryGetter(TeacherInvitationRepository);
/* Assignments */
-export const getAssignmentRepository = repositoryGetter<
- Assignment,
- AssignmentRepository
->(Assignment);
-export const getGroupRepository = repositoryGetter(
- Group
-);
-export const getSubmissionRepository = repositoryGetter<
- Submission,
- SubmissionRepository
->(Submission);
+export const getAssignmentRepository = repositoryGetter(Assignment);
+export const getGroupRepository = repositoryGetter(Group);
+export const getSubmissionRepository = repositoryGetter(Submission);
/* Questions and answers */
-export const getQuestionRepository = repositoryGetter<
- Question,
- QuestionRepository
->(Question);
-export const getAnswerRepository = repositoryGetter(
- Answer
-);
+export const getQuestionRepository = repositoryGetter(Question);
+export const getAnswerRepository = repositoryGetter(Answer);
/* Learning content */
-export const getLearningObjectRepository = repositoryGetter<
- LearningObject,
- LearningObjectRepository
->(LearningObject);
-export const getLearningPathRepository = repositoryGetter<
- LearningPath,
- LearningPathRepository
->(LearningPath);
-export const getAttachmentRepository = repositoryGetter<
- Attachment,
- AttachmentRepository
->(Assignment);
+export const getLearningObjectRepository = repositoryGetter(LearningObject);
+export const getLearningPathRepository = repositoryGetter(LearningPath);
+export const getAttachmentRepository = repositoryGetter(Assignment);
diff --git a/backend/src/data/themes.ts b/backend/src/data/themes.ts
index dd79400c..b0fc930c 100644
--- a/backend/src/data/themes.ts
+++ b/backend/src/data/themes.ts
@@ -23,13 +23,7 @@ export const themes: Theme[] = [
},
{
title: 'art',
- hruids: [
- 'pn_werking',
- 'un_artificiele_intelligentie',
- 'art1',
- 'art2',
- 'art3',
- ],
+ hruids: ['pn_werking', 'un_artificiele_intelligentie', 'art1', 'art2', 'art3'],
},
{
title: 'socialrobot',
@@ -37,12 +31,7 @@ export const themes: Theme[] = [
},
{
title: 'agriculture',
- hruids: [
- 'pn_werking',
- 'un_artificiele_intelligentie',
- 'agri_landbouw',
- 'agri_lopendeband',
- ],
+ hruids: ['pn_werking', 'un_artificiele_intelligentie', 'agri_landbouw', 'agri_lopendeband'],
},
{
title: 'wegostem',
@@ -83,16 +72,7 @@ export const themes: Theme[] = [
},
{
title: 'python_programming',
- hruids: [
- 'pn_werking',
- 'pn_datatypes',
- 'pn_operatoren',
- 'pn_structuren',
- 'pn_functies',
- 'art2',
- 'stem_insectbooks',
- 'un_algoenprog',
- ],
+ hruids: ['pn_werking', 'pn_datatypes', 'pn_operatoren', 'pn_structuren', 'pn_functies', 'art2', 'stem_insectbooks', 'un_algoenprog'],
},
{
title: 'stem',
@@ -110,15 +90,7 @@ export const themes: Theme[] = [
},
{
title: 'care',
- hruids: [
- 'pn_werking',
- 'un_artificiele_intelligentie',
- 'aiz1_zorg',
- 'aiz2_grafen',
- 'aiz3_unplugged',
- 'aiz4_eindtermen',
- 'aiz5_triage',
- ],
+ hruids: ['pn_werking', 'un_artificiele_intelligentie', 'aiz1_zorg', 'aiz2_grafen', 'aiz3_unplugged', 'aiz4_eindtermen', 'aiz5_triage'],
},
{
title: 'chatbot',
diff --git a/backend/src/entities/assignments/assignment.entity.ts b/backend/src/entities/assignments/assignment.entity.ts
index 89952c64..d1a04c5d 100644
--- a/backend/src/entities/assignments/assignment.entity.ts
+++ b/backend/src/entities/assignments/assignment.entity.ts
@@ -1,23 +1,11 @@
-import {
- Entity,
- Enum,
- ManyToOne,
- OneToMany,
- PrimaryKey,
- Property,
-} from '@mikro-orm/core';
+import { Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core';
import { Class } from '../classes/class.entity.js';
import { Group } from './group.entity.js';
import { Language } from '../content/language.js';
@Entity()
export class Assignment {
- @ManyToOne({
- entity: () => {
- return Class;
- },
- primary: true,
- })
+ @ManyToOne({ entity: () => Class, primary: true })
within!: Class;
@PrimaryKey({ type: 'number' })
@@ -32,18 +20,9 @@ export class Assignment {
@Property({ type: 'string' })
learningPathHruid!: string;
- @Enum({
- items: () => {
- return Language;
- },
- })
+ @Enum({ items: () => Language })
learningPathLanguage!: Language;
- @OneToMany({
- entity: () => {
- return Group;
- },
- mappedBy: 'assignment',
- })
+ @OneToMany({ entity: () => Group, mappedBy: 'assignment' })
groups!: Group[];
}
diff --git a/backend/src/entities/assignments/group.entity.ts b/backend/src/entities/assignments/group.entity.ts
index 5b224087..d8142bdb 100644
--- a/backend/src/entities/assignments/group.entity.ts
+++ b/backend/src/entities/assignments/group.entity.ts
@@ -5,9 +5,7 @@ import { Student } from '../users/student.entity.js';
@Entity()
export class Group {
@ManyToOne({
- entity: () => {
- return Assignment;
- },
+ entity: () => Assignment,
primary: true,
})
assignment!: Assignment;
@@ -16,9 +14,7 @@ export class Group {
groupNumber!: number;
@ManyToMany({
- entity: () => {
- return Student;
- },
+ entity: () => Student,
})
members!: Student[];
}
diff --git a/backend/src/entities/assignments/submission.entity.ts b/backend/src/entities/assignments/submission.entity.ts
index 1bc28add..77e01da8 100644
--- a/backend/src/entities/assignments/submission.entity.ts
+++ b/backend/src/entities/assignments/submission.entity.ts
@@ -9,9 +9,7 @@ export class Submission {
learningObjectHruid!: string;
@Enum({
- items: () => {
- return Language;
- },
+ items: () => Language,
primary: true,
})
learningObjectLanguage!: Language;
@@ -23,9 +21,7 @@ export class Submission {
submissionNumber!: number;
@ManyToOne({
- entity: () => {
- return Student;
- },
+ entity: () => Student,
})
submitter!: Student;
@@ -33,9 +29,7 @@ export class Submission {
submissionTime!: Date;
@ManyToOne({
- entity: () => {
- return Group;
- },
+ entity: () => Group,
nullable: true,
})
onBehalfOf?: Group;
diff --git a/backend/src/entities/classes/class-join-request.entity.ts b/backend/src/entities/classes/class-join-request.entity.ts
index 0ae38cd1..06e4095b 100644
--- a/backend/src/entities/classes/class-join-request.entity.ts
+++ b/backend/src/entities/classes/class-join-request.entity.ts
@@ -5,24 +5,18 @@ import { Class } from './class.entity.js';
@Entity()
export class ClassJoinRequest {
@ManyToOne({
- entity: () => {
- return Student;
- },
+ entity: () => Student,
primary: true,
})
requester!: Student;
@ManyToOne({
- entity: () => {
- return Class;
- },
+ entity: () => Class,
primary: true,
})
class!: Class;
- @Enum(() => {
- return ClassJoinRequestStatus;
- })
+ @Enum(() => ClassJoinRequestStatus)
status!: ClassJoinRequestStatus;
}
diff --git a/backend/src/entities/classes/class.entity.ts b/backend/src/entities/classes/class.entity.ts
index ecc11748..88d2660c 100644
--- a/backend/src/entities/classes/class.entity.ts
+++ b/backend/src/entities/classes/class.entity.ts
@@ -1,10 +1,4 @@
-import {
- Collection,
- Entity,
- ManyToMany,
- PrimaryKey,
- Property,
-} from '@mikro-orm/core';
+import { Collection, Entity, ManyToMany, PrimaryKey, Property } from '@mikro-orm/core';
import { v4 } from 'uuid';
import { Teacher } from '../users/teacher.entity.js';
import { Student } from '../users/student.entity.js';
@@ -17,13 +11,9 @@ export class Class {
@Property({ type: 'string' })
displayName!: string;
- @ManyToMany(() => {
- return Teacher;
- })
+ @ManyToMany(() => Teacher)
teachers!: Collection;
- @ManyToMany(() => {
- return Student;
- })
+ @ManyToMany(() => Student)
students!: Collection;
}
diff --git a/backend/src/entities/classes/teacher-invitation.entity.ts b/backend/src/entities/classes/teacher-invitation.entity.ts
index 98d2bdd4..a09d71d0 100644
--- a/backend/src/entities/classes/teacher-invitation.entity.ts
+++ b/backend/src/entities/classes/teacher-invitation.entity.ts
@@ -8,25 +8,19 @@ import { Class } from './class.entity.js';
@Entity()
export class TeacherInvitation {
@ManyToOne({
- entity: () => {
- return Teacher;
- },
+ entity: () => Teacher,
primary: true,
})
sender!: Teacher;
@ManyToOne({
- entity: () => {
- return Teacher;
- },
+ entity: () => Teacher,
primary: true,
})
receiver!: Teacher;
@ManyToOne({
- entity: () => {
- return Class;
- },
+ entity: () => Class,
primary: true,
})
class!: Class;
diff --git a/backend/src/entities/content/attachment.entity.ts b/backend/src/entities/content/attachment.entity.ts
index 7a9dd946..9e4c6275 100644
--- a/backend/src/entities/content/attachment.entity.ts
+++ b/backend/src/entities/content/attachment.entity.ts
@@ -4,9 +4,7 @@ import { LearningObject } from './learning-object.entity.js';
@Entity()
export class Attachment {
@ManyToOne({
- entity: () => {
- return LearningObject;
- },
+ entity: () => LearningObject,
primary: true,
})
learningObject!: LearningObject;
diff --git a/backend/src/entities/content/learning-object.entity.ts b/backend/src/entities/content/learning-object.entity.ts
index bf499e8a..2b7d7684 100644
--- a/backend/src/entities/content/learning-object.entity.ts
+++ b/backend/src/entities/content/learning-object.entity.ts
@@ -1,13 +1,4 @@
-import {
- Embeddable,
- Embedded,
- Entity,
- Enum,
- ManyToMany,
- OneToMany,
- PrimaryKey,
- Property,
-} from '@mikro-orm/core';
+import { Embeddable, Embedded, Entity, Enum, ManyToMany, OneToMany, PrimaryKey, Property } from '@mikro-orm/core';
import { Language } from './language.js';
import { Attachment } from './attachment.entity.js';
import { Teacher } from '../users/teacher.entity.js';
@@ -18,9 +9,7 @@ export class LearningObject {
hruid!: string;
@Enum({
- items: () => {
- return Language;
- },
+ items: () => Language,
primary: true,
})
language!: Language;
@@ -29,9 +18,7 @@ export class LearningObject {
version: string = '1';
@ManyToMany({
- entity: () => {
- return Teacher;
- },
+ entity: () => Teacher,
})
admins!: Teacher[];
@@ -57,9 +44,7 @@ export class LearningObject {
skosConcepts!: string[];
@Embedded({
- entity: () => {
- return EducationalGoal;
- },
+ entity: () => EducationalGoal,
array: true,
})
educationalGoals: EducationalGoal[] = [];
@@ -77,9 +62,7 @@ export class LearningObject {
estimatedTime!: number;
@Embedded({
- entity: () => {
- return ReturnValue;
- },
+ entity: () => ReturnValue,
})
returnValue!: ReturnValue;
@@ -90,9 +73,7 @@ export class LearningObject {
contentLocation?: string;
@OneToMany({
- entity: () => {
- return Attachment;
- },
+ entity: () => Attachment,
mappedBy: 'learningObject',
})
attachments: Attachment[] = [];
diff --git a/backend/src/entities/content/learning-path.entity.ts b/backend/src/entities/content/learning-path.entity.ts
index 28d3cadd..8dead69d 100644
--- a/backend/src/entities/content/learning-path.entity.ts
+++ b/backend/src/entities/content/learning-path.entity.ts
@@ -1,13 +1,4 @@
-import {
- Embeddable,
- Embedded,
- Entity,
- Enum,
- ManyToMany,
- OneToOne,
- PrimaryKey,
- Property,
-} from '@mikro-orm/core';
+import { Embeddable, Embedded, Entity, Enum, ManyToMany, OneToOne, PrimaryKey, Property } from '@mikro-orm/core';
import { Language } from './language.js';
import { Teacher } from '../users/teacher.entity.js';
@@ -17,17 +8,13 @@ export class LearningPath {
hruid!: string;
@Enum({
- items: () => {
- return Language;
- },
+ items: () => Language,
primary: true,
})
language!: Language;
@ManyToMany({
- entity: () => {
- return Teacher;
- },
+ entity: () => Teacher,
})
admins!: Teacher[];
@@ -41,9 +28,7 @@ export class LearningPath {
image!: string;
@Embedded({
- entity: () => {
- return LearningPathNode;
- },
+ entity: () => LearningPathNode,
array: true,
})
nodes: LearningPathNode[] = [];
@@ -55,9 +40,7 @@ export class LearningPathNode {
learningObjectHruid!: string;
@Enum({
- items: () => {
- return Language;
- },
+ items: () => Language,
})
language!: Language;
@@ -71,9 +54,7 @@ export class LearningPathNode {
startNode!: boolean;
@Embedded({
- entity: () => {
- return LearningPathTransition;
- },
+ entity: () => LearningPathTransition,
array: true,
})
transitions!: LearningPathTransition[];
@@ -85,9 +66,7 @@ export class LearningPathTransition {
condition!: string;
@OneToOne({
- entity: () => {
- return LearningPathNode;
- },
+ entity: () => LearningPathNode,
})
next!: LearningPathNode;
}
diff --git a/backend/src/entities/questions/answer.entity.ts b/backend/src/entities/questions/answer.entity.ts
index f0b67a54..627a49d0 100644
--- a/backend/src/entities/questions/answer.entity.ts
+++ b/backend/src/entities/questions/answer.entity.ts
@@ -5,17 +5,13 @@ import { Teacher } from '../users/teacher.entity.js';
@Entity()
export class Answer {
@ManyToOne({
- entity: () => {
- return Teacher;
- },
+ entity: () => Teacher,
primary: true,
})
author!: Teacher;
@ManyToOne({
- entity: () => {
- return Question;
- },
+ entity: () => Question,
primary: true,
})
toQuestion!: Question;
diff --git a/backend/src/entities/questions/question.entity.ts b/backend/src/entities/questions/question.entity.ts
index 444d2179..854a4512 100644
--- a/backend/src/entities/questions/question.entity.ts
+++ b/backend/src/entities/questions/question.entity.ts
@@ -8,9 +8,7 @@ export class Question {
learningObjectHruid!: string;
@Enum({
- items: () => {
- return Language;
- },
+ items: () => Language,
primary: true,
})
learningObjectLanguage!: Language;
@@ -22,9 +20,7 @@ export class Question {
sequenceNumber!: number;
@ManyToOne({
- entity: () => {
- return Student;
- },
+ entity: () => Student,
})
author!: Student;
diff --git a/backend/src/entities/users/student.entity.ts b/backend/src/entities/users/student.entity.ts
index c5632e84..da5b4367 100644
--- a/backend/src/entities/users/student.entity.ts
+++ b/backend/src/entities/users/student.entity.ts
@@ -5,19 +5,13 @@ import { Group } from '../assignments/group.entity.js';
import { StudentRepository } from '../../data/users/student-repository.js';
@Entity({
- repository: () => {
- return StudentRepository;
- },
+ repository: () => StudentRepository,
})
export class Student extends User {
- @ManyToMany(() => {
- return Class;
- })
+ @ManyToMany(() => Class)
classes!: Collection;
- @ManyToMany(() => {
- return Group;
- })
+ @ManyToMany(() => Group)
groups!: Collection;
constructor(
diff --git a/backend/src/entities/users/teacher.entity.ts b/backend/src/entities/users/teacher.entity.ts
index 9f11a3b0..2327527c 100644
--- a/backend/src/entities/users/teacher.entity.ts
+++ b/backend/src/entities/users/teacher.entity.ts
@@ -4,8 +4,6 @@ import { Class } from '../classes/class.entity.js';
@Entity()
export class Teacher extends User {
- @ManyToMany(() => {
- return Class;
- })
+ @ManyToMany(() => Class)
classes!: Collection;
}
diff --git a/backend/src/logging/initalize.ts b/backend/src/logging/initalize.ts
index 18166408..1ff761c9 100644
--- a/backend/src/logging/initalize.ts
+++ b/backend/src/logging/initalize.ts
@@ -1,9 +1,4 @@
-import {
- createLogger,
- format,
- Logger as WinstonLogger,
- transports,
-} from 'winston';
+import { createLogger, format, Logger as WinstonLogger, transports } from 'winston';
import LokiTransport from 'winston-loki';
import { LokiLabels } from 'loki-logger-ts';
import { LOG_LEVEL, LOKI_HOST } from '../config.js';
@@ -48,9 +43,7 @@ function initializeLogger(): Logger {
transports: [lokiTransport, consoleTransport],
});
- logger.debug(
- `Logger initialized with level ${LOG_LEVEL}, Loki host ${LOKI_HOST}`
- );
+ logger.debug(`Logger initialized with level ${LOG_LEVEL}, Loki host ${LOKI_HOST}`);
return logger;
}
diff --git a/backend/src/logging/mikroOrmLogger.ts b/backend/src/logging/mikroOrmLogger.ts
index e8bc1fad..25bbac13 100644
--- a/backend/src/logging/mikroOrmLogger.ts
+++ b/backend/src/logging/mikroOrmLogger.ts
@@ -12,42 +12,28 @@ export class MikroOrmLogger extends DefaultLogger {
switch (namespace) {
case 'query':
- this.logger.debug(
- this.createMessage(namespace, message, context)
- );
+ this.logger.debug(this.createMessage(namespace, message, context));
break;
case 'query-params':
// TODO Which log level should this be?
- this.logger.info(
- this.createMessage(namespace, message, context)
- );
+ this.logger.info(this.createMessage(namespace, message, context));
break;
case 'schema':
- this.logger.info(
- this.createMessage(namespace, message, context)
- );
+ this.logger.info(this.createMessage(namespace, message, context));
break;
case 'discovery':
- this.logger.debug(
- this.createMessage(namespace, message, context)
- );
+ this.logger.debug(this.createMessage(namespace, message, context));
break;
case 'info':
- this.logger.info(
- this.createMessage(namespace, message, context)
- );
+ this.logger.info(this.createMessage(namespace, message, context));
break;
case 'deprecated':
- this.logger.warn(
- this.createMessage(namespace, message, context)
- );
+ this.logger.warn(this.createMessage(namespace, message, context));
break;
default:
switch (context?.level) {
case 'info':
- this.logger.info(
- this.createMessage(namespace, message, context)
- );
+ this.logger.info(this.createMessage(namespace, message, context));
break;
case 'warning':
this.logger.warn(message);
@@ -62,11 +48,7 @@ export class MikroOrmLogger extends DefaultLogger {
}
}
- private createMessage(
- namespace: LoggerNamespace,
- messageArg: string,
- context?: LogContext
- ) {
+ private createMessage(namespace: LoggerNamespace, messageArg: string, context?: LogContext) {
const labels: LokiLabels = {
service: 'ORM',
};
diff --git a/backend/src/mikro-orm.config.ts b/backend/src/mikro-orm.config.ts
index f9629bef..56221473 100644
--- a/backend/src/mikro-orm.config.ts
+++ b/backend/src/mikro-orm.config.ts
@@ -52,9 +52,7 @@ function config(testingMode: boolean = false): Options {
// Workaround: vitest: `TypeError: Unknown file extension ".ts"` (ERR_UNKNOWN_FILE_EXTENSION)
// (see https://mikro-orm.io/docs/guide/project-setup#testing-the-endpoint)
- dynamicImportProvider: (id) => {
- return import(id);
- },
+ dynamicImportProvider: (id) => import(id),
};
}
@@ -70,9 +68,7 @@ function config(testingMode: boolean = false): Options {
// Logging
debug: LOG_LEVEL === 'debug',
- loggerFactory: (options: LoggerOptions) => {
- return new MikroOrmLogger(options);
- },
+ loggerFactory: (options: LoggerOptions) => new MikroOrmLogger(options),
};
}
diff --git a/backend/src/orm.ts b/backend/src/orm.ts
index 88decd92..93feea7a 100644
--- a/backend/src/orm.ts
+++ b/backend/src/orm.ts
@@ -28,9 +28,7 @@ export async function initORM(testingMode: boolean = false) {
}
export function forkEntityManager(): EntityManager {
if (!orm) {
- throw Error(
- 'Accessing the Entity Manager before the ORM is fully initialized.'
- );
+ throw Error('Accessing the Entity Manager before the ORM is fully initialized.');
}
return orm.em.fork();
}
diff --git a/backend/src/routes/learningObjects.ts b/backend/src/routes/learningObjects.ts
index 416602b5..681baa2f 100644
--- a/backend/src/routes/learningObjects.ts
+++ b/backend/src/routes/learningObjects.ts
@@ -1,8 +1,5 @@
import express from 'express';
-import {
- getAllLearningObjects,
- getLearningObject,
-} from '../controllers/learningObjects.js';
+import { getAllLearningObjects, getLearningObject } from '../controllers/learningObjects.js';
const router = express.Router();
diff --git a/backend/src/routes/question.ts b/backend/src/routes/question.ts
index f683d998..2e5db624 100644
--- a/backend/src/routes/question.ts
+++ b/backend/src/routes/question.ts
@@ -15,8 +15,7 @@ router.get('/:id', (req, res) => {
student: '0',
group: '0',
time: new Date(2025, 1, 1),
- content:
- 'Zijn alle gehele getallen groter dan 2 gelijk aan de som van 2 priemgetallen????',
+ content: 'Zijn alle gehele getallen groter dan 2 gelijk aan de som van 2 priemgetallen????',
learningObject: '0',
links: {
self: `${req.baseUrl}/${req.params.id}`,
diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learningObjects.ts
index 59cf6d31..5ea9f3aa 100644
--- a/backend/src/services/learningObjects.ts
+++ b/backend/src/services/learningObjects.ts
@@ -1,20 +1,12 @@
import { DWENGO_API_BASE } from '../config.js';
import { fetchWithLogging } from '../util/apiHelper.js';
-import {
- FilteredLearningObject,
- LearningObjectMetadata,
- LearningObjectNode,
- LearningPathResponse,
-} from '../interfaces/learningPath.js';
+import { FilteredLearningObject, LearningObjectMetadata, LearningObjectNode, LearningPathResponse } from '../interfaces/learningPath.js';
import { fetchLearningPaths } from './learningPaths.js';
import { getLogger, Logger } from '../logging/initalize.js';
const logger: Logger = getLogger();
-function filterData(
- data: LearningObjectMetadata,
- htmlUrl: string
-): FilteredLearningObject {
+function filterData(data: LearningObjectMetadata, htmlUrl: string): FilteredLearningObject {
return {
key: data.hruid, // Hruid learningObject (not path)
_id: data._id,
@@ -41,10 +33,7 @@ function filterData(
/**
* Fetches a single learning object by its HRUID
*/
-export async function getLearningObjectById(
- hruid: string,
- language: string
-): Promise {
+export async function getLearningObjectById(hruid: string, language: string): Promise {
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${hruid}&language=${language}`;
const metadata = await fetchWithLogging(
metadataUrl,
@@ -63,49 +52,24 @@ export async function getLearningObjectById(
/**
* Generic function to fetch learning objects (full data or just HRUIDs)
*/
-async function fetchLearningObjects(
- hruid: string,
- full: boolean,
- language: string
-): Promise {
+async function fetchLearningObjects(hruid: string, full: boolean, language: string): Promise {
try {
- const learningPathResponse: LearningPathResponse =
- await fetchLearningPaths(
- [hruid],
- language,
- `Learning path for HRUID "${hruid}"`
- );
+ const learningPathResponse: LearningPathResponse = await fetchLearningPaths([hruid], language, `Learning path for HRUID "${hruid}"`);
- if (
- !learningPathResponse.success ||
- !learningPathResponse.data?.length
- ) {
- logger.warn(
- `⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.`
- );
+ if (!learningPathResponse.success || !learningPathResponse.data?.length) {
+ logger.warn(`⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.`);
return [];
}
const nodes: LearningObjectNode[] = learningPathResponse.data[0].nodes;
if (!full) {
- return nodes.map((node) => {
- return node.learningobject_hruid;
- });
+ return nodes.map((node) => node.learningobject_hruid);
}
- return await Promise.all(
- nodes.map(async (node) => {
- return getLearningObjectById(
- node.learningobject_hruid,
- language
- );
- })
- ).then((objects) => {
- return objects.filter((obj): obj is FilteredLearningObject => {
- return obj !== null;
- });
- });
+ return await Promise.all(nodes.map(async (node) => getLearningObjectById(node.learningobject_hruid, language))).then((objects) =>
+ objects.filter((obj): obj is FilteredLearningObject => obj !== null)
+ );
} catch (error) {
logger.error('❌ Error fetching learning objects:', error);
return [];
@@ -115,23 +79,13 @@ async function fetchLearningObjects(
/**
* Fetch full learning object data (metadata)
*/
-export async function getLearningObjectsFromPath(
- hruid: string,
- language: string
-): Promise {
- return (await fetchLearningObjects(
- hruid,
- true,
- language
- )) as FilteredLearningObject[];
+export async function getLearningObjectsFromPath(hruid: string, language: string): Promise {
+ return (await fetchLearningObjects(hruid, true, language)) as FilteredLearningObject[];
}
/**
* Fetch only learning object HRUIDs
*/
-export async function getLearningObjectIdsFromPath(
- hruid: string,
- language: string
-): Promise {
+export async function getLearningObjectIdsFromPath(hruid: string, language: string): Promise {
return (await fetchLearningObjects(hruid, false, language)) as string[];
}
diff --git a/backend/src/services/learningPaths.ts b/backend/src/services/learningPaths.ts
index 52b168ee..7a32cd7b 100644
--- a/backend/src/services/learningPaths.ts
+++ b/backend/src/services/learningPaths.ts
@@ -1,18 +1,11 @@
import { fetchWithLogging } from '../util/apiHelper.js';
import { DWENGO_API_BASE } from '../config.js';
-import {
- LearningPath,
- LearningPathResponse,
-} from '../interfaces/learningPath.js';
+import { LearningPath, LearningPathResponse } from '../interfaces/learningPath.js';
import { getLogger, Logger } from '../logging/initalize.js';
const logger: Logger = getLogger();
-export async function fetchLearningPaths(
- hruids: string[],
- language: string,
- source: string
-): Promise {
+export async function fetchLearningPaths(hruids: string[], language: string, source: string): Promise {
if (hruids.length === 0) {
return {
success: false,
@@ -25,11 +18,7 @@ export async function fetchLearningPaths(
const apiUrl = `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`;
const params = { pathIdList: JSON.stringify({ hruids }), language };
- const learningPaths = await fetchWithLogging(
- apiUrl,
- `Learning paths for ${source}`,
- params
- );
+ const learningPaths = await fetchWithLogging(apiUrl, `Learning paths for ${source}`, params);
if (!learningPaths || learningPaths.length === 0) {
logger.warn(`⚠️ WARNING: No learning paths found for ${source}.`);
@@ -48,17 +37,10 @@ export async function fetchLearningPaths(
};
}
-export async function searchLearningPaths(
- query: string,
- language: string
-): Promise {
+export async function searchLearningPaths(query: string, language: string): Promise {
const apiUrl = `${DWENGO_API_BASE}/learningPath/search`;
const params = { all: query, language };
- const searchResults = await fetchWithLogging(
- apiUrl,
- `Search learning paths with query "${query}"`,
- params
- );
+ const searchResults = await fetchWithLogging(apiUrl, `Search learning paths with query "${query}"`, params);
return searchResults ?? [];
}
diff --git a/backend/src/util/apiHelper.ts b/backend/src/util/apiHelper.ts
index 83c3e975..b8c1b943 100644
--- a/backend/src/util/apiHelper.ts
+++ b/backend/src/util/apiHelper.ts
@@ -12,11 +12,7 @@ const logger: Logger = getLogger();
* @param params
* @returns The response data if successful, or null if an error occurs.
*/
-export async function fetchWithLogging(
- url: string,
- description: string,
- params?: Record
-): Promise {
+export async function fetchWithLogging(url: string, description: string, params?: Record): Promise {
try {
const config: AxiosRequestConfig = params ? { params } : {};
@@ -25,19 +21,14 @@ export async function fetchWithLogging(
} catch (error: any) {
if (error.response) {
if (error.response.status === 404) {
- logger.debug(
- `❌ ERROR: ${description} not found (404) at "${url}".`
- );
+ logger.debug(`❌ ERROR: ${description} not found (404) at "${url}".`);
} else {
logger.debug(
`❌ ERROR: Failed to fetch ${description}. Status: ${error.response.status} - ${error.response.statusText} (URL: "${url}")`
);
}
} else {
- logger.debug(
- `❌ ERROR: Network or unexpected error when fetching ${description}:`,
- error.message
- );
+ logger.debug(`❌ ERROR: Network or unexpected error when fetching ${description}:`, error.message);
}
return null;
}
diff --git a/backend/src/util/envvars.ts b/backend/src/util/envvars.ts
index 5a06ac22..6d10e296 100644
--- a/backend/src/util/envvars.ts
+++ b/backend/src/util/envvars.ts
@@ -36,9 +36,7 @@ export function getNumericEnvVar(envVar: EnvVar): number {
const valueString = getEnvVar(envVar);
const value = parseInt(valueString);
if (isNaN(value)) {
- throw new Error(
- `Invalid value for environment variable ${envVar.key}: ${valueString}. Expected a number.`
- );
+ throw new Error(`Invalid value for environment variable ${envVar.key}: ${valueString}. Expected a number.`);
} else {
return value;
}
diff --git a/backend/src/util/translationHelper.ts b/backend/src/util/translationHelper.ts
index 650d9843..d0a83b02 100644
--- a/backend/src/util/translationHelper.ts
+++ b/backend/src/util/translationHelper.ts
@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import yaml from 'js-yaml';
-import { FALLBACK_LANG } from '../../config.js';
+import { FALLBACK_LANG } from '../config.js';
import { getLogger, Logger } from '../logging/initalize.js';
const logger: Logger = getLogger();
@@ -12,15 +12,8 @@ export function loadTranslations(language: string): T {
const yamlFile = fs.readFileSync(filePath, 'utf8');
return yaml.load(yamlFile) as T;
} catch (error) {
- logger.warn(
- `Cannot load translation for ${language}, fallen back to dutch`,
- error
- );
- const fallbackPath = path.join(
- process.cwd(),
- '_i18n',
- `${FALLBACK_LANG}.yml`
- );
+ logger.warn(`Cannot load translation for ${language}, fallen back to dutch`, error);
+ const fallbackPath = path.join(process.cwd(), '_i18n', `${FALLBACK_LANG}.yml`);
return yaml.load(fs.readFileSync(fallbackPath, 'utf8')) as T;
}
}
diff --git a/backend/tests/data/users.test.ts b/backend/tests/data/users.test.ts
index 887748a2..87149050 100644
--- a/backend/tests/data/users.test.ts
+++ b/backend/tests/data/users.test.ts
@@ -16,12 +16,9 @@ describe('StudentRepository', () => {
});
it('should return the queried student after he was added', async () => {
- await studentRepository.insert(
- new Student(username, firstName, lastName)
- );
+ await studentRepository.insert(new Student(username, firstName, lastName));
- const retrievedStudent =
- await studentRepository.findByUsername(username);
+ const retrievedStudent = await studentRepository.findByUsername(username);
expect(retrievedStudent).toBeTruthy();
expect(retrievedStudent?.firstName).toBe(firstName);
expect(retrievedStudent?.lastName).toBe(lastName);
@@ -30,8 +27,7 @@ describe('StudentRepository', () => {
it('should no longer return the queried student after he was removed again', async () => {
await studentRepository.deleteByUsername(username);
- const retrievedStudent =
- await studentRepository.findByUsername(username);
+ const retrievedStudent = await studentRepository.findByUsername(username);
expect(retrievedStudent).toBeNull();
});
});
diff --git a/docs/architecture/schema.png b/docs/architecture/schema.png
index 616d896c..9e4b00ce 100644
Binary files a/docs/architecture/schema.png and b/docs/architecture/schema.png differ
diff --git a/docs/architecture/schema.py b/docs/architecture/schema.py
index 87a59f9a..7aa4cefd 100644
--- a/docs/architecture/schema.py
+++ b/docs/architecture/schema.py
@@ -1,30 +1,49 @@
-from diagrams import Cluster, Diagram
+from diagrams import Cluster, Diagram, Edge
from diagrams.custom import Custom
from diagrams.onprem.certificates import LetsEncrypt
-from diagrams.onprem.container import Docker
from diagrams.onprem.database import PostgreSQL
from diagrams.onprem.logging import Loki
from diagrams.onprem.monitoring import Grafana
from diagrams.onprem.network import Nginx
+from diagrams.programming.flowchart import InputOutput
from diagrams.programming.framework import Vue
from diagrams.programming.language import Nodejs
-from diagrams.programming.flowchart import InputOutput
with Diagram("Dwengo-1 architectuur", filename="docs/architecture/schema", show=False):
- reverse_proxy = Nginx("reverse proxy")
- reverse_proxy >> LetsEncrypt("SSL")
+ ingress = Nginx("Reverse Proxy")
+ certificates = LetsEncrypt("SSL")
- with Cluster("Docker"):
- Docker()
-
- frontend = Vue("/")
- backend = Nodejs("/api")
- reverse_proxy >> frontend
- frontend >> backend >> InputOutput("MikroORM") >> PostgreSQL()
-
- backend >> Loki("logging") >> Grafana("monitoring")
-
- with Cluster("Dwengo"):
+ with Cluster("Dwengo VZW"):
dwengo = Custom("Dwengo", "../../assets/img/dwengo-groen-zwart.png")
- backend >> dwengo
+ with Cluster("Dwengo-1"):
+ frontend = Vue("/")
+ backend = Nodejs("/api")
+ identity_provider = Custom("IDP", "../../assets/img/keycloak.png")
+
+ database = PostgreSQL("Database")
+ orm = InputOutput("MikroORM")
+ orm >> Edge(label="map") << database
+
+ with Cluster("Observability"):
+ logging = Loki("Logging")
+ logging << Edge(color="firebrick", style="dashed") << Grafana("Monitoring")
+
+ dependencies = [
+ dwengo,
+ logging,
+ orm
+ ]
+
+ backend >> dependencies
+
+ service = [
+ frontend,
+ backend,
+ identity_provider,
+ certificates
+ ]
+
+ ingress \
+ >> Edge(color="darkgreen") \
+ << service
diff --git a/eslint.config.ts b/eslint.config.ts
index b838d8c5..6a59a583 100644
--- a/eslint.config.ts
+++ b/eslint.config.ts
@@ -16,12 +16,7 @@ export default [
prettierConfig,
includeIgnoreFile(gitignorePath),
{
- ignores: [
- '**/dist/**',
- '**/.node_modules/**',
- '**/coverage/**',
- '**/.github/**',
- ],
+ ignores: ['**/dist/**', '**/.node_modules/**', '**/coverage/**', '**/.github/**'],
files: ['**/*.ts', '**/*.cts', '**.*.mts', '**/*.ts'],
},
{
@@ -43,8 +38,9 @@ export default [
'no-unreachable-loop': 'warn',
'no-use-before-define': 'error',
'no-useless-assignment': 'error',
+ 'no-unused-vars': 'error',
- 'arrow-body-style': ['warn', 'always'],
+ 'arrow-body-style': ['warn', 'as-needed'],
'block-scoped-var': 'warn',
camelcase: 'warn',
'capitalized-comments': 'warn',
diff --git a/frontend/eslint.config.ts b/frontend/eslint.config.ts
index 9e68e9c0..e9359af7 100644
--- a/frontend/eslint.config.ts
+++ b/frontend/eslint.config.ts
@@ -14,6 +14,9 @@ const vueConfig = defineConfigWithVueTs(
{
name: "app/files-to-lint",
files: ["**/*.{ts,mts,tsx,vue}"],
+ rules: {
+ "no-useless-assignment": "off", // Depend on `no-unused-vars` to catch this
+ },
},
{
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 558d88e7..eaefcd6c 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -22,16 +22,12 @@ const router = createRouter({
{
path: "/",
name: "home",
- component: () => {
- return import("../views/HomePage.vue");
- },
+ component: () => import("../views/HomePage.vue"),
},
{
path: "/login",
name: "LoginPage",
- component: () => {
- return import("../views/LoginPage.vue");
- },
+ component: () => import("../views/LoginPage.vue"),
},
{
path: "/student/:id",
diff --git a/prettier.config.js b/prettier.config.js
index 8be1cdc4..175599a4 100644
--- a/prettier.config.js
+++ b/prettier.config.js
@@ -2,7 +2,7 @@
* @type {import("prettier").Options}
*/
export default {
- printWidth: 80,
+ printWidth: 150,
semi: true,
singleQuote: true,
trailingComma: 'es5',