refactor(backend): Types

This commit is contained in:
Tibo De Peuter 2025-03-23 11:14:32 +01:00
parent 6ad7fbf208
commit 25f9eb2af2
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
31 changed files with 92 additions and 86 deletions

View file

@ -1,16 +1,16 @@
import { envVars, getEnvVar } from '../util/envVars.js';
type FrontendIdpConfig = {
interface FrontendIdpConfig {
authority: string;
clientId: string;
scope: string;
responseType: string;
};
}
type FrontendAuthConfig = {
interface FrontendAuthConfig {
student: FrontendIdpConfig;
teacher: FrontendIdpConfig;
};
}
const SCOPE = 'openid profile email';
const RESPONSE_TYPE = 'code';

View file

@ -13,7 +13,7 @@ function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIde
throw new BadRequestException('HRUID is required.');
}
return {
hruid: req.params.hruid as string,
hruid: req.params.hruid,
language: (req.query.language || getEnvVar(envVars.FallbackLanguage)) as Language,
version: parseInt(req.query.version as string),
};
@ -24,7 +24,7 @@ function getLearningPathIdentifierFromRequest(req: Request): LearningPathIdentif
throw new BadRequestException('HRUID is required.');
}
return {
hruid: req.params.hruid as string,
hruid: req.params.hruid,
language: (req.query.language as Language) || FALLBACK_LANG,
};
}

View file

@ -85,7 +85,7 @@ export async function deleteTeacherHandler(req: Request, res: Response): Promise
export async function getTeacherClassHandler(req: Request, res: Response): Promise<void> {
try {
const username = req.params.username as string;
const username = req.params.username;
const full = req.query.full === 'true';
if (!username) {
@ -104,7 +104,7 @@ export async function getTeacherClassHandler(req: Request, res: Response): Promi
export async function getTeacherStudentHandler(req: Request, res: Response): Promise<void> {
try {
const username = req.params.username as string;
const username = req.params.username;
const full = req.query.full === 'true';
if (!username) {
@ -123,7 +123,7 @@ export async function getTeacherStudentHandler(req: Request, res: Response): Pro
export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> {
try {
const username = req.params.username as string;
const username = req.params.username;
const full = req.query.full === 'true';
if (!username) {

View file

@ -24,7 +24,7 @@ export async function getAllUsersHandler<T extends User>(req: Request, res: Resp
export async function getUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
try {
const username = req.params.username as string;
const username = req.params.username;
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });

View file

@ -58,7 +58,7 @@ export interface EducationalGoal {
export interface ReturnValue {
callback_url: string;
callback_schema: Record<string, any>;
callback_schema: Record<string, unknown>;
}
export interface LearningObjectMetadata {

View file

@ -1,11 +1,11 @@
/**
* Object with information about the user who is currently logged in.
*/
export type AuthenticationInfo = {
export interface AuthenticationInfo {
accountType: 'student' | 'teacher';
username: string;
name?: string;
firstName?: string;
lastName?: string;
email?: string;
};
}

View file

@ -1,7 +1,6 @@
import { LearningObjectProvider } from './learning-object-provider.js';
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content.js';
import { getLearningObjectRepository, getLearningPathRepository } from '../../data/repositories.js';
import { Language } from '../../entities/content/language.js';
import { LearningObject } from '../../entities/content/learning-object.entity.js';
import { getUrlStringForLearningObject } from '../../util/links.js';
import processingService from './processing/processing-service.js';
@ -44,7 +43,7 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL
async function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
const learningObjectRepo = getLearningObjectRepository();
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language);
}
/**
@ -65,7 +64,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
const learningObjectRepo = getLearningObjectRepository();
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language);
if (!learningObject) {
return null;
}

View file

@ -8,6 +8,7 @@ import { DwengoContentType } from '../content-type.js';
import dwengoMarkedRenderer from './dwengo-marked-renderer.js';
import { StringProcessor } from '../string-processor.js';
import { ProcessingError } from '../processing-error.js';
import { YAMLException } from 'js-yaml';
class MarkdownProcessor extends StringProcessor {
constructor() {
@ -19,8 +20,12 @@ class MarkdownProcessor extends StringProcessor {
marked.use({ renderer: dwengoMarkedRenderer });
const html = marked(mdText, { async: false });
return this.replaceLinks(html); // Replace html image links path
} catch (e: any) {
throw new ProcessingError(e.message);
} catch (e: unknown) {
if (e instanceof YAMLException) {
throw new ProcessingError(e.message);
}
throw new ProcessingError('Unknown error while processing markdown: ' + e);
}
}

View file

@ -21,7 +21,7 @@ const EMBEDDED_LEARNING_OBJECT_PLACEHOLDER = /<learning-object hruid="([^"]+)" l
const LEARNING_OBJECT_DOES_NOT_EXIST = "<div class='non-existing-learning-object' />";
class ProcessingService {
private processors!: Map<DwengoContentType, Processor<any>>;
private processors!: Map<DwengoContentType, Processor<DwengoContentType>>;
constructor() {
const processors = [

View file

@ -25,7 +25,7 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise<Ma
version: node.version,
language: node.language,
})
.then((learningObject) => <[LearningPathNode, FilteredLearningObject | null]>[node, learningObject])
.then((learningObject) => ([node, learningObject] as [LearningPathNode, FilteredLearningObject | null]))
)
)
);

View file

@ -27,14 +27,14 @@ export class SqliteAutoincrementSubscriber implements EventSubscriber {
for (const prop of Object.values(args.meta.properties)) {
const property = prop as EntityProperty<T>;
if (property.primary && property.autoincrement && !(args.entity as Record<string, any>)[property.name]) {
if (property.primary && property.autoincrement && !(args.entity as Record<string, unknown>)[property.name]) {
// Obtain and increment sequence number of this entity.
const propertyKey = args.meta.class.name + '.' + property.name;
const nextSeqNumber = this.sequenceNumbersForEntityType.get(propertyKey) || 0;
this.sequenceNumbersForEntityType.set(propertyKey, nextSeqNumber + 1);
// Set the property accordingly.
(args.entity as Record<string, any>)[property.name] = nextSeqNumber + 1;
(args.entity as Record<string, unknown>)[property.name] = nextSeqNumber + 1;
}
}
}

View file

@ -1,5 +1,6 @@
import axios, { AxiosRequestConfig } from 'axios';
import { getLogger, Logger } from '../logging/initalize.js';
import { LearningObjectIdentifier } from '../interfaces/learning-content.js';
const logger: Logger = getLogger();
@ -17,8 +18,8 @@ export async function fetchWithLogging<T>(
url: string,
description: string,
options?: {
params?: Record<string, any>;
query?: Record<string, any>;
params?: Record<string, unknown> | LearningObjectIdentifier;
query?: Record<string, unknown>;
responseType?: 'json' | 'text';
}
): Promise<T | null> {
@ -26,18 +27,21 @@ export async function fetchWithLogging<T>(
const config: AxiosRequestConfig = options || {};
const response = await axios.get<T>(url, config);
return response.data;
} catch (error: any) {
if (error.response) {
if (error.response.status === 404) {
logger.debug(`❌ ERROR: ${description} not found (404) at "${url}".`);
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
if (error.response) {
if (error.response.status === 404) {
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: Failed to fetch ${description}. Status: ${error.response.status} - ${error.response.statusText} (URL: "${url}")`
);
logger.debug(`❌ ERROR: Network or unexpected error when fetching ${description}:`, error.message);
}
} else {
logger.debug(`❌ ERROR: Network or unexpected error when fetching ${description}:`, error.message);
}
logger.error(`❌ ERROR: Unknown error while fetching ${description}.`, error);
return null;
}
}

View file

@ -5,7 +5,7 @@ const STUDENT_IDP_PREFIX = IDP_PREFIX + 'STUDENT_';
const TEACHER_IDP_PREFIX = IDP_PREFIX + 'TEACHER_';
const CORS_PREFIX = PREFIX + 'CORS_';
type EnvVar = { key: string; required?: boolean; defaultValue?: any };
interface EnvVar { key: string; required?: boolean; defaultValue?: number | string | boolean }
export const envVars: { [key: string]: EnvVar } = {
Port: { key: PREFIX + 'PORT', defaultValue: 3000 },
@ -44,7 +44,7 @@ export function getEnvVar(envVar: EnvVar): string {
} else if (envVar.required) {
throw new Error(`Missing environment variable: ${envVar.key}`);
} else {
return envVar.defaultValue || '';
return String(envVar.defaultValue) || '';
}
}