diff --git a/backend/src/controllers/learningObjects.ts b/backend/src/controllers/learningObjects.ts index d0df24fd..026d37ef 100644 --- a/backend/src/controllers/learningObjects.ts +++ b/backend/src/controllers/learningObjects.ts @@ -1,29 +1,57 @@ import { Request, Response } from 'express'; -import { getLearningObjectsFromPath } from '../services/learningObjects.js'; +import { + getLearningObjectById, + getLearningObjectIdsFromPath, + getLearningObjectsFromPath +} from '../services/learningObjects.js'; import { FALLBACK_LANG } from '../config.js'; +import {FilteredLearningObject} from "../interfaces/learningPath"; export async function getAllLearningObjects( req: Request, res: Response +): Promise { + try { + const hruid = (req.query.hruid as string); + const full = req.query.full === 'true'; + const language = (req.query.language as string) || FALLBACK_LANG; + + if (!hruid) { + res.status(400).json({ error: 'HRUID query is required.' }); + return; + } + + let learningObjects: FilteredLearningObject[] | string[]; + if (full) + learningObjects = await getLearningObjectsFromPath(hruid,language); + else + learningObjects = await getLearningObjectIdsFromPath(hruid, language) + + res.json(learningObjects); + } catch (error) { + console.error('Error fetching learning objects:', error); + res.status(500).json({ error: 'Internal server error' }); + } +} + + +export async function getLearningObject( + req: Request, + res: Response ): Promise { try { const { hruid } = req.params; const language = (req.query.language as string) || FALLBACK_LANG; - if (!language) { - res.status(400).json({ - error: 'Language query parameter is required.', - }); + if (!hruid) { + res.status(400).json({ error: 'HRUID parameter is required.' }); return; } - const learningObjects = await getLearningObjectsFromPath( - hruid, - language - ); - res.json(learningObjects); - } catch (error) { - console.error('Error fetching learning objects:', error); + const learningObject = await getLearningObjectById(hruid, language); + res.json(learningObject); + } catch (error){ + console.error('Error fetching learning object:', error); res.status(500).json({ error: 'Internal server error' }); } } diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learningObjects.ts index a2243a9a..b184f1fd 100644 --- a/backend/src/services/learningObjects.ts +++ b/backend/src/services/learningObjects.ts @@ -3,11 +3,11 @@ import { fetchWithLogging } from '../util/apiHelper.js'; import { FilteredLearningObject, LearningObjectMetadata, - LearningObjectNode, + LearningObjectNode, LearningPathResponse, } from '../interfaces/learningPath.js'; import { fetchLearningPaths } from './learningPaths.js'; -function filterLearningObjectMetadata( +function filterData( data: LearningObjectMetadata, htmlUrl: string ): FilteredLearningObject { @@ -34,52 +34,79 @@ function filterLearningObjectMetadata( }; } -export async function getLearningObjectsFromPath( +/** + * Fetches a single learning object by its HRUID + */ +export async function getLearningObjectById( hruid: string, language: string -): Promise { +): Promise { + const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${hruid}&language=${language}`; + const metadata = await fetchWithLogging( + metadataUrl, + `Metadata for Learning Object HRUID "${hruid}" (language ${language})` + ); + + if (!metadata) { + console.error(`⚠️ WARNING: Learning object "${hruid}" not found.`); + return null; + } + + const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw?hruid=${hruid}&language=${language}`; + return filterData(metadata, htmlUrl); +} + +/** + * Generic function to fetch learning objects (full data or just HRUIDs) + */ +async function fetchLearningObjects( + hruid: string, + full: boolean, + language: string +): Promise { try { - const learningPathResponse = await fetchLearningPaths( + const learningPathResponse: LearningPathResponse = await fetchLearningPaths( [hruid], language, `Learning path for HRUID "${hruid}"` ); - if ( - !learningPathResponse.success || - !learningPathResponse.data || - learningPathResponse.data.length === 0 - ) { - console.error( - `⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.` - ); + if (!learningPathResponse.success || !learningPathResponse.data?.length) { + console.error(`⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.`); return []; } const nodes: LearningObjectNode[] = learningPathResponse.data[0].nodes; + if (!full) { + return nodes.map(node => node.learningobject_hruid); + } + return await Promise.all( - nodes.map(async (node) => { - const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${node.learningobject_hruid}&version=${node.version}&language=${language}`; - 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(metadata, htmlUrl); - }) - ).then((objects) => { - return objects.filter((obj): obj is FilteredLearningObject => { - return obj !== null; - }); - }); + nodes.map(async (node) => getLearningObjectById(node.learningobject_hruid, language)) + ).then((objects) => objects.filter((obj): obj is FilteredLearningObject => obj !== null)); } catch (error) { - console.error('Error fetching learning objects:', error); + console.error('❌ Error fetching learning objects:', error); return []; } } + +/** + * Fetch full learning object data (metadata) + */ +export async function getLearningObjectsFromPath( + hruid: string, + language: string +): Promise { + return await fetchLearningObjects(hruid, true, language) as FilteredLearningObject[]; +} + +/** + * Fetch only learning object HRUIDs + */ +export async function getLearningObjectIdsFromPath( + hruid: string, + language: string +): Promise { + return await fetchLearningObjects(hruid, false, language) as string[]; +}