refactor(backend): Preparation for learning content from multiple data sources.
Refactored the service layer so that it becomes possible to add another source for learning objects and learning paths.
This commit is contained in:
parent
8c22b72b22
commit
d7728ddd03
10 changed files with 219 additions and 117 deletions
|
@ -3,9 +3,9 @@ import {
|
|||
getLearningObjectById,
|
||||
getLearningObjectIdsFromPath,
|
||||
getLearningObjectsFromPath,
|
||||
} from '../services/learningObjects.js';
|
||||
} from '../services/learning-content/dwengo-api/dwengo-api-learning-object-provider.js';
|
||||
import { FALLBACK_LANG } from '../config.js';
|
||||
import { FilteredLearningObject } from '../interfaces/learningPath';
|
||||
import { FilteredLearningObject } from '../interfaces/learningContent';
|
||||
|
||||
export async function getAllLearningObjects(
|
||||
req: Request,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { FALLBACK_LANG } from '../config.js';
|
|||
import {
|
||||
fetchLearningPaths,
|
||||
searchLearningPaths,
|
||||
} from '../services/learningPaths.js';
|
||||
} from '../services/learning-content/dwengo-api/dwengo-api-learning-path-provider.js';
|
||||
/**
|
||||
* Fetch learning paths based on query parameters.
|
||||
*/
|
||||
|
|
|
@ -20,7 +20,7 @@ export interface LearningObjectNode {
|
|||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface LearningPath {
|
||||
export interface LearningContent {
|
||||
_id: string;
|
||||
language: string;
|
||||
hruid: string;
|
||||
|
@ -93,6 +93,6 @@ export interface FilteredLearningObject {
|
|||
export interface LearningPathResponse {
|
||||
success: boolean;
|
||||
source: string;
|
||||
data: LearningPath[] | null;
|
||||
data: LearningContent[] | null;
|
||||
message?: string;
|
||||
}
|
|
@ -1,13 +1,20 @@
|
|||
import { DWENGO_API_BASE } from '../config.js';
|
||||
import { fetchWithLogging } from '../util/apiHelper.js';
|
||||
import { DWENGO_API_BASE } from '../../../config.js';
|
||||
import { fetchWithLogging } from '../../../util/apiHelper.js';
|
||||
import {
|
||||
FilteredLearningObject,
|
||||
LearningObjectMetadata,
|
||||
LearningObjectNode,
|
||||
LearningPathResponse,
|
||||
} from '../interfaces/learningPath.js';
|
||||
import { fetchLearningPaths } from './learningPaths.js';
|
||||
} from '../../../interfaces/learningContent.js';
|
||||
import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider.js';
|
||||
import {LearningObjectProvider} from "../learning-object-provider";
|
||||
|
||||
/**
|
||||
* Helper function to convert the learning object metadata retrieved from the API to a FilteredLearningObject which
|
||||
* our API should return.
|
||||
* @param data
|
||||
* @param htmlUrl
|
||||
*/
|
||||
function filterData(
|
||||
data: LearningObjectMetadata,
|
||||
htmlUrl: string
|
||||
|
@ -36,29 +43,7 @@ function filterData(
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
export async function getLearningObjectById(
|
||||
hruid: string,
|
||||
language: string
|
||||
): Promise<FilteredLearningObject | null> {
|
||||
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${hruid}&language=${language}`;
|
||||
const metadata = await fetchWithLogging<LearningObjectMetadata>(
|
||||
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)
|
||||
* Generic helper function to fetch learning objects (full data or just HRUIDs)
|
||||
*/
|
||||
async function fetchLearningObjects(
|
||||
hruid: string,
|
||||
|
@ -67,7 +52,7 @@ async function fetchLearningObjects(
|
|||
): Promise<FilteredLearningObject[] | string[]> {
|
||||
try {
|
||||
const learningPathResponse: LearningPathResponse =
|
||||
await fetchLearningPaths(
|
||||
await dwengoApiLearningPathProvider.fetchLearningPaths(
|
||||
[hruid],
|
||||
language,
|
||||
`Learning path for HRUID "${hruid}"`
|
||||
|
@ -93,7 +78,7 @@ async function fetchLearningObjects(
|
|||
|
||||
return await Promise.all(
|
||||
nodes.map(async (node) => {
|
||||
return getLearningObjectById(
|
||||
return dwengoApiLearningObjectProvider.getLearningObjectById(
|
||||
node.learningobject_hruid,
|
||||
language
|
||||
);
|
||||
|
@ -109,26 +94,52 @@ async function fetchLearningObjects(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch full learning object data (metadata)
|
||||
*/
|
||||
export async function getLearningObjectsFromPath(
|
||||
hruid: string,
|
||||
language: string
|
||||
): Promise<FilteredLearningObject[]> {
|
||||
return (await fetchLearningObjects(
|
||||
hruid,
|
||||
true,
|
||||
language
|
||||
)) as FilteredLearningObject[];
|
||||
}
|
||||
const dwengoApiLearningObjectProvider: LearningObjectProvider = {
|
||||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
async getLearningObjectById(
|
||||
hruid: string,
|
||||
language: string
|
||||
): Promise<FilteredLearningObject | null> {
|
||||
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${hruid}&language=${language}`;
|
||||
const metadata = await fetchWithLogging<LearningObjectMetadata>(
|
||||
metadataUrl,
|
||||
`Metadata for Learning Object HRUID "${hruid}" (language ${language})`
|
||||
);
|
||||
|
||||
/**
|
||||
* Fetch only learning object HRUIDs
|
||||
*/
|
||||
export async function getLearningObjectIdsFromPath(
|
||||
hruid: string,
|
||||
language: string
|
||||
): Promise<string[]> {
|
||||
return (await fetchLearningObjects(hruid, false, language)) as string[];
|
||||
}
|
||||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch full learning object data (metadata)
|
||||
*/
|
||||
async getLearningObjectsFromPath(
|
||||
hruid: string,
|
||||
language: string
|
||||
): Promise<FilteredLearningObject[]> {
|
||||
return (await fetchLearningObjects(
|
||||
hruid,
|
||||
true,
|
||||
language
|
||||
)) as FilteredLearningObject[];
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch only learning object HRUIDs
|
||||
*/
|
||||
async getLearningObjectIdsFromPath(
|
||||
hruid: string,
|
||||
language: string
|
||||
): Promise<string[]> {
|
||||
return (await fetchLearningObjects(hruid, false, language)) as string[];
|
||||
}
|
||||
};
|
||||
|
||||
export default dwengoApiLearningObjectProvider;
|
|
@ -0,0 +1,65 @@
|
|||
import { fetchWithLogging } from '../../../util/apiHelper.js';
|
||||
import { DWENGO_API_BASE } from '../../../config.js';
|
||||
import {
|
||||
LearningContent,
|
||||
LearningPathResponse,
|
||||
} from '../../../interfaces/learningContent.js';
|
||||
import {LearningPathProvider} from "../learning-path-provider";
|
||||
|
||||
const dwengoApiLearningPathProvider: LearningPathProvider = {
|
||||
async 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<LearningContent[]>(
|
||||
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,
|
||||
};
|
||||
},
|
||||
async searchLearningPaths(
|
||||
query: string,
|
||||
language: string
|
||||
): Promise<LearningContent[]> {
|
||||
const apiUrl = `${DWENGO_API_BASE}/learningPath/search`;
|
||||
const params = { all: query, language };
|
||||
|
||||
const searchResults = await fetchWithLogging<LearningContent[]>(
|
||||
apiUrl,
|
||||
`Search learning paths with query "${query}"`,
|
||||
params
|
||||
);
|
||||
return searchResults ?? [];
|
||||
}
|
||||
};
|
||||
|
||||
export default dwengoApiLearningPathProvider;
|
|
@ -0,0 +1,18 @@
|
|||
import {FilteredLearningObject} from "../../interfaces/learningContent";
|
||||
|
||||
export interface LearningObjectProvider {
|
||||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
getLearningObjectById(hruid: string, language: string): Promise<FilteredLearningObject | null>;
|
||||
|
||||
/**
|
||||
* Fetch full learning object data (metadata)
|
||||
*/
|
||||
getLearningObjectsFromPath(hruid: string, language: string): Promise<FilteredLearningObject[]>;
|
||||
|
||||
/**
|
||||
* Fetch only learning object HRUIDs
|
||||
*/
|
||||
getLearningObjectIdsFromPath(hruid: string, language: string): Promise<string[]>;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import {FilteredLearningObject} from "../../interfaces/learningContent";
|
||||
import dwengoApiLearningObjectProvider from "./dwengo-api/dwengo-api-learning-object-provider";
|
||||
|
||||
/**
|
||||
* Service providing access to data about learning objects from the appropriate data source (database or Dwengo-api)
|
||||
*/
|
||||
const learningObjectService = {
|
||||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
getLearningObjectById(hruid: string, language: string): Promise<FilteredLearningObject | null> {
|
||||
return dwengoApiLearningObjectProvider.getLearningObjectById(hruid, language);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch full learning object data (metadata)
|
||||
*/
|
||||
getLearningObjectsFromPath(hruid: string, language: string): Promise<FilteredLearningObject[]> {
|
||||
return dwengoApiLearningObjectProvider.getLearningObjectsFromPath(hruid, language);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch only learning object HRUIDs
|
||||
*/
|
||||
getLearningObjectIdsFromPath(hruid: string, language: string): Promise<string[]> {
|
||||
return dwengoApiLearningObjectProvider.getLearningObjectIdsFromPath(hruid, language);
|
||||
}
|
||||
};
|
||||
|
||||
export default learningObjectService;
|
|
@ -0,0 +1,16 @@
|
|||
import {LearningContent, LearningPathResponse} from "../../interfaces/learningContent";
|
||||
|
||||
/**
|
||||
* Generic interface for a service which provides access to learning paths from a data source.
|
||||
*/
|
||||
export interface LearningPathProvider {
|
||||
/**
|
||||
* Fetch the learning paths with the given hruids from the data source.
|
||||
*/
|
||||
fetchLearningPaths(hruids: string[], language: string, source: string): Promise<LearningPathResponse>;
|
||||
|
||||
/**
|
||||
* Search learning paths in the data source using the given search string.
|
||||
*/
|
||||
searchLearningPaths(query: string, language: string): Promise<LearningContent[]>;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import {LearningContent, LearningPathResponse} from "../../interfaces/learningContent";
|
||||
import dwengoApiLearningPathProvider from "./dwengo-api/dwengo-api-learning-path-provider";
|
||||
|
||||
/**
|
||||
* Service providing access to data about learning paths from the appropriate data source (database or Dwengo-api)
|
||||
*/
|
||||
const learningPathService = {
|
||||
/**
|
||||
* Fetch the learning paths with the given hruids from the data source.
|
||||
*/
|
||||
fetchLearningPaths(hruids: string[], language: string, source: string): Promise<LearningPathResponse> {
|
||||
return dwengoApiLearningPathProvider.fetchLearningPaths(hruids, language, source);
|
||||
},
|
||||
|
||||
/**
|
||||
* Search learning paths in the data source using the given search string.
|
||||
*/
|
||||
searchLearningPaths(query: string, language: string): Promise<LearningContent[]> {
|
||||
return dwengoApiLearningPathProvider.searchLearningPaths(query, language);
|
||||
}
|
||||
}
|
||||
|
||||
export default learningPathService;
|
|
@ -1,61 +0,0 @@
|
|||
import { fetchWithLogging } from '../util/apiHelper.js';
|
||||
import { DWENGO_API_BASE } from '../config.js';
|
||||
import {
|
||||
LearningPath,
|
||||
LearningPathResponse,
|
||||
} from '../interfaces/learningPath.js';
|
||||
|
||||
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<LearningPath[]>(
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
export async function searchLearningPaths(
|
||||
query: string,
|
||||
language: string
|
||||
): Promise<LearningPath[]> {
|
||||
const apiUrl = `${DWENGO_API_BASE}/learningPath/search`;
|
||||
const params = { all: query, language };
|
||||
|
||||
const searchResults = await fetchWithLogging<LearningPath[]>(
|
||||
apiUrl,
|
||||
`Search learning paths with query "${query}"`,
|
||||
params
|
||||
);
|
||||
return searchResults ?? [];
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue