fix: opslitsing learningpath controller met extra service + api helper
This commit is contained in:
		
							parent
							
								
									91eb374b7e
								
							
						
					
					
						commit
						f7d6cbce65
					
				
					 4 changed files with 98 additions and 116 deletions
				
			
		|  | @ -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<void> { | ||||
| export async function getLearningPaths(req: Request, res: Response): Promise<void> { | ||||
|     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<void> { | ||||
| export async function getLearningPathsByTheme(req: Request, res: Response): Promise<void> { | ||||
|     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<void> { | ||||
| /** | ||||
|  * Search learning paths by query. | ||||
|  */ | ||||
| export async function searchLearningPaths(req: Request, res: Response): Promise<void> { | ||||
|     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<any>(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<void> { | ||||
|     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); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
							
								
								
									
										45
									
								
								backend/src/services/learningPaths.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								backend/src/services/learningPaths.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<LearningPathResponse> { | ||||
|     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<any>(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, | ||||
|     }; | ||||
| } | ||||
|  | @ -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<T>(url: string, description: string): Promise<T | null> { | ||||
| export async function fetchWithLogging<T>( | ||||
|     url: string, | ||||
|     description: string, | ||||
|     params?: Record<string,any> | ||||
| ):  Promise<T | null> { | ||||
|     try { | ||||
|         const response = await axios.get<T>(url); | ||||
|         const config: AxiosRequestConfig = params ? { params } : {}; | ||||
| 
 | ||||
|         const response = await axios.get<T>(url, config); | ||||
|         return response.data; | ||||
|     } catch (error: any) { | ||||
|         if (error.response) { | ||||
|  |  | |||
		Reference in a new issue
	
	 Gabriellvl
						Gabriellvl