Skip to content

Commit

Permalink
fix: replacing LMS endpoints with CMS endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
PKulkoRaccoonGang committed Jan 10, 2024
1 parent e8bc94c commit 8710ceb
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 76 deletions.
10 changes: 7 additions & 3 deletions src/course-unit/course-sequence/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ import { useWindowSize } from '@edx/paragon';

import { useModel } from '../../generic/model-store';
import { RequestStatus } from '../../data/constants';
import { getCourseSectionVertical, getSequenceStatus, sequenceIdsSelector } from '../data/selectors';
import {
getCourseSectionVertical,
getCourseSectionVerticalLoadingStatus,
sequenceIdsSelector,
} from '../data/selectors';

export function useSequenceNavigationMetadata(currentSequenceId, currentUnitId) {
const { SUCCESSFUL } = RequestStatus;
const sequenceIds = useSelector(sequenceIdsSelector);
const sequenceStatus = useSelector(getSequenceStatus);
const courseSectionVerticalLoadingStatus = useSelector(getCourseSectionVerticalLoadingStatus);
const { nextUrl, prevUrl } = useSelector(getCourseSectionVertical);
const sequence = useModel('sequences', currentSequenceId);
const { courseId, status } = useSelector(state => state.courseDetail);

const isCourseOrSequenceNotSuccessful = status !== SUCCESSFUL || sequenceStatus !== SUCCESSFUL;
const isCourseOrSequenceNotSuccessful = status !== SUCCESSFUL || courseSectionVerticalLoadingStatus !== SUCCESSFUL;
const areIdsNotValid = !currentSequenceId || !currentUnitId || !sequence.unitIds;
const isNotSuccessfulCompletion = isCourseOrSequenceNotSuccessful || areIdsNotValid;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import UnitIcon from './UnitIcon';
const UnitButton = ({
title, contentType, isActive, unitId, className, showTitle,
}) => {
const courseId = useSelector(state => state.courseUnit.courseId);
const courseId = useSelector(state => state.courseDetail.courseId);
const sequenceId = useSelector(state => state.courseUnit.sequenceId);

return (
Expand Down
16 changes: 2 additions & 14 deletions src/course-unit/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
normalizeMetadata,
normalizeCourseHomeCourseMetadata,
appendBrowserTimezoneToUrl,
normalizeCourseSectionVerticalData,
} from './utils';

const getStudioBaseUrl = () => getConfig().STUDIO_BASE_URL;
Expand All @@ -16,7 +17,6 @@ const getLmsBaseUrl = () => getConfig().LMS_BASE_URL;
export const getCourseUnitApiUrl = (itemId) => `${getStudioBaseUrl()}/xblock/container/${itemId}`;
export const getXBlockBaseApiUrl = (itemId) => `${getStudioBaseUrl()}/xblock/${itemId}`;
export const getCourseSectionVerticalApiUrl = (itemId) => `${getStudioBaseUrl()}/api/contentstore/v1/container_handler/${itemId}`;
export const getSequenceMetadataApiUrl = (sequenceId) => `${getLmsBaseUrl()}/api/courseware/sequence/${sequenceId}`;
export const getLearningSequencesOutlineApiUrl = (courseId) => `${getLmsBaseUrl()}/api/learning_sequences/v1/course_outline/${courseId}`;
export const getCourseMetadataApiUrl = (courseId) => `${getLmsBaseUrl()}/api/courseware/course/${courseId}`;
export const getCourseHomeCourseMetadataApiUrl = (courseId) => `${getLmsBaseUrl()}/api/course_home/course_metadata/${courseId}`;
Expand Down Expand Up @@ -50,18 +50,6 @@ export async function editUnitDisplayName(unitId, displayName) {
return data;
}

/**
* Get sequence metadata for a given sequence ID.
* @param {string} sequenceId - The ID of the sequence for which metadata is requested.
* @returns {Promise<Object>} - A Promise that resolves to the normalized sequence metadata.
*/
export async function getSequenceMetadata(sequenceId) {
const { data } = await getAuthenticatedHttpClient()
.get(getSequenceMetadataApiUrl(sequenceId), {});

return normalizeSequenceMetadata(data);
}

/**
* Get an object containing course section vertical data.
* @param {string} unitId
Expand All @@ -71,7 +59,7 @@ export async function getCourseSectionVerticalData(unitId) {
const { data } = await getAuthenticatedHttpClient()
.get(getCourseSectionVerticalApiUrl(unitId));

return camelCaseObject(data);
return normalizeCourseSectionVerticalData(data);
}

/**
Expand Down
8 changes: 2 additions & 6 deletions src/course-unit/data/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ import { createSelector } from '@reduxjs/toolkit';
import { RequestStatus } from '../../data/constants';

export const getCourseUnitData = (state) => state.courseUnit.unit;

export const getSavingStatus = (state) => state.courseUnit.savingStatus;

export const getLoadingStatus = (state) => state.courseUnit.loadingStatus;

export const getSequenceStatus = (state) => state.courseUnit.sequenceStatus;

export const getCourseSectionVertical = (state) => state.courseUnit.courseSectionVertical;

export const getCourseSectionVerticalLoadingStatus = (state) => state
.courseUnit.loadingStatus.courseSectionVerticalLoadingStatus;
export const getCourseStatus = state => state.courseUnit.courseStatus;
export const getCoursewareMeta = state => state.models.coursewareMeta;
export const getSections = state => state.models.sections;
export const getCourseId = state => state.courseDetail.courseId;

export const sequenceIdsSelector = createSelector(
[getCourseStatus, getCoursewareMeta, getSections, getCourseId],
(courseStatus, coursewareMeta, sections, courseId) => {
Expand Down
62 changes: 13 additions & 49 deletions src/course-unit/data/thunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import {
getCourseUnitData,
editUnitDisplayName,
getSequenceMetadata,
getCourseMetadata,
getLearningSequencesOutline, getCourseHomeCourseMetadata, getCourseSectionVerticalData,
} from './api';
Expand Down Expand Up @@ -47,17 +46,28 @@ export function fetchCourseUnitQuery(courseId) {
};
}

export function fetchCourseSectionVerticalData(courseId) {
export function fetchCourseSectionVerticalData(courseId, sequenceId) {
return async (dispatch) => {
dispatch(updateLoadingCourseSectionVerticalDataStatus({ status: RequestStatus.IN_PROGRESS }));
dispatch(fetchSequenceRequest({ sequenceId }));

try {
const courseSectionVerticalData = await getCourseSectionVerticalData(courseId);
dispatch(fetchCourseSectionVerticalDataSuccess(courseSectionVerticalData));
dispatch(updateLoadingCourseSectionVerticalDataStatus({ status: RequestStatus.SUCCESSFUL }));
dispatch(updateModel({
modelType: 'sequences',
model: courseSectionVerticalData.sequence,
}));
dispatch(updateModels({
modelType: 'units',
models: courseSectionVerticalData.units,
}));
dispatch(fetchSequenceSuccess({ sequenceId }));
return true;
} catch (error) {
dispatch(updateLoadingCourseSectionVerticalDataStatus({ status: RequestStatus.FAILED }));
dispatch(fetchSequenceFailure({ sequenceId }));
return false;
}
};
Expand All @@ -84,45 +94,6 @@ export function editCourseItemQuery(itemId, displayName) {
};
}

export function fetchSequence(sequenceId) {
return async (dispatch) => {
dispatch(fetchSequenceRequest({ sequenceId }));
try {
const { sequence, units } = await getSequenceMetadata(sequenceId);

if (sequence.blockType !== 'sequential') {
// Some other block types (particularly 'chapter') can be returned
// by this API. We want to error in that case, since downstream
// courseware code is written to render Sequences of Units.
logError(
`Requested sequence '${sequenceId}' `
+ `has block type '${sequence.blockType}'; expected block type 'sequential'.`,
);
dispatch(fetchSequenceFailure({ sequenceId }));
} else {
dispatch(updateModel({
modelType: 'sequences',
model: sequence,
}));
dispatch(updateModels({
modelType: 'units',
models: units,
}));
dispatch(fetchSequenceSuccess({ sequenceId }));
}
} catch (error) {
// Some errors are expected - for example, CoursewareContainer may request sequence metadata for a unit and rely
// on the request failing to notice that it actually does have a unit (mostly so it doesn't have to know anything
// about the opaque key structure). In such cases, the backend gives us a 422.
const sequenceMightBeUnit = error?.response?.status === 422;
if (!sequenceMightBeUnit) {
logError(error);
}
dispatch(fetchSequenceFailure({ sequenceId, sequenceMightBeUnit }));
}
};
}

export function fetchCourse(courseId) {
return async (dispatch) => {
dispatch(fetchCourseRequest({ courseId }));
Expand Down Expand Up @@ -152,9 +123,7 @@ export function fetchCourse(courseId) {
}

if (learningSequencesOutlineResult.status === 'fulfilled') {
const {
courses, sections, sequences,
} = learningSequencesOutlineResult.value;
const { courses, sections } = learningSequencesOutlineResult.value;

// This updates the course with a sectionIds array from the Learning Sequence data.
dispatch(updateModelsMap({
Expand All @@ -165,11 +134,6 @@ export function fetchCourse(courseId) {
modelType: 'sections',
modelsMap: sections,
}));
// We update for sequences because the sequence metadata may have come back first.
dispatch(updateModelsMap({
modelType: 'sequences',
modelsMap: sequences,
}));
}

const fetchedMetadata = courseMetadataResult.status === 'fulfilled';
Expand Down
22 changes: 22 additions & 0 deletions src/course-unit/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,25 @@ export function normalizeCourseHomeCourseMetadata(metadata, rootSlug) {
isMasquerading: data.originalUserIsStaff && !data.isStaff,
};
}

export function normalizeCourseSectionVerticalData(metadata) {
const data = camelCaseObject(metadata);
return {
...data,
sequence: {
id: data.subsectionLocation,
title: data.xblock.displayName,
unitIds: data.xblockInfo.ancestorInfo.ancestors[0].childInfo.children.map((item) => item.id),
},
units: data.xblockInfo.ancestorInfo.ancestors[0].childInfo.children.map((unit) => ({
id: unit.id,
sequenceId: data.subsectionLocation,
bookmarked: unit.bookmarked,
complete: unit.complete,
title: unit.displayName,
contentType: unit.type,
graded: unit.graded,
containsContentTypeGatedContent: unit.contains_content_type_gated_content,
})),
};
}
4 changes: 1 addition & 3 deletions src/course-unit/hooks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { RequestStatus } from '../data/constants';
import {
fetchCourseUnitQuery,
editCourseItemQuery,
fetchSequence,
fetchCourse,
fetchCourseSectionVerticalData,
} from './data/thunk';
Expand Down Expand Up @@ -67,8 +66,7 @@ export const useCourseUnit = ({ courseId, blockId }) => {

useEffect(() => {
dispatch(fetchCourseUnitQuery(blockId));
dispatch(fetchCourseSectionVerticalData(blockId));
dispatch(fetchSequence(sequenceId));
dispatch(fetchCourseSectionVerticalData(blockId, sequenceId));
dispatch(fetchCourse(courseId));
handleNavigate(sequenceId);
}, [courseId, blockId, sequenceId]);
Expand Down

0 comments on commit 8710ceb

Please sign in to comment.