Merge remote-tracking branch 'origin/dev' into fix/class-join-request

# Conflicts:
#	backend/src/controllers/classes.ts
#	backend/src/data/questions/question-repository.ts
#	backend/tests/controllers/students.test.ts
#	backend/tests/controllers/teachers.test.ts
This commit is contained in:
Gabriellvl 2025-04-09 12:13:42 +02:00
commit 0a0857deb9
58 changed files with 2058 additions and 277 deletions

View file

@ -0,0 +1,39 @@
import type { AnswerData, AnswerDTO, AnswerId } from "@dwengo-1/common/interfaces/answer";
import { BaseController } from "@/controllers/base-controller.ts";
import type { QuestionId } from "@dwengo-1/common/interfaces/question";
export interface AnswersResponse {
answers: AnswerDTO[] | AnswerId[];
}
export interface AnswerResponse {
answer: AnswerDTO;
}
export class AnswerController extends BaseController {
constructor(questionId: QuestionId) {
this.loId = questionId.learningObjectIdentifier;
this.sequenceNumber = questionId.sequenceNumber;
super(`learningObject/${loId.hruid}/:${loId.version}/questions/${this.sequenceNumber}/answers`);
}
async getAll(full = true): Promise<AnswersResponse> {
return this.get<AnswersResponse>("/", { lang: this.loId.lang, full });
}
async getBy(seq: number): Promise<AnswerResponse> {
return this.get<AnswerResponse>(`/${seq}`, { lang: this.loId.lang });
}
async create(answerData: AnswerData): Promise<AnswerResponse> {
return this.post<AnswerResponse>("/", answerData, { lang: this.loId.lang });
}
async remove(seq: number): Promise<AnswerResponse> {
return this.delete<AnswerResponse>(`/${seq}`, { lang: this.loId.lang });
}
async update(seq: number, answerData: AnswerData): Promise<AnswerResponse> {
return this.put<AnswerResponse>(`/${seq}`, answerData, { lang: this.loId.lang });
}
}

View file

@ -10,7 +10,7 @@ export abstract class BaseController {
}
private static assertSuccessResponse(response: AxiosResponse<unknown, unknown>): void {
if (response.status / 100 !== 2) {
if (response.status < 200 || response.status >= 300) {
throw new HttpErrorResponseException(response);
}
}
@ -21,20 +21,20 @@ export abstract class BaseController {
return response.data;
}
protected async post<T>(path: string, body: unknown): Promise<T> {
const response = await apiClient.post<T>(this.absolutePathFor(path), body);
protected async post<T>(path: string, body: unknown, queryParams?: QueryParams): Promise<T> {
const response = await apiClient.post<T>(this.absolutePathFor(path), body, { params: queryParams });
BaseController.assertSuccessResponse(response);
return response.data;
}
protected async delete<T>(path: string): Promise<T> {
const response = await apiClient.delete<T>(this.absolutePathFor(path));
protected async delete<T>(path: string, queryParams?: QueryParams): Promise<T> {
const response = await apiClient.delete<T>(this.absolutePathFor(path), { params: queryParams });
BaseController.assertSuccessResponse(response);
return response.data;
}
protected async put<T>(path: string, body: unknown): Promise<T> {
const response = await apiClient.put<T>(this.absolutePathFor(path), body);
protected async put<T>(path: string, body: unknown, queryParams?: QueryParams): Promise<T> {
const response = await apiClient.put<T>(this.absolutePathFor(path), body, { params: queryParams });
BaseController.assertSuccessResponse(response);
return response.data;
}

View file

@ -1,6 +1,7 @@
import { ThemeController } from "@/controllers/themes.ts";
import { LearningObjectController } from "@/controllers/learning-objects.ts";
import { LearningPathController } from "@/controllers/learning-paths.ts";
import { ClassController } from "@/controllers/classes.ts";
export function controllerGetter<T>(factory: new () => T): () => T {
let instance: T | undefined;
@ -16,3 +17,4 @@ export function controllerGetter<T>(factory: new () => T): () => T {
export const getThemeController = controllerGetter(ThemeController);
export const getLearningObjectController = controllerGetter(LearningObjectController);
export const getLearningPathController = controllerGetter(LearningPathController);
export const getClassController = controllerGetter(ClassController);

View file

@ -1,5 +1,38 @@
import type { QuestionDTO, QuestionId } from "@dwengo-1/common/interfaces/question";
import type { QuestionData, QuestionDTO, QuestionId } from "@dwengo-1/common/interfaces/question";
import { BaseController } from "@/controllers/base-controller.ts";
import type { LearningObjectIdentifierDTO } from "@dwengo-1/common/interfaces/learning-content";
export interface QuestionsResponse {
questions: QuestionDTO[] | QuestionId[];
}
export interface QuestionResponse {
question: QuestionDTO;
}
export class QuestionController extends BaseController {
constructor(loId: LearningObjectIdentifierDTO) {
this.loId = loId;
super(`learningObject/${loId.hruid}/:${loId.version}/questions`);
}
async getAll(full = true): Promise<QuestionsResponse> {
return this.get<QuestionsResponse>("/", { lang: this.loId.lang, full });
}
async getBy(sequenceNumber: number): Promise<QuestionResponse> {
return this.get<QuestionResponse>(`/${sequenceNumber}`, { lang: this.loId.lang });
}
async create(questionData: QuestionData): Promise<QuestionResponse> {
return this.post<QuestionResponse>("/", questionData, { lang: this.loId.lang });
}
async remove(sequenceNumber: number): Promise<QuestionResponse> {
return this.delete<QuestionResponse>(`/${sequenceNumber}`, { lang: this.loId.lang });
}
async update(sequenceNumber: number, questionData: QuestionData): Promise<QuestionResponse> {
return this.put<QuestionResponse>(`/${sequenceNumber}`, questionData, { lang: this.loId.lang });
}
}

View file

@ -70,7 +70,7 @@ export class StudentController extends BaseController {
}
async createJoinRequest(username: string, classId: string): Promise<JoinRequestResponse> {
return this.post<JoinRequestResponse>(`/${username}/joinRequests}`, classId);
return this.post<JoinRequestResponse>(`/${username}/joinRequests`, { classId });
}
async deleteJoinRequest(username: string, classId: string): Promise<JoinRequestResponse> {