refactor(backend): Functions
This commit is contained in:
parent
5ec62554e3
commit
65c1a5e6b6
57 changed files with 172 additions and 117 deletions
|
@ -26,7 +26,7 @@ app.use('/api', apiRouter);
|
||||||
// Swagger
|
// Swagger
|
||||||
app.use('/api-docs', swaggerUi.serve, swaggerMiddleware);
|
app.use('/api-docs', swaggerUi.serve, swaggerMiddleware);
|
||||||
|
|
||||||
async function startServer() {
|
async function startServer(): Promise<void> {
|
||||||
await initORM();
|
await initORM();
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
|
|
|
@ -47,7 +47,7 @@ export async function getStudentHandler(req: Request, res: Response): Promise<vo
|
||||||
res.status(201).json(user);
|
res.status(201).json(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createStudentHandler(req: Request, res: Response) {
|
export async function createStudentHandler(req: Request, res: Response): Promise<void> {
|
||||||
const userData = req.body as StudentDTO;
|
const userData = req.body as StudentDTO;
|
||||||
|
|
||||||
if (!userData.username || !userData.firstName || !userData.lastName) {
|
if (!userData.username || !userData.firstName || !userData.lastName) {
|
||||||
|
@ -61,7 +61,7 @@ export async function createStudentHandler(req: Request, res: Response) {
|
||||||
res.status(201).json(newUser);
|
res.status(201).json(newUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteStudentHandler(req: Request, res: Response) {
|
export async function deleteStudentHandler(req: Request, res: Response): Promise<void> {
|
||||||
const username = req.params.username;
|
const username = req.params.username;
|
||||||
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ export async function getSubmissionHandler(req: Request<SubmissionParams>, res:
|
||||||
res.json(submission);
|
res.json(submission);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSubmissionHandler(req: Request, res: Response) {
|
export async function createSubmissionHandler(req: Request, res: Response): Promise<void> {
|
||||||
const submissionDTO = req.body as SubmissionDTO;
|
const submissionDTO = req.body as SubmissionDTO;
|
||||||
|
|
||||||
const submission = await createSubmission(submissionDTO);
|
const submission = await createSubmission(submissionDTO);
|
||||||
|
@ -42,7 +42,7 @@ export async function createSubmissionHandler(req: Request, res: Response) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteSubmissionHandler(req: Request, res: Response) {
|
export async function deleteSubmissionHandler(req: Request, res: Response): Promise<void> {
|
||||||
const hruid = req.params.hruid;
|
const hruid = req.params.hruid;
|
||||||
const submissionNumber = +req.params.id;
|
const submissionNumber = +req.params.id;
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ export async function getTeacherHandler(req: Request, res: Response): Promise<vo
|
||||||
res.status(201).json(user);
|
res.status(201).json(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createTeacherHandler(req: Request, res: Response) {
|
export async function createTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||||
const userData = req.body as TeacherDTO;
|
const userData = req.body as TeacherDTO;
|
||||||
|
|
||||||
if (!userData.username || !userData.firstName || !userData.lastName) {
|
if (!userData.username || !userData.firstName || !userData.lastName) {
|
||||||
|
@ -64,7 +64,7 @@ export async function createTeacherHandler(req: Request, res: Response) {
|
||||||
res.status(201).json(newUser);
|
res.status(201).json(newUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteTeacherHandler(req: Request, res: Response) {
|
export async function deleteTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||||
const username = req.params.username;
|
const username = req.params.username;
|
||||||
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface Translations {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getThemes(req: Request, res: Response) {
|
export function getThemes(req: Request, res: Response): void {
|
||||||
const language = (req.query.language as string)?.toLowerCase() || 'nl';
|
const language = (req.query.language as string)?.toLowerCase() || 'nl';
|
||||||
const translations = loadTranslations<Translations>(language);
|
const translations = loadTranslations<Translations>(language);
|
||||||
const themeList = themes.map((theme) => ({
|
const themeList = themes.map((theme) => ({
|
||||||
|
@ -21,7 +21,7 @@ export function getThemes(req: Request, res: Response) {
|
||||||
res.json(themeList);
|
res.json(themeList);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getThemeByTitle(req: Request, res: Response) {
|
export function getThemeByTitle(req: Request, res: Response): void {
|
||||||
const themeKey = req.params.theme;
|
const themeKey = req.params.theme;
|
||||||
const theme = themes.find((t) => t.title === themeKey);
|
const theme = themes.find((t) => t.title === themeKey);
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ export async function getUserHandler<T extends User>(req: Request, res: Response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>, userClass: new () => T) {
|
export async function createUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>, userClass: new () => T): Promise<void> {
|
||||||
try {
|
try {
|
||||||
getLogger().debug({ req: req });
|
getLogger().debug({ req: req });
|
||||||
const userData = req.body as UserDTO;
|
const userData = req.body as UserDTO;
|
||||||
|
@ -67,7 +67,7 @@ export async function createUserHandler<T extends User>(req: Request, res: Respo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>) {
|
export async function deleteUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const username = req.params.username;
|
const username = req.params.username;
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,13 @@ import { Assignment } from '../../entities/assignments/assignment.entity.js';
|
||||||
import { Class } from '../../entities/classes/class.entity.js';
|
import { Class } from '../../entities/classes/class.entity.js';
|
||||||
|
|
||||||
export class AssignmentRepository extends DwengoEntityRepository<Assignment> {
|
export class AssignmentRepository extends DwengoEntityRepository<Assignment> {
|
||||||
public findByClassAndId(within: Class, id: number): Promise<Assignment | null> {
|
public async findByClassAndId(within: Class, id: number): Promise<Assignment | null> {
|
||||||
return this.findOne({ within: within, id: id });
|
return this.findOne({ within: within, id: id });
|
||||||
}
|
}
|
||||||
public findAllAssignmentsInClass(within: Class): Promise<Assignment[]> {
|
public async findAllAssignmentsInClass(within: Class): Promise<Assignment[]> {
|
||||||
return this.findAll({ where: { within: within } });
|
return this.findAll({ where: { within: within } });
|
||||||
}
|
}
|
||||||
public deleteByClassAndId(within: Class, id: number): Promise<void> {
|
public async deleteByClassAndId(within: Class, id: number): Promise<void> {
|
||||||
return this.deleteWhere({ within: within, id: id });
|
return this.deleteWhere({ within: within, id: id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Assignment } from '../../entities/assignments/assignment.entity.js';
|
||||||
import { Student } from '../../entities/users/student.entity.js';
|
import { Student } from '../../entities/users/student.entity.js';
|
||||||
|
|
||||||
export class GroupRepository extends DwengoEntityRepository<Group> {
|
export class GroupRepository extends DwengoEntityRepository<Group> {
|
||||||
public findByAssignmentAndGroupNumber(assignment: Assignment, groupNumber: number): Promise<Group | null> {
|
public async findByAssignmentAndGroupNumber(assignment: Assignment, groupNumber: number): Promise<Group | null> {
|
||||||
return this.findOne(
|
return this.findOne(
|
||||||
{
|
{
|
||||||
assignment: assignment,
|
assignment: assignment,
|
||||||
|
@ -13,16 +13,16 @@ export class GroupRepository extends DwengoEntityRepository<Group> {
|
||||||
{ populate: ['members'] }
|
{ populate: ['members'] }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public findAllGroupsForAssignment(assignment: Assignment): Promise<Group[]> {
|
public async findAllGroupsForAssignment(assignment: Assignment): Promise<Group[]> {
|
||||||
return this.findAll({
|
return this.findAll({
|
||||||
where: { assignment: assignment },
|
where: { assignment: assignment },
|
||||||
populate: ['members'],
|
populate: ['members'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public findAllGroupsWithStudent(student: Student): Promise<Group[]> {
|
public async findAllGroupsWithStudent(student: Student): Promise<Group[]> {
|
||||||
return this.find({ members: student }, { populate: ['members'] });
|
return this.find({ members: student }, { populate: ['members'] });
|
||||||
}
|
}
|
||||||
public deleteByAssignmentAndGroupNumber(assignment: Assignment, groupNumber: number) {
|
public async deleteByAssignmentAndGroupNumber(assignment: Assignment, groupNumber: number): Promise<void> {
|
||||||
return this.deleteWhere({
|
return this.deleteWhere({
|
||||||
assignment: assignment,
|
assignment: assignment,
|
||||||
groupNumber: groupNumber,
|
groupNumber: groupNumber,
|
||||||
|
|
|
@ -5,7 +5,10 @@ import { LearningObjectIdentifier } from '../../entities/content/learning-object
|
||||||
import { Student } from '../../entities/users/student.entity.js';
|
import { Student } from '../../entities/users/student.entity.js';
|
||||||
|
|
||||||
export class SubmissionRepository extends DwengoEntityRepository<Submission> {
|
export class SubmissionRepository extends DwengoEntityRepository<Submission> {
|
||||||
public findSubmissionByLearningObjectAndSubmissionNumber(loId: LearningObjectIdentifier, submissionNumber: number): Promise<Submission | null> {
|
public async findSubmissionByLearningObjectAndSubmissionNumber(
|
||||||
|
loId: LearningObjectIdentifier,
|
||||||
|
submissionNumber: number
|
||||||
|
): Promise<Submission | null> {
|
||||||
return this.findOne({
|
return this.findOne({
|
||||||
learningObjectHruid: loId.hruid,
|
learningObjectHruid: loId.hruid,
|
||||||
learningObjectLanguage: loId.language,
|
learningObjectLanguage: loId.language,
|
||||||
|
@ -14,7 +17,7 @@ export class SubmissionRepository extends DwengoEntityRepository<Submission> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public findMostRecentSubmissionForStudent(loId: LearningObjectIdentifier, submitter: Student): Promise<Submission | null> {
|
public async findMostRecentSubmissionForStudent(loId: LearningObjectIdentifier, submitter: Student): Promise<Submission | null> {
|
||||||
return this.findOne(
|
return this.findOne(
|
||||||
{
|
{
|
||||||
learningObjectHruid: loId.hruid,
|
learningObjectHruid: loId.hruid,
|
||||||
|
@ -26,7 +29,7 @@ export class SubmissionRepository extends DwengoEntityRepository<Submission> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findMostRecentSubmissionForGroup(loId: LearningObjectIdentifier, group: Group): Promise<Submission | null> {
|
public async findMostRecentSubmissionForGroup(loId: LearningObjectIdentifier, group: Group): Promise<Submission | null> {
|
||||||
return this.findOne(
|
return this.findOne(
|
||||||
{
|
{
|
||||||
learningObjectHruid: loId.hruid,
|
learningObjectHruid: loId.hruid,
|
||||||
|
@ -38,15 +41,15 @@ export class SubmissionRepository extends DwengoEntityRepository<Submission> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findAllSubmissionsForGroup(group: Group): Promise<Submission[]> {
|
public async findAllSubmissionsForGroup(group: Group): Promise<Submission[]> {
|
||||||
return this.find({ onBehalfOf: group });
|
return this.find({ onBehalfOf: group });
|
||||||
}
|
}
|
||||||
|
|
||||||
public findAllSubmissionsForStudent(student: Student): Promise<Submission[]> {
|
public async findAllSubmissionsForStudent(student: Student): Promise<Submission[]> {
|
||||||
return this.find({ submitter: student });
|
return this.find({ submitter: student });
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteSubmissionByLearningObjectAndSubmissionNumber(loId: LearningObjectIdentifier, submissionNumber: number): Promise<void> {
|
public async deleteSubmissionByLearningObjectAndSubmissionNumber(loId: LearningObjectIdentifier, submissionNumber: number): Promise<void> {
|
||||||
return this.deleteWhere({
|
return this.deleteWhere({
|
||||||
learningObjectHruid: loId.hruid,
|
learningObjectHruid: loId.hruid,
|
||||||
learningObjectLanguage: loId.language,
|
learningObjectLanguage: loId.language,
|
||||||
|
|
|
@ -4,13 +4,13 @@ import { ClassJoinRequest } from '../../entities/classes/class-join-request.enti
|
||||||
import { Student } from '../../entities/users/student.entity.js';
|
import { Student } from '../../entities/users/student.entity.js';
|
||||||
|
|
||||||
export class ClassJoinRequestRepository extends DwengoEntityRepository<ClassJoinRequest> {
|
export class ClassJoinRequestRepository extends DwengoEntityRepository<ClassJoinRequest> {
|
||||||
public findAllRequestsBy(requester: Student): Promise<ClassJoinRequest[]> {
|
public async findAllRequestsBy(requester: Student): Promise<ClassJoinRequest[]> {
|
||||||
return this.findAll({ where: { requester: requester } });
|
return this.findAll({ where: { requester: requester } });
|
||||||
}
|
}
|
||||||
public findAllOpenRequestsTo(clazz: Class): Promise<ClassJoinRequest[]> {
|
public async findAllOpenRequestsTo(clazz: Class): Promise<ClassJoinRequest[]> {
|
||||||
return this.findAll({ where: { class: clazz } });
|
return this.findAll({ where: { class: clazz } });
|
||||||
}
|
}
|
||||||
public deleteBy(requester: Student, clazz: Class): Promise<void> {
|
public async deleteBy(requester: Student, clazz: Class): Promise<void> {
|
||||||
return this.deleteWhere({ requester: requester, class: clazz });
|
return this.deleteWhere({ requester: requester, class: clazz });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,20 @@ import { Student } from '../../entities/users/student.entity.js';
|
||||||
import { Teacher } from '../../entities/users/teacher.entity';
|
import { Teacher } from '../../entities/users/teacher.entity';
|
||||||
|
|
||||||
export class ClassRepository extends DwengoEntityRepository<Class> {
|
export class ClassRepository extends DwengoEntityRepository<Class> {
|
||||||
public findById(id: string): Promise<Class | null> {
|
public async findById(id: string): Promise<Class | null> {
|
||||||
return this.findOne({ classId: id }, { populate: ['students', 'teachers'] });
|
return this.findOne({ classId: id }, { populate: ['students', 'teachers'] });
|
||||||
}
|
}
|
||||||
public deleteById(id: string): Promise<void> {
|
public async deleteById(id: string): Promise<void> {
|
||||||
return this.deleteWhere({ classId: id });
|
return this.deleteWhere({ classId: id });
|
||||||
}
|
}
|
||||||
public findByStudent(student: Student): Promise<Class[]> {
|
public async findByStudent(student: Student): Promise<Class[]> {
|
||||||
return this.find(
|
return this.find(
|
||||||
{ students: student },
|
{ students: student },
|
||||||
{ populate: ['students', 'teachers'] } // Voegt student en teacher objecten toe
|
{ populate: ['students', 'teachers'] } // Voegt student en teacher objecten toe
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findByTeacher(teacher: Teacher): Promise<Class[]> {
|
public async findByTeacher(teacher: Teacher): Promise<Class[]> {
|
||||||
return this.find({ teachers: teacher }, { populate: ['students', 'teachers'] });
|
return this.find({ teachers: teacher }, { populate: ['students', 'teachers'] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,16 @@ import { TeacherInvitation } from '../../entities/classes/teacher-invitation.ent
|
||||||
import { Teacher } from '../../entities/users/teacher.entity.js';
|
import { Teacher } from '../../entities/users/teacher.entity.js';
|
||||||
|
|
||||||
export class TeacherInvitationRepository extends DwengoEntityRepository<TeacherInvitation> {
|
export class TeacherInvitationRepository extends DwengoEntityRepository<TeacherInvitation> {
|
||||||
public findAllInvitationsForClass(clazz: Class): Promise<TeacherInvitation[]> {
|
public async findAllInvitationsForClass(clazz: Class): Promise<TeacherInvitation[]> {
|
||||||
return this.findAll({ where: { class: clazz } });
|
return this.findAll({ where: { class: clazz } });
|
||||||
}
|
}
|
||||||
public findAllInvitationsBy(sender: Teacher): Promise<TeacherInvitation[]> {
|
public async findAllInvitationsBy(sender: Teacher): Promise<TeacherInvitation[]> {
|
||||||
return this.findAll({ where: { sender: sender } });
|
return this.findAll({ where: { sender: sender } });
|
||||||
}
|
}
|
||||||
public findAllInvitationsFor(receiver: Teacher): Promise<TeacherInvitation[]> {
|
public async findAllInvitationsFor(receiver: Teacher): Promise<TeacherInvitation[]> {
|
||||||
return this.findAll({ where: { receiver: receiver } });
|
return this.findAll({ where: { receiver: receiver } });
|
||||||
}
|
}
|
||||||
public deleteBy(clazz: Class, sender: Teacher, receiver: Teacher): Promise<void> {
|
public async deleteBy(clazz: Class, sender: Teacher, receiver: Teacher): Promise<void> {
|
||||||
return this.deleteWhere({
|
return this.deleteWhere({
|
||||||
sender: sender,
|
sender: sender,
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Language } from '../../entities/content/language';
|
||||||
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier';
|
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier';
|
||||||
|
|
||||||
export class AttachmentRepository extends DwengoEntityRepository<Attachment> {
|
export class AttachmentRepository extends DwengoEntityRepository<Attachment> {
|
||||||
public findByLearningObjectIdAndName(learningObjectId: LearningObjectIdentifier, name: string): Promise<Attachment | null> {
|
public async findByLearningObjectIdAndName(learningObjectId: LearningObjectIdentifier, name: string): Promise<Attachment | null> {
|
||||||
return this.findOne({
|
return this.findOne({
|
||||||
learningObject: {
|
learningObject: {
|
||||||
hruid: learningObjectId.hruid,
|
hruid: learningObjectId.hruid,
|
||||||
|
@ -15,7 +15,11 @@ export class AttachmentRepository extends DwengoEntityRepository<Attachment> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public findByMostRecentVersionOfLearningObjectAndName(hruid: string, language: Language, attachmentName: string): Promise<Attachment | null> {
|
public async findByMostRecentVersionOfLearningObjectAndName(
|
||||||
|
hruid: string,
|
||||||
|
language: Language,
|
||||||
|
attachmentName: string
|
||||||
|
): Promise<Attachment | null> {
|
||||||
return this.findOne(
|
return this.findOne(
|
||||||
{
|
{
|
||||||
learningObject: {
|
learningObject: {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Language } from '../../entities/content/language.js';
|
||||||
import { Teacher } from '../../entities/users/teacher.entity.js';
|
import { Teacher } from '../../entities/users/teacher.entity.js';
|
||||||
|
|
||||||
export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> {
|
export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> {
|
||||||
public findByIdentifier(identifier: LearningObjectIdentifier): Promise<LearningObject | null> {
|
public async findByIdentifier(identifier: LearningObjectIdentifier): Promise<LearningObject | null> {
|
||||||
return this.findOne(
|
return this.findOne(
|
||||||
{
|
{
|
||||||
hruid: identifier.hruid,
|
hruid: identifier.hruid,
|
||||||
|
@ -18,7 +18,7 @@ export class LearningObjectRepository extends DwengoEntityRepository<LearningObj
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findLatestByHruidAndLanguage(hruid: string, language: Language) {
|
public async findLatestByHruidAndLanguage(hruid: string, language: Language): Promise<LearningObject | null> {
|
||||||
return this.findOne(
|
return this.findOne(
|
||||||
{
|
{
|
||||||
hruid: hruid,
|
hruid: hruid,
|
||||||
|
@ -33,7 +33,7 @@ export class LearningObjectRepository extends DwengoEntityRepository<LearningObj
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findAllByTeacher(teacher: Teacher): Promise<LearningObject[]> {
|
public async findAllByTeacher(teacher: Teacher): Promise<LearningObject[]> {
|
||||||
return this.find(
|
return this.find(
|
||||||
{ admins: teacher },
|
{ admins: teacher },
|
||||||
{ populate: ['admins'] } // Make sure to load admin relations
|
{ populate: ['admins'] } // Make sure to load admin relations
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { LearningPath } from '../../entities/content/learning-path.entity.js';
|
||||||
import { Language } from '../../entities/content/language.js';
|
import { Language } from '../../entities/content/language.js';
|
||||||
|
|
||||||
export class LearningPathRepository extends DwengoEntityRepository<LearningPath> {
|
export class LearningPathRepository extends DwengoEntityRepository<LearningPath> {
|
||||||
public findByHruidAndLanguage(hruid: string, language: Language): Promise<LearningPath | null> {
|
public async findByHruidAndLanguage(hruid: string, language: Language): Promise<LearningPath | null> {
|
||||||
return this.findOne({ hruid: hruid, language: language }, { populate: ['nodes', 'nodes.transitions'] });
|
return this.findOne({ hruid: hruid, language: language }, { populate: ['nodes', 'nodes.transitions'] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { EntityRepository, FilterQuery } from '@mikro-orm/core';
|
import { EntityRepository, FilterQuery } from '@mikro-orm/core';
|
||||||
|
|
||||||
export abstract class DwengoEntityRepository<T extends object> extends EntityRepository<T> {
|
export abstract class DwengoEntityRepository<T extends object> extends EntityRepository<T> {
|
||||||
public async save(entity: T) {
|
public async save(entity: T): Promise<void> {
|
||||||
const em = this.getEntityManager();
|
const em = this.getEntityManager();
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
await em.flush();
|
await em.flush();
|
||||||
}
|
}
|
||||||
public async deleteWhere(query: FilterQuery<T>) {
|
public async deleteWhere(query: FilterQuery<T>): Promise<void> {
|
||||||
const toDelete = await this.findOne(query);
|
const toDelete = await this.findOne(query);
|
||||||
const em = this.getEntityManager();
|
const em = this.getEntityManager();
|
||||||
if (toDelete) {
|
if (toDelete) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Question } from '../../entities/questions/question.entity.js';
|
||||||
import { Teacher } from '../../entities/users/teacher.entity.js';
|
import { Teacher } from '../../entities/users/teacher.entity.js';
|
||||||
|
|
||||||
export class AnswerRepository extends DwengoEntityRepository<Answer> {
|
export class AnswerRepository extends DwengoEntityRepository<Answer> {
|
||||||
public createAnswer(answer: { toQuestion: Question; author: Teacher; content: string }): Promise<Answer> {
|
public async createAnswer(answer: { toQuestion: Question; author: Teacher; content: string }): Promise<Answer> {
|
||||||
const answerEntity = this.create({
|
const answerEntity = this.create({
|
||||||
toQuestion: answer.toQuestion,
|
toQuestion: answer.toQuestion,
|
||||||
author: answer.author,
|
author: answer.author,
|
||||||
|
@ -13,13 +13,13 @@ export class AnswerRepository extends DwengoEntityRepository<Answer> {
|
||||||
});
|
});
|
||||||
return this.insert(answerEntity);
|
return this.insert(answerEntity);
|
||||||
}
|
}
|
||||||
public findAllAnswersToQuestion(question: Question): Promise<Answer[]> {
|
public async findAllAnswersToQuestion(question: Question): Promise<Answer[]> {
|
||||||
return this.findAll({
|
return this.findAll({
|
||||||
where: { toQuestion: question },
|
where: { toQuestion: question },
|
||||||
orderBy: { sequenceNumber: 'ASC' },
|
orderBy: { sequenceNumber: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public removeAnswerByQuestionAndSequenceNumber(question: Question, sequenceNumber: number): Promise<void> {
|
public async removeAnswerByQuestionAndSequenceNumber(question: Question, sequenceNumber: number): Promise<void> {
|
||||||
return this.deleteWhere({
|
return this.deleteWhere({
|
||||||
toQuestion: question,
|
toQuestion: question,
|
||||||
sequenceNumber: sequenceNumber,
|
sequenceNumber: sequenceNumber,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Student } from '../../entities/users/student.entity.js';
|
||||||
import { LearningObject } from '../../entities/content/learning-object.entity.js';
|
import { LearningObject } from '../../entities/content/learning-object.entity.js';
|
||||||
|
|
||||||
export class QuestionRepository extends DwengoEntityRepository<Question> {
|
export class QuestionRepository extends DwengoEntityRepository<Question> {
|
||||||
public createQuestion(question: { loId: LearningObjectIdentifier; author: Student; content: string }): Promise<Question> {
|
public async createQuestion(question: { loId: LearningObjectIdentifier; author: Student; content: string }): Promise<Question> {
|
||||||
const questionEntity = this.create({
|
const questionEntity = this.create({
|
||||||
learningObjectHruid: question.loId.hruid,
|
learningObjectHruid: question.loId.hruid,
|
||||||
learningObjectLanguage: question.loId.language,
|
learningObjectLanguage: question.loId.language,
|
||||||
|
@ -21,7 +21,7 @@ export class QuestionRepository extends DwengoEntityRepository<Question> {
|
||||||
questionEntity.content = question.content;
|
questionEntity.content = question.content;
|
||||||
return this.insert(questionEntity);
|
return this.insert(questionEntity);
|
||||||
}
|
}
|
||||||
public findAllQuestionsAboutLearningObject(loId: LearningObjectIdentifier): Promise<Question[]> {
|
public async findAllQuestionsAboutLearningObject(loId: LearningObjectIdentifier): Promise<Question[]> {
|
||||||
return this.findAll({
|
return this.findAll({
|
||||||
where: {
|
where: {
|
||||||
learningObjectHruid: loId.hruid,
|
learningObjectHruid: loId.hruid,
|
||||||
|
@ -33,7 +33,7 @@ export class QuestionRepository extends DwengoEntityRepository<Question> {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public removeQuestionByLearningObjectAndSequenceNumber(loId: LearningObjectIdentifier, sequenceNumber: number): Promise<void> {
|
public async removeQuestionByLearningObjectAndSequenceNumber(loId: LearningObjectIdentifier, sequenceNumber: number): Promise<void> {
|
||||||
return this.deleteWhere({
|
return this.deleteWhere({
|
||||||
learningObjectHruid: loId.hruid,
|
learningObjectHruid: loId.hruid,
|
||||||
learningObjectLanguage: loId.language,
|
learningObjectLanguage: loId.language,
|
||||||
|
|
|
@ -34,7 +34,7 @@ let entityManager: EntityManager | undefined;
|
||||||
/**
|
/**
|
||||||
* Execute all the database operations within the function f in a single transaction.
|
* Execute all the database operations within the function f in a single transaction.
|
||||||
*/
|
*/
|
||||||
export function transactional<T>(f: () => Promise<T>) {
|
export function transactional<T>(f: () => Promise<T>): void {
|
||||||
entityManager?.transactional(f);
|
entityManager?.transactional(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { Student } from '../../entities/users/student.entity.js';
|
||||||
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||||
|
|
||||||
export class StudentRepository extends DwengoEntityRepository<Student> {
|
export class StudentRepository extends DwengoEntityRepository<Student> {
|
||||||
public findByUsername(username: string): Promise<Student | null> {
|
public async findByUsername(username: string): Promise<Student | null> {
|
||||||
return this.findOne({ username: username });
|
return this.findOne({ username: username });
|
||||||
}
|
}
|
||||||
public deleteByUsername(username: string): Promise<void> {
|
public async deleteByUsername(username: string): Promise<void> {
|
||||||
return this.deleteWhere({ username: username });
|
return this.deleteWhere({ username: username });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { Teacher } from '../../entities/users/teacher.entity.js';
|
||||||
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||||
|
|
||||||
export class TeacherRepository extends DwengoEntityRepository<Teacher> {
|
export class TeacherRepository extends DwengoEntityRepository<Teacher> {
|
||||||
public findByUsername(username: string): Promise<Teacher | null> {
|
public async findByUsername(username: string): Promise<Teacher | null> {
|
||||||
return this.findOne({ username: username });
|
return this.findOne({ username: username });
|
||||||
}
|
}
|
||||||
public deleteByUsername(username: string): Promise<void> {
|
public async deleteByUsername(username: string): Promise<void> {
|
||||||
return this.deleteWhere({ username: username });
|
return this.deleteWhere({ username: username });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||||
import { User } from '../../entities/users/user.entity.js';
|
import { User } from '../../entities/users/user.entity.js';
|
||||||
|
|
||||||
export class UserRepository<T extends User> extends DwengoEntityRepository<T> {
|
export class UserRepository<T extends User> extends DwengoEntityRepository<T> {
|
||||||
public findByUsername(username: string): Promise<T | null> {
|
public async findByUsername(username: string): Promise<T | null> {
|
||||||
return this.findOne({ username } as Partial<T>);
|
return this.findOne({ username } as Partial<T>);
|
||||||
}
|
}
|
||||||
public deleteByUsername(username: string): Promise<void> {
|
public async deleteByUsername(username: string): Promise<void> {
|
||||||
return this.deleteWhere({ username } as Partial<T>);
|
return this.deleteWhere({ username } as Partial<T>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,7 @@ export class LearningObjectIdentifier {
|
||||||
public hruid: string,
|
public hruid: string,
|
||||||
public language: Language,
|
public language: Language,
|
||||||
public version: number
|
public version: number
|
||||||
) {}
|
) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ function initializeLogger(): Logger {
|
||||||
level: LOG_LEVEL,
|
level: LOG_LEVEL,
|
||||||
json: true,
|
json: true,
|
||||||
format: format.combine(format.timestamp(), format.json()),
|
format: format.combine(format.timestamp(), format.json()),
|
||||||
onConnectionError: (err) => {
|
onConnectionError: (err): void => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(`Connection error: ${err}`);
|
console.error(`Connection error: ${err}`);
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { LokiLabels } from 'loki-logger-ts';
|
||||||
export class MikroOrmLogger extends DefaultLogger {
|
export class MikroOrmLogger extends DefaultLogger {
|
||||||
private logger: Logger = getLogger();
|
private logger: Logger = getLogger();
|
||||||
|
|
||||||
log(namespace: LoggerNamespace, message: string, context?: LogContext) {
|
log(namespace: LoggerNamespace, message: string, context?: LogContext): void {
|
||||||
if (!this.isEnabled(namespace, context)) {
|
if (!this.isEnabled(namespace, context)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ export class MikroOrmLogger extends DefaultLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createMessage(namespace: LoggerNamespace, messageArg: string, context?: LogContext) {
|
private createMessage(namespace: LoggerNamespace, messageArg: string, context?: LogContext): unknown {
|
||||||
const labels: LokiLabels = {
|
const labels: LokiLabels = {
|
||||||
service: 'ORM',
|
service: 'ORM',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { getLogger, Logger } from './initalize.js';
|
import { getLogger, Logger } from './initalize.js';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
export function responseTimeLogger(req: Request, res: Response, time: number) {
|
export function responseTimeLogger(req: Request, res: Response, time: number): void {
|
||||||
const logger: Logger = getLogger();
|
const logger: Logger = getLogger();
|
||||||
|
|
||||||
const method = req.method;
|
const method = req.method;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { envVars, getEnvVar } from '../../util/envVars.js';
|
import { envVars, getEnvVar } from '../../util/envVars.js';
|
||||||
import { expressjwt } from 'express-jwt';
|
import { expressjwt } from 'express-jwt';
|
||||||
|
import * as jwt from 'jsonwebtoken';
|
||||||
import { JwtPayload } from 'jsonwebtoken';
|
import { JwtPayload } from 'jsonwebtoken';
|
||||||
import jwksClient from 'jwks-rsa';
|
import jwksClient from 'jwks-rsa';
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import * as jwt from 'jsonwebtoken';
|
|
||||||
import { AuthenticatedRequest } from './authenticated-request.js';
|
import { AuthenticatedRequest } from './authenticated-request.js';
|
||||||
import { AuthenticationInfo } from './authentication-info.js';
|
import { AuthenticationInfo } from './authentication-info.js';
|
||||||
import { ForbiddenException, UnauthorizedException } from '../../exceptions.js';
|
import { ForbiddenException, UnauthorizedException } from '../../exceptions.js';
|
||||||
|
@ -74,7 +74,7 @@ const verifyJwtToken = expressjwt({
|
||||||
*/
|
*/
|
||||||
function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | undefined {
|
function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | undefined {
|
||||||
if (!req.jwtPayload) {
|
if (!req.jwtPayload) {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
const issuer = req.jwtPayload.iss;
|
const issuer = req.jwtPayload.iss;
|
||||||
let accountType: 'student' | 'teacher';
|
let accountType: 'student' | 'teacher';
|
||||||
|
@ -84,8 +84,9 @@ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo |
|
||||||
} else if (issuer === idpConfigs.teacher.issuer) {
|
} else if (issuer === idpConfigs.teacher.issuer) {
|
||||||
accountType = 'teacher';
|
accountType = 'teacher';
|
||||||
} else {
|
} else {
|
||||||
return;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountType: accountType,
|
accountType: accountType,
|
||||||
username: req.jwtPayload[JWT_PROPERTY_NAMES.username]!,
|
username: req.jwtPayload[JWT_PROPERTY_NAMES.username]!,
|
||||||
|
@ -100,10 +101,10 @@ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo |
|
||||||
* Add the AuthenticationInfo object with the information about the current authentication to the request in order
|
* Add the AuthenticationInfo object with the information about the current authentication to the request in order
|
||||||
* to avoid that the routers have to deal with the JWT token.
|
* to avoid that the routers have to deal with the JWT token.
|
||||||
*/
|
*/
|
||||||
const addAuthenticationInfo = (req: AuthenticatedRequest, _res: express.Response, next: express.NextFunction) => {
|
function addAuthenticationInfo(req: AuthenticatedRequest, _res: express.Response, next: express.NextFunction): void {
|
||||||
req.auth = getAuthenticationInfo(req);
|
req.auth = getAuthenticationInfo(req);
|
||||||
next();
|
next();
|
||||||
};
|
}
|
||||||
|
|
||||||
export const authenticateUser = [verifyJwtToken, addAuthenticationInfo];
|
export const authenticateUser = [verifyJwtToken, addAuthenticationInfo];
|
||||||
|
|
||||||
|
@ -113,9 +114,8 @@ export const authenticateUser = [verifyJwtToken, addAuthenticationInfo];
|
||||||
* @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates
|
* @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates
|
||||||
* to true.
|
* to true.
|
||||||
*/
|
*/
|
||||||
export const authorize =
|
export function authorize(accessCondition: (auth: AuthenticationInfo) => boolean) {
|
||||||
(accessCondition: (auth: AuthenticationInfo) => boolean) =>
|
return (req: AuthenticatedRequest, _res: express.Response, next: express.NextFunction): void => {
|
||||||
(req: AuthenticatedRequest, _res: express.Response, next: express.NextFunction): void => {
|
|
||||||
if (!req.auth) {
|
if (!req.auth) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
} else if (!accessCondition(req.auth)) {
|
} else if (!accessCondition(req.auth)) {
|
||||||
|
@ -124,6 +124,7 @@ export const authorize =
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware which rejects all unauthenticated users, but accepts all authenticated users.
|
* Middleware which rejects all unauthenticated users, but accepts all authenticated users.
|
||||||
|
|
|
@ -54,7 +54,7 @@ function config(testingMode: boolean = false): Options {
|
||||||
|
|
||||||
// Workaround: vitest: `TypeError: Unknown file extension ".ts"` (ERR_UNKNOWN_FILE_EXTENSION)
|
// Workaround: vitest: `TypeError: Unknown file extension ".ts"` (ERR_UNKNOWN_FILE_EXTENSION)
|
||||||
// (see https://mikro-orm.io/docs/guide/project-setup#testing-the-endpoint)
|
// (see https://mikro-orm.io/docs/guide/project-setup#testing-the-endpoint)
|
||||||
dynamicImportProvider: (id) => import(id),
|
dynamicImportProvider: async (id) => import(id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { envVars, getEnvVar } from './util/envVars.js';
|
||||||
import { getLogger, Logger } from './logging/initalize.js';
|
import { getLogger, Logger } from './logging/initalize.js';
|
||||||
|
|
||||||
let orm: MikroORM | undefined;
|
let orm: MikroORM | undefined;
|
||||||
export async function initORM(testingMode: boolean = false) {
|
export async function initORM(testingMode: boolean = false): Promise<void> {
|
||||||
const logger: Logger = getLogger();
|
const logger: Logger = getLogger();
|
||||||
|
|
||||||
logger.info('Initializing ORM');
|
logger.info('Initializing ORM');
|
||||||
|
|
|
@ -79,7 +79,7 @@ export async function getAssignmentsSubmissions(classid: string, assignmentNumbe
|
||||||
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
|
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
|
||||||
|
|
||||||
const submissionRepository = getSubmissionRepository();
|
const submissionRepository = getSubmissionRepository();
|
||||||
const submissions = (await Promise.all(groups.map((group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat();
|
const submissions = (await Promise.all(groups.map(async (group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat();
|
||||||
|
|
||||||
return submissions.map(mapToSubmissionDTO);
|
return submissions.map(mapToSubmissionDTO);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,15 @@ export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[
|
||||||
export async function createClass(classData: ClassDTO): Promise<Class | null> {
|
export async function createClass(classData: ClassDTO): Promise<Class | null> {
|
||||||
const teacherRepository = getTeacherRepository();
|
const teacherRepository = getTeacherRepository();
|
||||||
const teacherUsernames = classData.teachers || [];
|
const teacherUsernames = classData.teachers || [];
|
||||||
const teachers = (await Promise.all(teacherUsernames.map((id) => teacherRepository.findByUsername(id)))).filter((teacher) => teacher != null);
|
const teachers = (await Promise.all(teacherUsernames.map(async (id) => teacherRepository.findByUsername(id)))).filter(
|
||||||
|
(teacher) => teacher != null
|
||||||
|
);
|
||||||
|
|
||||||
const studentRepository = getStudentRepository();
|
const studentRepository = getStudentRepository();
|
||||||
const studentUsernames = classData.students || [];
|
const studentUsernames = classData.students || [];
|
||||||
const students = (await Promise.all(studentUsernames.map((id) => studentRepository.findByUsername(id)))).filter((student) => student != null);
|
const students = (await Promise.all(studentUsernames.map(async (id) => studentRepository.findByUsername(id)))).filter(
|
||||||
|
(student) => student != null
|
||||||
|
);
|
||||||
|
|
||||||
//Const cls = mapToClass(classData, teachers, students);
|
//Const cls = mapToClass(classData, teachers, students);
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,9 @@ export async function createGroup(groupData: GroupDTO, classid: string, assignme
|
||||||
const studentRepository = getStudentRepository();
|
const studentRepository = getStudentRepository();
|
||||||
|
|
||||||
const memberUsernames = (groupData.members as string[]) || []; // TODO check if groupdata.members is a list
|
const memberUsernames = (groupData.members as string[]) || []; // TODO check if groupdata.members is a list
|
||||||
const members = (await Promise.all([...memberUsernames].map((id) => studentRepository.findByUsername(id)))).filter((student) => student != null);
|
const members = (await Promise.all([...memberUsernames].map(async (id) => studentRepository.findByUsername(id)))).filter(
|
||||||
|
(student) => student != null
|
||||||
|
);
|
||||||
|
|
||||||
getLogger().debug(members);
|
getLogger().debug(members);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Attachment } from '../../entities/content/attachment.entity.js';
|
||||||
import { LearningObjectIdentifier } from '../../interfaces/learning-content.js';
|
import { LearningObjectIdentifier } from '../../interfaces/learning-content.js';
|
||||||
|
|
||||||
const attachmentService = {
|
const attachmentService = {
|
||||||
getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
|
async getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
|
||||||
const attachmentRepo = getAttachmentRepository();
|
const attachmentRepo = getAttachmentRepository();
|
||||||
|
|
||||||
if (learningObjectId.version) {
|
if (learningObjectId.version) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
|
async function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
|
||||||
const learningObjectRepo = getLearningObjectRepository();
|
const learningObjectRepo = getLearningObjectRepository();
|
||||||
|
|
||||||
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
|
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
|
||||||
|
@ -69,7 +69,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
|
||||||
if (!learningObject) {
|
if (!learningObject) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return await processingService.render(learningObject, (id) => findLearningObjectEntityById(id));
|
return await processingService.render(learningObject, async (id) => findLearningObjectEntityById(id));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +96,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
|
||||||
throw new NotFoundError('The learning path with the given ID could not be found.');
|
throw new NotFoundError('The learning path with the given ID could not be found.');
|
||||||
}
|
}
|
||||||
const learningObjects = await Promise.all(
|
const learningObjects = await Promise.all(
|
||||||
learningPath.nodes.map((it) => {
|
learningPath.nodes.map(async (it) => {
|
||||||
const learningObject = learningObjectService.getLearningObjectById({
|
const learningObject = learningObjectService.getLearningObjectById({
|
||||||
hruid: it.learningObjectHruid,
|
hruid: it.learningObjectHruid,
|
||||||
language: it.language,
|
language: it.language,
|
||||||
|
|
|
@ -18,28 +18,28 @@ const learningObjectService = {
|
||||||
/**
|
/**
|
||||||
* Fetches a single learning object by its HRUID
|
* Fetches a single learning object by its HRUID
|
||||||
*/
|
*/
|
||||||
getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
|
async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
|
||||||
return getProvider(id).getLearningObjectById(id);
|
return getProvider(id).getLearningObjectById(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch full learning object data (metadata)
|
* Fetch full learning object data (metadata)
|
||||||
*/
|
*/
|
||||||
getLearningObjectsFromPath(id: LearningPathIdentifier): Promise<FilteredLearningObject[]> {
|
async getLearningObjectsFromPath(id: LearningPathIdentifier): Promise<FilteredLearningObject[]> {
|
||||||
return getProvider(id).getLearningObjectsFromPath(id);
|
return getProvider(id).getLearningObjectsFromPath(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch only learning object HRUIDs
|
* Fetch only learning object HRUIDs
|
||||||
*/
|
*/
|
||||||
getLearningObjectIdsFromPath(id: LearningPathIdentifier): Promise<string[]> {
|
async getLearningObjectIdsFromPath(id: LearningPathIdentifier): Promise<string[]> {
|
||||||
return getProvider(id).getLearningObjectIdsFromPath(id);
|
return getProvider(id).getLearningObjectIdsFromPath(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a HTML-rendering of the learning object with the given identifier (as a string).
|
* Obtain a HTML-rendering of the learning object with the given identifier (as a string).
|
||||||
*/
|
*/
|
||||||
getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
|
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
|
||||||
return getProvider(id).getLearningObjectHTML(id);
|
return getProvider(id).getLearningObjectHTML(id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ class ExternProcessor extends StringProcessor {
|
||||||
super(DwengoContentType.EXTERN);
|
super(DwengoContentType.EXTERN);
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(externURL: string) {
|
override renderFn(externURL: string): string {
|
||||||
if (!isValidHttpUrl(externURL)) {
|
if (!isValidHttpUrl(externURL)) {
|
||||||
throw new ProcessingError('The url is not valid: ' + externURL);
|
throw new ProcessingError('The url is not valid: ' + externURL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class GiftProcessor extends StringProcessor {
|
||||||
super(DwengoContentType.GIFT);
|
super(DwengoContentType.GIFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(giftString: string) {
|
override renderFn(giftString: string): string {
|
||||||
const quizQuestions: GIFTQuestion[] = parse(giftString);
|
const quizQuestions: GIFTQuestion[] = parse(giftString);
|
||||||
|
|
||||||
let html = "<div class='learning-object-gift'>\n";
|
let html = "<div class='learning-object-gift'>\n";
|
||||||
|
|
|
@ -10,7 +10,7 @@ class BlockImageProcessor extends InlineImageProcessor {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(imageUrl: string) {
|
override renderFn(imageUrl: string): string {
|
||||||
const inlineHtml = super.render(imageUrl);
|
const inlineHtml = super.render(imageUrl);
|
||||||
return DOMPurify.sanitize(`<div>${inlineHtml}</div>`);
|
return DOMPurify.sanitize(`<div>${inlineHtml}</div>`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class InlineImageProcessor extends StringProcessor {
|
||||||
super(contentType);
|
super(contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(imageUrl: string) {
|
override renderFn(imageUrl: string): string {
|
||||||
if (!isValidHttpUrl(imageUrl)) {
|
if (!isValidHttpUrl(imageUrl)) {
|
||||||
throw new ProcessingError(`Image URL is invalid: ${imageUrl}`);
|
throw new ProcessingError(`Image URL is invalid: ${imageUrl}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ class MarkdownProcessor extends StringProcessor {
|
||||||
super(DwengoContentType.TEXT_MARKDOWN);
|
super(DwengoContentType.TEXT_MARKDOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(mdText: string) {
|
override renderFn(mdText: string): string {
|
||||||
try {
|
try {
|
||||||
marked.use({ renderer: dwengoMarkedRenderer });
|
marked.use({ renderer: dwengoMarkedRenderer });
|
||||||
const html = marked(mdText, { async: false });
|
const html = marked(mdText, { async: false });
|
||||||
|
@ -24,7 +24,7 @@ class MarkdownProcessor extends StringProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceLinks(html: string) {
|
replaceLinks(html: string): string {
|
||||||
const proc = new InlineImageProcessor();
|
const proc = new InlineImageProcessor();
|
||||||
html = html.replace(
|
html = html.replace(
|
||||||
/<img.*?src="(.*?)".*?(alt="(.*?)")?.*?(title="(.*?)")?.*?>/g,
|
/<img.*?src="(.*?)".*?(alt="(.*?)")?.*?(title="(.*?)")?.*?>/g,
|
||||||
|
|
|
@ -15,7 +15,7 @@ class PdfProcessor extends StringProcessor {
|
||||||
super(DwengoContentType.APPLICATION_PDF);
|
super(DwengoContentType.APPLICATION_PDF);
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(pdfUrl: string) {
|
override renderFn(pdfUrl: string): string {
|
||||||
if (!isValidHttpUrl(pdfUrl)) {
|
if (!isValidHttpUrl(pdfUrl)) {
|
||||||
throw new ProcessingError(`PDF URL is invalid: ${pdfUrl}`);
|
throw new ProcessingError(`PDF URL is invalid: ${pdfUrl}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ import { DwengoContentType } from './content-type.js';
|
||||||
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/processor.js
|
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/processor.js
|
||||||
*/
|
*/
|
||||||
abstract class Processor<T> {
|
abstract class Processor<T> {
|
||||||
protected constructor(public contentType: DwengoContentType) {}
|
protected constructor(public contentType: DwengoContentType) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the given object.
|
* Render the given object.
|
||||||
|
|
|
@ -11,7 +11,7 @@ class TextProcessor extends StringProcessor {
|
||||||
super(DwengoContentType.TEXT_PLAIN);
|
super(DwengoContentType.TEXT_PLAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
override renderFn(text: string) {
|
override renderFn(text: string): string {
|
||||||
// Sanitize plain text to prevent xss.
|
// Sanitize plain text to prevent xss.
|
||||||
return DOMPurify.sanitize(text);
|
return DOMPurify.sanitize(text);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise<Ma
|
||||||
// Its corresponding learning object.
|
// Its corresponding learning object.
|
||||||
const nullableNodesToLearningObjects = new Map<LearningPathNode, FilteredLearningObject | null>(
|
const nullableNodesToLearningObjects = new Map<LearningPathNode, FilteredLearningObject | null>(
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
nodes.map((node) =>
|
nodes.map(async (node) =>
|
||||||
learningObjectService
|
learningObjectService
|
||||||
.getLearningObjectById({
|
.getLearningObjectById({
|
||||||
hruid: node.learningObjectHruid,
|
hruid: node.learningObjectHruid,
|
||||||
|
@ -117,7 +117,7 @@ async function convertNodes(
|
||||||
nodesToLearningObjects: Map<LearningPathNode, FilteredLearningObject>,
|
nodesToLearningObjects: Map<LearningPathNode, FilteredLearningObject>,
|
||||||
personalizedFor?: PersonalizationTarget
|
personalizedFor?: PersonalizationTarget
|
||||||
): Promise<LearningObjectNode[]> {
|
): Promise<LearningObjectNode[]> {
|
||||||
const nodesPromise = Array.from(nodesToLearningObjects.entries()).map((entry) =>
|
const nodesPromise = Array.from(nodesToLearningObjects.entries()).map(async (entry) =>
|
||||||
convertNode(entry[0], entry[1], personalizedFor, nodesToLearningObjects)
|
convertNode(entry[0], entry[1], personalizedFor, nodesToLearningObjects)
|
||||||
);
|
);
|
||||||
return await Promise.all(nodesPromise);
|
return await Promise.all(nodesPromise);
|
||||||
|
@ -179,11 +179,11 @@ const databaseLearningPathProvider: LearningPathProvider = {
|
||||||
): Promise<LearningPathResponse> {
|
): Promise<LearningPathResponse> {
|
||||||
const learningPathRepo = getLearningPathRepository();
|
const learningPathRepo = getLearningPathRepository();
|
||||||
|
|
||||||
const learningPaths = (await Promise.all(hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)))).filter(
|
const learningPaths = (await Promise.all(hruids.map(async (hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)))).filter(
|
||||||
(learningPath) => learningPath !== null
|
(learningPath) => learningPath !== null
|
||||||
);
|
);
|
||||||
const filteredLearningPaths = await Promise.all(
|
const filteredLearningPaths = await Promise.all(
|
||||||
learningPaths.map((learningPath, index) => convertLearningPath(learningPath, index, personalizedFor))
|
learningPaths.map(async (learningPath, index) => convertLearningPath(learningPath, index, personalizedFor))
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -200,7 +200,7 @@ const databaseLearningPathProvider: LearningPathProvider = {
|
||||||
const learningPathRepo = getLearningPathRepository();
|
const learningPathRepo = getLearningPathRepository();
|
||||||
|
|
||||||
const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language);
|
const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language);
|
||||||
return await Promise.all(searchResults.map((result, index) => convertLearningPath(result, index, personalizedFor)));
|
return await Promise.all(searchResults.map(async (result, index) => convertLearningPath(result, index, personalizedFor)));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,9 @@ const learningPathService = {
|
||||||
* Search learning paths in the data source using the given search string.
|
* Search learning paths in the data source using the given search string.
|
||||||
*/
|
*/
|
||||||
async searchLearningPaths(query: string, language: Language, personalizedFor?: PersonalizationTarget): Promise<LearningPath[]> {
|
async searchLearningPaths(query: string, language: Language, personalizedFor?: PersonalizationTarget): Promise<LearningPath[]> {
|
||||||
const providerResponses = await Promise.all(allProviders.map((provider) => provider.searchLearningPaths(query, language, personalizedFor)));
|
const providerResponses = await Promise.all(
|
||||||
|
allProviders.map(async (provider) => provider.searchLearningPaths(query, language, personalizedFor))
|
||||||
|
);
|
||||||
return providerResponses.flat();
|
return providerResponses.flat();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { getAnswerRepository, getQuestionRepository } from '../data/repositories
|
||||||
import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question.js';
|
import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question.js';
|
||||||
import { Question } from '../entities/questions/question.entity.js';
|
import { Question } from '../entities/questions/question.entity.js';
|
||||||
import { Answer } from '../entities/questions/answer.entity.js';
|
import { Answer } from '../entities/questions/answer.entity.js';
|
||||||
import { mapToAnswerDTO, mapToAnswerId } from '../interfaces/answer.js';
|
import { AnswerDTO, AnswerId, mapToAnswerDTO, mapToAnswerId } from '../interfaces/answer.js';
|
||||||
import { QuestionRepository } from '../data/questions/question-repository.js';
|
import { QuestionRepository } from '../data/questions/question-repository.js';
|
||||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||||
import { mapToStudent } from '../interfaces/student.js';
|
import { mapToStudent } from '../interfaces/student.js';
|
||||||
|
@ -45,7 +45,7 @@ export async function getQuestion(questionId: QuestionId): Promise<QuestionDTO |
|
||||||
return mapToQuestionDTO(question);
|
return mapToQuestionDTO(question);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAnswersByQuestion(questionId: QuestionId, full: boolean) {
|
export async function getAnswersByQuestion(questionId: QuestionId, full: boolean): Promise<AnswerDTO[] | AnswerId[]> {
|
||||||
const answerRepository = getAnswerRepository();
|
const answerRepository = getAnswerRepository();
|
||||||
const question = await fetchQuestion(questionId);
|
const question = await fetchQuestion(questionId);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ export async function getAnswersByQuestion(questionId: QuestionId, full: boolean
|
||||||
return answersDTO.map(mapToAnswerId);
|
return answersDTO.map(mapToAnswerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createQuestion(questionDTO: QuestionDTO) {
|
export async function createQuestion(questionDTO: QuestionDTO): Promise<QuestionDTO | null> {
|
||||||
const questionRepository = getQuestionRepository();
|
const questionRepository = getQuestionRepository();
|
||||||
|
|
||||||
const author = mapToStudent(questionDTO.author);
|
const author = mapToStudent(questionDTO.author);
|
||||||
|
@ -86,7 +86,7 @@ export async function createQuestion(questionDTO: QuestionDTO) {
|
||||||
return questionDTO;
|
return questionDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteQuestion(questionId: QuestionId) {
|
export async function deleteQuestion(questionId: QuestionId): Promise<Question | null> {
|
||||||
const questionRepository = getQuestionRepository();
|
const questionRepository = getQuestionRepository();
|
||||||
|
|
||||||
const question = await fetchQuestion(questionId);
|
const question = await fetchQuestion(questionId);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { getSubmissionRepository } from '../data/repositories.js';
|
||||||
import { Language } from '../entities/content/language.js';
|
import { Language } from '../entities/content/language.js';
|
||||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||||
import { mapToSubmission, mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
|
import { mapToSubmission, mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
|
||||||
|
import { Submission } from '../entities/assignments/submission.entity.js';
|
||||||
|
|
||||||
export async function getSubmission(
|
export async function getSubmission(
|
||||||
learningObjectHruid: string,
|
learningObjectHruid: string,
|
||||||
|
@ -21,7 +22,7 @@ export async function getSubmission(
|
||||||
return mapToSubmissionDTO(submission);
|
return mapToSubmissionDTO(submission);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createSubmission(submissionDTO: SubmissionDTO) {
|
export async function createSubmission(submissionDTO: SubmissionDTO): Promise<Submission | null> {
|
||||||
const submissionRepository = getSubmissionRepository();
|
const submissionRepository = getSubmissionRepository();
|
||||||
const submission = mapToSubmission(submissionDTO);
|
const submission = mapToSubmission(submissionDTO);
|
||||||
|
|
||||||
|
@ -35,7 +36,12 @@ export async function createSubmission(submissionDTO: SubmissionDTO) {
|
||||||
return submission;
|
return submission;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteSubmission(learningObjectHruid: string, language: Language, version: number, submissionNumber: number) {
|
export async function deleteSubmission(
|
||||||
|
learningObjectHruid: string,
|
||||||
|
language: Language,
|
||||||
|
version: number,
|
||||||
|
submissionNumber: number
|
||||||
|
): Promise<SubmissionDTO | null> {
|
||||||
const submissionRepository = getSubmissionRepository();
|
const submissionRepository = getSubmissionRepository();
|
||||||
|
|
||||||
const submission = getSubmission(learningObjectHruid, language, version, submissionNumber);
|
const submission = getSubmission(learningObjectHruid, language, version, submissionNumber);
|
||||||
|
|
|
@ -77,7 +77,7 @@ export async function getClassIdsByTeacher(username: string): Promise<string[]>
|
||||||
return classes.map((cls) => cls.id);
|
return classes.map((cls) => cls.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchStudentsByTeacher(username: string) {
|
export async function fetchStudentsByTeacher(username: string): Promise<StudentDTO[]> {
|
||||||
const classes = await getClassIdsByTeacher(username);
|
const classes = await getClassIdsByTeacher(username);
|
||||||
|
|
||||||
return (await Promise.all(classes.map(async (id) => getClassStudents(id)))).flat();
|
return (await Promise.all(classes.map(async (id) => getClassStudents(id)))).flat();
|
||||||
|
|
|
@ -6,7 +6,11 @@
|
||||||
* @param regex
|
* @param regex
|
||||||
* @param replacementFn
|
* @param replacementFn
|
||||||
*/
|
*/
|
||||||
export async function replaceAsync(str: string, regex: RegExp, replacementFn: (match: string, ...args: string[]) => Promise<string>) {
|
export async function replaceAsync(
|
||||||
|
str: string,
|
||||||
|
regex: RegExp,
|
||||||
|
replacementFn: (match: string, ...args: string[]) => Promise<string>
|
||||||
|
): Promise<string> {
|
||||||
const promises: Promise<string>[] = [];
|
const promises: Promise<string>[] = [];
|
||||||
|
|
||||||
// First run through matches: add all Promises resulting from the replacement function
|
// First run through matches: add all Promises resulting from the replacement function
|
||||||
|
|
|
@ -9,7 +9,7 @@ export function isValidHttpUrl(url: string): boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUrlStringForLearningObject(learningObjectId: LearningObjectIdentifier) {
|
export function getUrlStringForLearningObject(learningObjectId: LearningObjectIdentifier): string {
|
||||||
let url = `/learningObject/${learningObjectId.hruid}/html?language=${learningObjectId.language}`;
|
let url = `/learningObject/${learningObjectId.hruid}/html?language=${learningObjectId.language}`;
|
||||||
if (learningObjectId.version) {
|
if (learningObjectId.version) {
|
||||||
url += `&version=${learningObjectId.version}`;
|
url += `&version=${learningObjectId.version}`;
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe('DatabaseLearningObjectProvider', () => {
|
||||||
});
|
});
|
||||||
it('should throw an error if queried with a path identifier for which there is no learning path', async () => {
|
it('should throw an error if queried with a path identifier for which there is no learning path', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
await databaseLearningObjectProvider.getLearningObjectIdsFromPath({
|
await databaseLearningObjectProvider.getLearningObjectIdsFromPath({
|
||||||
hruid: 'non_existing_hruid',
|
hruid: 'non_existing_hruid',
|
||||||
language: Language.Dutch,
|
language: Language.Dutch,
|
||||||
|
@ -97,7 +97,7 @@ describe('DatabaseLearningObjectProvider', () => {
|
||||||
});
|
});
|
||||||
it('should throw an error if queried with a path identifier for which there is no learning path', async () => {
|
it('should throw an error if queried with a path identifier for which there is no learning path', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
await databaseLearningObjectProvider.getLearningObjectsFromPath({
|
await databaseLearningObjectProvider.getLearningObjectsFromPath({
|
||||||
hruid: 'non_existing_hruid',
|
hruid: 'non_existing_hruid',
|
||||||
language: Language.Dutch,
|
language: Language.Dutch,
|
||||||
|
|
|
@ -124,7 +124,7 @@ describe('DatabaseLearningPathProvider', () => {
|
||||||
|
|
||||||
const learningObjectsOnPath = (
|
const learningObjectsOnPath = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
example.learningPath.nodes.map((node) =>
|
example.learningPath.nodes.map(async (node) =>
|
||||||
learningObjectService.getLearningObjectById({
|
learningObjectService.getLearningObjectById({
|
||||||
hruid: node.learningObjectHruid,
|
hruid: node.learningObjectHruid,
|
||||||
version: node.version,
|
version: node.version,
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { makeTestQuestions } from './test_assets/questions/questions.testdata.js
|
||||||
import { makeTestAnswers } from './test_assets/questions/answers.testdata.js';
|
import { makeTestAnswers } from './test_assets/questions/answers.testdata.js';
|
||||||
import { makeTestSubmissions } from './test_assets/assignments/submission.testdata.js';
|
import { makeTestSubmissions } from './test_assets/assignments/submission.testdata.js';
|
||||||
|
|
||||||
export async function setupTestApp() {
|
export async function setupTestApp(): Promise<void> {
|
||||||
dotenv.config({ path: '.env.test' });
|
dotenv.config({ path: '.env.test' });
|
||||||
await initORM(true);
|
await initORM(true);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { envVars, getEnvVar } from '../../../../src/util/envVars';
|
||||||
*/
|
*/
|
||||||
export function dummyLearningObject(hruid: string, language: Language, title: string): LearningObjectExample {
|
export function dummyLearningObject(hruid: string, language: Language, title: string): LearningObjectExample {
|
||||||
return {
|
return {
|
||||||
createLearningObject: () => {
|
createLearningObject: (): LearningObject => {
|
||||||
const learningObject = new LearningObject();
|
const learningObject = new LearningObject();
|
||||||
learningObject.hruid = getEnvVar(envVars.UserContentPrefix) + hruid;
|
learningObject.hruid = getEnvVar(envVars.UserContentPrefix) + hruid;
|
||||||
learningObject.language = language;
|
learningObject.language = language;
|
||||||
|
|
|
@ -3,7 +3,12 @@ import { LearningPathTransition } from '../../../src/entities/content/learning-p
|
||||||
import { LearningPathNode } from '../../../src/entities/content/learning-path-node.entity';
|
import { LearningPathNode } from '../../../src/entities/content/learning-path-node.entity';
|
||||||
import { LearningPath } from '../../../src/entities/content/learning-path.entity';
|
import { LearningPath } from '../../../src/entities/content/learning-path.entity';
|
||||||
|
|
||||||
export function createLearningPathTransition(node: LearningPathNode, transitionNumber: number, condition: string | null, to: LearningPathNode) {
|
export function createLearningPathTransition(
|
||||||
|
node: LearningPathNode,
|
||||||
|
transitionNumber: number,
|
||||||
|
condition: string | null,
|
||||||
|
to: LearningPathNode
|
||||||
|
): LearningPathTransition {
|
||||||
const trans = new LearningPathTransition();
|
const trans = new LearningPathTransition();
|
||||||
trans.node = node;
|
trans.node = node;
|
||||||
trans.transitionNumber = transitionNumber;
|
trans.transitionNumber = transitionNumber;
|
||||||
|
@ -19,7 +24,7 @@ export function createLearningPathNode(
|
||||||
version: number,
|
version: number,
|
||||||
language: Language,
|
language: Language,
|
||||||
startNode: boolean
|
startNode: boolean
|
||||||
) {
|
): LearningPathNode {
|
||||||
const node = new LearningPathNode();
|
const node = new LearningPathNode();
|
||||||
node.learningPath = learningPath;
|
node.learningPath = learningPath;
|
||||||
node.nodeNumber = nodeNumber;
|
node.nodeNumber = nodeNumber;
|
||||||
|
|
|
@ -69,7 +69,7 @@ export function expectToBeCorrectEntity<T extends object>(actual: { entity: T; n
|
||||||
* @param filtered the representation as FilteredLearningObject
|
* @param filtered the representation as FilteredLearningObject
|
||||||
* @param original the original entity added to the database
|
* @param original the original entity added to the database
|
||||||
*/
|
*/
|
||||||
export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearningObject, original: LearningObject) {
|
export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearningObject, original: LearningObject): void {
|
||||||
expect(filtered.uuid).toEqual(original.uuid);
|
expect(filtered.uuid).toEqual(original.uuid);
|
||||||
expect(filtered.version).toEqual(original.version);
|
expect(filtered.version).toEqual(original.version);
|
||||||
expect(filtered.language).toEqual(original.language);
|
expect(filtered.language).toEqual(original.language);
|
||||||
|
@ -105,7 +105,7 @@ export function expectToBeCorrectLearningPath(
|
||||||
learningPath: LearningPath,
|
learningPath: LearningPath,
|
||||||
expectedEntity: LearningPathEntity,
|
expectedEntity: LearningPathEntity,
|
||||||
learningObjectsOnPath: FilteredLearningObject[]
|
learningObjectsOnPath: FilteredLearningObject[]
|
||||||
) {
|
): void {
|
||||||
expect(learningPath.hruid).toEqual(expectedEntity.hruid);
|
expect(learningPath.hruid).toEqual(expectedEntity.hruid);
|
||||||
expect(learningPath.language).toEqual(expectedEntity.language);
|
expect(learningPath.language).toEqual(expectedEntity.language);
|
||||||
expect(learningPath.description).toEqual(expectedEntity.description);
|
expect(learningPath.description).toEqual(expectedEntity.description);
|
||||||
|
|
|
@ -23,11 +23,20 @@ export default [
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
linterOptions: {
|
linterOptions: {
|
||||||
reportUnusedInlineConfigs: 'error',
|
reportUnusedInlineConfigs: 'error',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
'consistent-return': 'off',
|
||||||
|
'@typescript-eslint/consistent-return': 'off',
|
||||||
|
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'warn',
|
||||||
|
|
||||||
'@typescript-eslint/naming-convention': [
|
'@typescript-eslint/naming-convention': [
|
||||||
'warn',
|
'warn',
|
||||||
{ // Enforce that all variables, functions and properties are camelCase
|
{ // Enforce that all variables, functions and properties are camelCase
|
||||||
|
@ -49,6 +58,14 @@ export default [
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// 'no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'error',
|
||||||
|
|
||||||
|
'no-loop-func': 'off',
|
||||||
|
'@typescript-eslint/no-loop-func': 'error',
|
||||||
|
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'error',
|
||||||
|
|
||||||
'no-unused-expressions': 'off',
|
'no-unused-expressions': 'off',
|
||||||
'@typescript-eslint/no-unused-expressions': 'warn',
|
'@typescript-eslint/no-unused-expressions': 'warn',
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
|
@ -66,6 +83,10 @@ export default [
|
||||||
'no-use-before-define': 'off',
|
'no-use-before-define': 'off',
|
||||||
'@typescript-eslint/no-use-before-define': 'off',
|
'@typescript-eslint/no-use-before-define': 'off',
|
||||||
|
|
||||||
|
'@typescript-eslint/prefer-function-type': 'error',
|
||||||
|
|
||||||
|
'@typescript-eslint/promise-function-async': 'warn',
|
||||||
|
|
||||||
'no-await-in-loop': 'warn',
|
'no-await-in-loop': 'warn',
|
||||||
'no-constructor-return': 'error',
|
'no-constructor-return': 'error',
|
||||||
'no-duplicate-imports': 'error',
|
'no-duplicate-imports': 'error',
|
||||||
|
@ -110,7 +131,6 @@ export default [
|
||||||
'no-iterator': 'error',
|
'no-iterator': 'error',
|
||||||
'no-label-var': 'warn',
|
'no-label-var': 'warn',
|
||||||
'no-labels': 'warn',
|
'no-labels': 'warn',
|
||||||
'no-loop-func': 'error',
|
|
||||||
'no-multi-assign': 'error',
|
'no-multi-assign': 'error',
|
||||||
'no-nested-ternary': 'error',
|
'no-nested-ternary': 'error',
|
||||||
'no-object-constructor': 'error',
|
'no-object-constructor': 'error',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue