From 91eb374b7e8b6e72a832f33affdfc1a1c84849a4 Mon Sep 17 00:00:00 2001 From: Gabriellvl Date: Sat, 1 Mar 2025 15:15:04 +0100 Subject: [PATCH] fix: api helper voor extra error checks bij fetch dwengo api + schrijffout in controllers/themes --- backend/src/controllers/themes.ts | 2 +- backend/src/routes/learningObjects.ts | 3 +++ backend/src/routes/learningPaths.ts | 2 ++ backend/src/services/learningObjects.ts | 17 +++++++++------ backend/src/util/apiHelper.ts | 29 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 backend/src/util/apiHelper.ts diff --git a/backend/src/controllers/themes.ts b/backend/src/controllers/themes.ts index d81cf054..d6c67107 100644 --- a/backend/src/controllers/themes.ts +++ b/backend/src/controllers/themes.ts @@ -17,7 +17,7 @@ function loadTranslations(language: string): Translations { return yaml.load(yamlFile) as Translations; } catch (error) { console.error( - `Cant load for language: ${language}, fallen back on dutch` + `Cannot load translation for: ${language}, fallen back to Dutch` ); console.error(error); const fallbackPath = path.join(process.cwd(), '_i18n', 'nl.yml'); diff --git a/backend/src/routes/learningObjects.ts b/backend/src/routes/learningObjects.ts index 907ec1d7..1d72daaf 100644 --- a/backend/src/routes/learningObjects.ts +++ b/backend/src/routes/learningObjects.ts @@ -3,9 +3,12 @@ import { getAllLearningObjects } from '../controllers/learningObjects.js'; const router = express.Router(); +// DWENGO learning objects + // Arg: hruid learningPath // Query: language // Route to fetch list of learning objects based on hruid of learning path +// example: http://localhost:3000/learningObject/un_artificiele_intelligentie router.get('/:hruid', getAllLearningObjects); export default router; diff --git a/backend/src/routes/learningPaths.ts b/backend/src/routes/learningPaths.ts index 7e821350..17eb881d 100644 --- a/backend/src/routes/learningPaths.ts +++ b/backend/src/routes/learningPaths.ts @@ -8,6 +8,8 @@ import { const router = express.Router(); +// DWENGO learning paths + // Query: hruids(list), language // Route to fetch learning paths based on a list of HRUIDs // Example: http://localhost:3000/learningPath?hruids=pn_werking&hruids=art1 diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learningObjects.ts index 0168981c..bab246e0 100644 --- a/backend/src/services/learningObjects.ts +++ b/backend/src/services/learningObjects.ts @@ -1,5 +1,6 @@ import axios from 'axios'; import { DWENGO_API_BASE } from '../config/config.js'; +import { fetchWithLogging } from "../util/apiHelper.js"; interface LearningObjectNode { _id: string; @@ -18,7 +19,7 @@ function filterLearningObjectMetadata(data: any, htmlUrl: string) { title: data.title, htmlUrl, - // Html content object + // Url to fetch html content language: data.language, difficulty: data.difficulty, estimatedTime: data.estimated_time, @@ -50,15 +51,18 @@ export async function getLearningObjectsFromPath( ) { try { const learningPathUrl = `${DWENGO_API_BASE}/learningPath/${hruid}/${language}`; - const learningPathResponse = await axios.get(learningPathUrl); - const nodes = learningPathResponse.data.nodes; + const learningPathData = await fetchWithLogging<{ nodes: LearningObjectNode[] }>( + learningPathUrl, + `Learning path for HRUID "${hruid}" in language "${language}"` + ); - if (!nodes || nodes.length === 0) { - throw new Error('No learning objects found in this learning path.'); + if (!learningPathData || !learningPathData.nodes || learningPathData.nodes.length === 0) { + console.error(`⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.`); + return []; } return await Promise.all( - nodes.map(async (node: LearningObjectNode) => { + learningPathData.nodes.map(async (node: LearningObjectNode) => { const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${node.learningobject_hruid}&version=${node.version}&language=${language}`; const metadataResponse = await axios.get(metadataUrl); @@ -72,6 +76,5 @@ export async function getLearningObjectsFromPath( ); } catch (error) { console.error('Error fetching learning objects:', error); - throw new Error('Failed to fetch learning objects.'); } } diff --git a/backend/src/util/apiHelper.ts b/backend/src/util/apiHelper.ts new file mode 100644 index 00000000..08e12b11 --- /dev/null +++ b/backend/src/util/apiHelper.ts @@ -0,0 +1,29 @@ +import axios from 'axios'; + +// !!!! when logger is done -> change + +/** + * Utility function to fetch data from an API endpoint with error handling. + * Logs errors but does NOT throw exceptions to keep the system running. + * + * @param url The API endpoint to fetch from. + * @param description A short description of what is being fetched (for logging). + * @returns The response data if successful, or null if an error occurs. + */ +export async function fetchWithLogging(url: string, description: string): Promise { + try { + const response = await axios.get(url); + return response.data; + } catch (error: any) { + if (error.response) { + if (error.response.status === 404) { + console.error(`❌ ERROR: ${description} not found (404) at "${url}".`); + } else { + console.error(`❌ ERROR: Failed to fetch ${description}. Status: ${error.response.status} - ${error.response.statusText} (URL: "${url}")`); + } + } else { + console.error(`❌ ERROR: Network or unexpected error when fetching ${description}:`, error.message); + } + return null; + } +}