fix(backend): Merge-conflicten opgelost.

This commit is contained in:
Gerald Schmittinger 2025-04-09 19:51:15 +02:00
parent 8c387d6811
commit fb3c37ce5a
8 changed files with 79 additions and 49 deletions

View file

@ -7,11 +7,12 @@ import {
getQuestion,
getQuestionsAboutLearningObjectInAssignment, updateQuestion,
} from '../services/questions.js';
import { FALLBACK_LANG, FALLBACK_SEQ_NUM } from '../config.js';
import {FALLBACK_LANG, FALLBACK_SEQ_NUM, FALLBACK_VERSION_NUM} from '../config.js';
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
import { QuestionData, QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
import { Language } from '@dwengo-1/common/util/language';
import {requireFields} from "./error-helper";
import {BadRequestException} from "../exceptions/bad-request-exception";
export function getLearningObjectId(hruid: string, version: string, lang: string): LearningObjectIdentifier {
return {
@ -28,7 +29,7 @@ export function getQuestionId(learningObjectIdentifier: LearningObjectIdentifier
};
}
function getQuestionId(req: Request, res: Response): QuestionId | null {
function getQuestionIdFromRequest(req: Request): QuestionId | null {
const seq = req.params.seq;
const hruid = req.params.hruid;
const version = req.params.version;
@ -39,10 +40,7 @@ function getQuestionId(req: Request, res: Response): QuestionId | null {
return null;
}
return {
learningObjectIdentifier,
sequenceNumber: seq ? Number(seq) : FALLBACK_SEQ_NUM,
};
return getQuestionId(learningObjectIdentifier, seq);
}
export async function getAllQuestionsHandler(req: Request, res: Response): Promise<void> {
@ -52,16 +50,22 @@ export async function getAllQuestionsHandler(req: Request, res: Response): Promi
const full = req.query.full === 'true';
requireFields({ hruid });
const assignmentId = parseInt(req.query.assignmentId as string);
if (isNaN(assignmentId)) {
throw new BadRequestException("The assignment ID must be a number.");
}
const learningObjectId = getLearningObjectId(hruid, version, language);
let questions: QuestionDTO[] | QuestionId[];
if (req.query.classId && req.query.assignmentId) {
questions = await getQuestionsAboutLearningObjectInAssignment(
learningObjectId,
req.query.classId,
req.query.assignmentId,
req.query.classId as string,
parseInt(req.query.assignmentId as string),
full ?? false,
req.query.forStudent
req.query.forStudent as string | undefined
);
} else {
questions = await getAllQuestions(learningObjectId, full ?? false);
@ -70,40 +74,37 @@ export async function getAllQuestionsHandler(req: Request, res: Response): Promi
res.json({ questions });
}
export async function getQuestionHandler(req: Request, res: Response): Promise<void> {
const hruid = req.params.hruid;
const version = req.params.version;
const language = req.query.lang as string;
const seq = req.params.seq;
requireFields({ hruid });
export async function getQuestionHandler(req: Request, res: Response): Promise<void> {
const hruid = req.params.hruid;
const version = req.params.version;
const language = req.query.lang as string;
const seq = req.params.seq;
requireFields({ hruid });
const learningObjectId = getLearningObjectId(hruid, version, language);
const questionId = getQuestionId(learningObjectId, seq);
const learningObjectId = getLearningObjectId(hruid, version, language);
const questionId = getQuestionId(learningObjectId, seq);
const question = await getQuestion(questionId);
const question = await getQuestion(questionId);
res.json({ question });
res.json({ question });
}
export async function getQuestionAnswersHandler(req: Request, res: Response): Promise<void> {
const questionId = getQuestionIdFromRequest(req);
const full = req.query.full;
if (!questionId) {
return;
}
export async function getQuestionAnswersHandler(
req: Request<GetQuestionIdPathParams, { answers: AnswerDTO[] | AnswerId[] }, unknown, GetQuestionAnswersQueryParams>,
res: Response
): Promise<void> {
const questionId = getQuestionId(req, res);
const full = req.query.full;
const answers = await getAnswersByQuestion(questionId, full === "true");
if (!questionId) {
return;
}
const answers = await getAnswersByQuestion(questionId, full);
if (!answers) {
res.status(404).json({ error: `Questions not found` });
} else {
res.json({ answers: answers });
}
if (!answers) {
res.status(404).json({ error: `Questions not found` });
} else {
res.json({ answers: answers });
}
}
export async function createQuestionHandler(req: Request, res: Response): Promise<void> {
const hruid = req.params.hruid;

View file

@ -1,9 +1,9 @@
import { Question } from '../entities/questions/question.entity.js';
import { mapToStudentDTO } from './student.js';
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content';
import { mapToGroupDTOId } from './group';
import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content';
import {LearningObjectIdentifier} from "../entities/content/learning-object-identifier";
function getLearningObjectIdentifier(question: Question): LearningObjectIdentifierDTO {
return {

View file

@ -0,0 +1,23 @@
import {authorize} from "./auth-checks";
import {AuthenticationInfo} from "../authentication-info";
import {AuthenticatedRequest} from "../authenticated-request";
import {getGroup} from "../../../services/groups";
/**
* Only allows requests whose learning path personalization query parameters ('forGroup' / 'assignmentNo' / 'classId')
* are
* - either not set
* - or set to a group the user is in,
* - or set to anything if the user is a teacher.
*/
export const onlyAllowPersonalizationForOwnGroup = authorize(
async (auth: AuthenticationInfo, req: AuthenticatedRequest) => {
const {forGroup, assignmentNo, classId} = req.params;
if (forGroup && assignmentNo && classId) {
const group = getGroup(forGroup, parseInt(assignmentNo), classId, false);
} else {
return true;
}
}
);

View file

@ -1,13 +1,15 @@
import express from 'express';
import { createGroupHandler, getAllGroupsHandler, getGroupHandler, getGroupSubmissionsHandler } from '../controllers/groups.js';
import {onlyAllowIfHasAccessToGroup} from "../middleware/auth/checks/group-auth-checker";
import {teachersOnly} from "../middleware/auth/checks/auth-checks";
import {onlyAllowIfHasAccessToAssignment} from "../middleware/auth/checks/assignment-auth-checks";
const router = express.Router({ mergeParams: true });
// Root endpoint used to search objects
router.get('/', getAllGroupsHandler);
router.get('/', onlyAllowIfHasAccessToAssignment, getAllGroupsHandler);
router.post('/', createGroupHandler);
router.post('/', teachersOnly, onlyAllowIfHasAccessToAssignment, createGroupHandler);
// Information about a group (members, ... [TODO DOC])
router.get('/:groupid', onlyAllowIfHasAccessToGroup, getGroupHandler);

View file

@ -3,6 +3,7 @@ import { getAllLearningObjects, getAttachment, getLearningObject, getLearningObj
import submissionRoutes from './submissions.js';
import questionRoutes from './questions.js';
import {authenticatedOnly} from "../middleware/auth/checks/auth-checks";
const router = express.Router();
@ -16,13 +17,13 @@ const router = express.Router();
// Route 2: list of object data
// Example 2: http://localhost:3000/learningObject?full=true&hruid=un_artificiele_intelligentie
router.get('/', getAllLearningObjects);
router.get('/', authenticatedOnly, getAllLearningObjects);
// Parameter: hruid of learning object
// Query: language
// Route to fetch data of one learning object based on its hruid
// Example: http://localhost:3000/learningObject/un_ai7
router.get('/:hruid', getLearningObject);
router.get('/:hruid', authenticatedOnly, getLearningObject);
router.use('/:hruid/submissions', submissionRoutes);
@ -32,12 +33,12 @@ router.use('/:hruid/:version/questions', questionRoutes);
// Query: language, version (optional)
// Route to fetch the HTML rendering of one learning object based on its hruid.
// Example: http://localhost:3000/learningObject/un_ai7/html
router.get('/:hruid/html', getLearningObjectHTML);
router.get('/:hruid/html', authenticatedOnly, getLearningObjectHTML);
// Parameter: hruid of learning object, name of attachment.
// Query: language, version (optional).
// Route to get the raw data of the attachment for one learning object based on its hruid.
// Example: http://localhost:3000/learningObject/u_test/attachment/testimage.png
router.get('/:hruid/html/:attachmentName', getAttachment);
router.get('/:hruid/html/:attachmentName', authenticatedOnly, getAttachment);
export default router;

View file

@ -1,5 +1,6 @@
import express from 'express';
import { getLearningPaths } from '../controllers/learning-paths.js';
import {authenticatedOnly} from "../middleware/auth/checks/auth-checks";
const router = express.Router();
@ -22,6 +23,6 @@ const router = express.Router();
// Route to fetch learning paths based on a theme
// Example: http://localhost:3000/learningPath?theme=kiks
router.get('/', getLearningPaths);
router.get('/', authenticatedOnly, getLearningPaths);
export default router;

View file

@ -11,11 +11,13 @@ import { mapToAnswerDTO, mapToAnswerDTOId } from '../interfaces/answer.js';
import { QuestionRepository } from '../data/questions/question-repository.js';
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
import { mapToStudent } from '../interfaces/student.js';
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
import {QuestionData, QuestionDTO, QuestionId} from '@dwengo-1/common/interfaces/question';
import { AnswerDTO, AnswerId } from '@dwengo-1/common/interfaces/answer';
import {fetchStudent} from "./students";
import {mapToAssignment} from "../interfaces/assignment";
import { NotFoundException } from '../exceptions/not-found-exception.js';
import {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment";
import {FALLBACK_VERSION_NUM} from "../config";
export async function getQuestionsAboutLearningObjectInAssignment(
loId: LearningObjectIdentifier,
@ -90,10 +92,9 @@ export async function createQuestion(loId: LearningObjectIdentifier, questionDat
const author = await fetchStudent(questionData.author!);
const content = questionData.content;
const clazz = await getClassRepository().findById((questionDTO.inGroup.assignment as AssignmentDTO).class);
let questionDTO;
const assignment = mapToAssignment(questionDTO.inGroup.assignment as AssignmentDTO, clazz!);
const inGroup = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionDTO.inGroup.groupNumber);
const clazz = await getClassRepository().findById((questionData.inGroup.assignment as AssignmentDTO).class);
const assignment = mapToAssignment(questionData.inGroup.assignment as AssignmentDTO, clazz!);
const inGroup = (await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionData.inGroup.groupNumber))!;
const question = await questionRepository.createQuestion({
loId,

View file

@ -13,6 +13,7 @@ export interface QuestionDTO {
export interface QuestionData {
author?: string;
inGroup: GroupDTO;
content: string;
}