From f7d6cbce65eb4698ba008f2c0e4cf6f6904d8cbf Mon Sep 17 00:00:00 2001 From: Gabriellvl Date: Sat, 1 Mar 2025 17:52:55 +0100 Subject: [PATCH] fix: opslitsing learningpath controller met extra service + api helper --- backend/src/controllers/learningPaths.ts | 143 ++++++----------------- backend/src/services/learningObjects.ts | 13 ++- backend/src/services/learningPaths.ts | 45 +++++++ backend/src/util/apiHelper.ts | 13 ++- 4 files changed, 98 insertions(+), 116 deletions(-) create mode 100644 backend/src/services/learningPaths.ts diff --git a/backend/src/controllers/learningPaths.ts b/backend/src/controllers/learningPaths.ts index e588bbe3..880858f3 100644 --- a/backend/src/controllers/learningPaths.ts +++ b/backend/src/controllers/learningPaths.ts @@ -1,100 +1,68 @@ import { Request, Response } from 'express'; -import axios from 'axios'; import { themes } from '../data/themes.js'; import { DWENGO_API_BASE } from '../config/config.js'; +import { fetchWithLogging } from "../util/apiHelper.js"; +import { fetchLearningPaths } from "../services/learningPaths.js"; /** - * Fetch learning paths for a given list of HRUIDs. - * This function sends a request to the Dwengo API with the provided HRUIDs. + * Fetch learning paths based on HRUIDs or return all if no HRUIDs are provided. + * - If `hruids` are given -> fetch specific learning paths. + * - If `hruids` is missing -> return all available learning paths. */ -export async function getLearningPathsFromIds( - req: Request, - res: Response -): Promise { +export async function getLearningPaths(req: Request, res: Response): Promise { try { - const { hruids } = req.query; - const language = (req.query.language as string) || 'nl'; // Default to Dutch + const hruids = req.query.hruids; // Can be string or array + const language = (req.query.language as string) || 'nl'; - if (!hruids) { - res.status(400).json({ - error: 'Missing required parameter: hruids', - }); - return; + let hruidList: string[] = []; + + if (hruids) { + hruidList = Array.isArray(hruids) ? hruids.map(String) : [String(hruids)]; + } else { + // If no hruids are provided, fetch ALL learning paths + hruidList = themes.flatMap((theme) => theme.hruids); } - // Convert the input to an array if it's a string - const hruidList = Array.isArray(hruids) ? hruids : [hruids]; + const learningPaths = await fetchLearningPaths(hruidList, language, `HRUIDs: ${hruidList.join(', ')}`); - // Request learning paths from Dwengo API - const response = await axios.get( - `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`, - { - params: { - pathIdList: JSON.stringify({ hruids: hruidList }), - language, - }, - } - ); - - res.json(response.data); + res.json(learningPaths); } catch (error) { - console.error('Error fetching learning paths:', error); + console.error('❌ Unexpected error fetching learning paths:', error); res.status(500).json({ error: 'Internal server error' }); } } + /** * Fetch all learning paths for a specific theme. - * First retrieves the HRUIDs associated with the theme, - * then fetches the corresponding learning paths from the Dwengo API. */ -export async function getLearningPathsByTheme( - req: Request, - res: Response -): Promise { +export async function getLearningPathsByTheme(req: Request, res: Response): Promise { try { const themeKey = req.params.theme; - const language = (req.query.language as string) || 'nl'; // Default to Dutch - - // Find the theme by its title - const theme = themes.find((t) => { - return t.title === themeKey; - }); + const language = (req.query.language as string) || 'nl'; + const theme = themes.find((t) => t.title === themeKey); if (!theme) { + console.error(`⚠️ WARNING: Theme "${themeKey}" not found.`); res.status(404).json({ error: 'Theme not found' }); return; } - // Extract HRUIDs from the theme - const hruidList = theme.hruids; - - // Request learning paths from Dwengo API using the extracted HRUIDs - const response = await axios.get( - `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`, - { - params: { - pathIdList: JSON.stringify({ hruids: hruidList }), - language, - }, - } - ); - + const response = await fetchLearningPaths(theme.hruids, language, `theme "${themeKey}"`); res.json({ theme: themeKey, - hruids: hruidList, - learningPaths: response.data, + hruids: theme.hruids, + ...response, }); } catch (error) { - console.error('Error fetching learning paths for theme:', error); - res.status(500).json({ error: 'Internal server error' }); + console.error('❌ Unexpected error fetching learning paths by theme:', error); } } -export async function searchLearningPaths( - req: Request, - res: Response -): Promise { +/** + * Search learning paths by query. + */ +export async function searchLearningPaths(req: Request, res: Response): Promise { try { const query = req.query.query as string; const language = (req.query.language as string) || 'nl'; @@ -104,51 +72,12 @@ export async function searchLearningPaths( return; } - const response = await axios.get( - `${DWENGO_API_BASE}/learningPath/search`, - { - params: { all: query, language }, - } - ); + const apiUrl = `${DWENGO_API_BASE}/learningPath/search`; + const params = { all: query, language }; - res.json(response.data); + const searchResults = await fetchWithLogging(apiUrl, `Search learning paths with query "${query}"`, params); + res.json(searchResults ?? []); } catch (error) { - console.error('Error searching learning paths:', error); - res.status(500).json({ error: 'Internal server error' }); - } -} - -export async function getAllLearningPaths( - req: Request, - res: Response -): Promise { - try { - const language = (req.query.language as string) || 'nl'; // Default to Dutch - - // Collect all HRUIDs from all themes - const allHruids: string[] = themes.flatMap((theme) => { - return theme.hruids; - }); - - if (allHruids.length === 0) { - res.status(404).json({ error: 'No HRUIDs found in themes' }); - return; - } - - // Call the Dwengo API with all HRUIDs combined - const response = await axios.get( - `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`, - { - params: { - pathIdList: JSON.stringify({ hruids: allHruids }), - language, - }, - } - ); - - res.json(response.data); - } catch (error) { - console.error('Error fetching all learning paths:', error); - res.status(500).json({ error: 'Internal server error' }); + console.error('❌ Unexpected error searching learning paths:', error); } } diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learningObjects.ts index bab246e0..52913169 100644 --- a/backend/src/services/learningObjects.ts +++ b/backend/src/services/learningObjects.ts @@ -64,14 +64,15 @@ export async function getLearningObjectsFromPath( return await Promise.all( 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); + const metadata = await fetchWithLogging( + metadataUrl, + `Metadata for Learning Object HRUID "${node.learningobject_hruid}" (version ${node.version}, language ${language})` + ); + + if (!metadata) return null; const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw?hruid=${node.learningobject_hruid}&version=${node.version}&language=${language}`; - - return filterLearningObjectMetadata( - metadataResponse.data, - htmlUrl - ); + return filterLearningObjectMetadata(metadata, htmlUrl); }) ); } catch (error) { diff --git a/backend/src/services/learningPaths.ts b/backend/src/services/learningPaths.ts new file mode 100644 index 00000000..6a3039f7 --- /dev/null +++ b/backend/src/services/learningPaths.ts @@ -0,0 +1,45 @@ +import { fetchWithLogging } from "../util/apiHelper.js"; +import { DWENGO_API_BASE } from "../config/config.js"; + +interface LearningPathResponse { + success: boolean; + source: string; + data: any[] | null; + message?: string; +} + +export async function fetchLearningPaths( + hruids: string[], + language: string, + source: string +): Promise { + if (hruids.length === 0) { + return { + success: false, + source, + data: null, + message: `No HRUIDs provided for ${source}.`, + }; + } + + const apiUrl = `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`; + const params = { pathIdList: JSON.stringify({ hruids }), language }; + + const learningPaths = await fetchWithLogging(apiUrl, `Learning paths for ${source}`, params); + + if (!learningPaths || learningPaths.length === 0) { + console.error(`⚠️ WARNING: No learning paths found for ${source}.`); + return { + success: false, + source, + data: [], + message: `No learning paths found for ${source}.`, + }; + } + + return { + success: true, + source, + data: learningPaths, + }; +} diff --git a/backend/src/util/apiHelper.ts b/backend/src/util/apiHelper.ts index 08e12b11..4c7c10fe 100644 --- a/backend/src/util/apiHelper.ts +++ b/backend/src/util/apiHelper.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios, {AxiosRequestConfig} from 'axios'; // !!!! when logger is done -> change @@ -8,11 +8,18 @@ import axios from 'axios'; * * @param url The API endpoint to fetch from. * @param description A short description of what is being fetched (for logging). + * @param params * @returns The response data if successful, or null if an error occurs. */ -export async function fetchWithLogging(url: string, description: string): Promise { +export async function fetchWithLogging( + url: string, + description: string, + params?: Record +): Promise { try { - const response = await axios.get(url); + const config: AxiosRequestConfig = params ? { params } : {}; + + const response = await axios.get(url, config); return response.data; } catch (error: any) { if (error.response) {