From 4d67042660352d2505c1a3f441e8be2da6a87b86 Mon Sep 17 00:00:00 2001 From: Michael Terry Date: Tue, 20 Jul 2021 13:30:27 -0400 Subject: [PATCH] fix: handle new 403 "redirect" responses from backend To make it easier to redirect the user to an another page or an access denied error page, this PR deprecates the old custom 401 & 404 error handling, replacing them both with a generic 403 error code that contains a URL to redirect the user to. Before this change, we weren't handling all the error cases, and when we didn't, we ended up showing a broken page to the user. --- src/course-home/data/api.js | 40 +++++++++++++++++++++++++++++++++- src/course-home/data/thunks.js | 5 +++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/course-home/data/api.js b/src/course-home/data/api.js index f1f59060..0069c04d 100644 --- a/src/course-home/data/api.js +++ b/src/course-home/data/api.js @@ -182,6 +182,26 @@ export function normalizeOutlineBlocks(courseId, blocks) { return models; } +function processTabDataErrorRedirect(error) { + const { httpErrorResponseData, httpErrorStatus } = error && error.customAttributes; + + if (httpErrorStatus === 403) { + // Currently, only 403 errors contain redirect content + try { + const { redirect } = JSON.parse(httpErrorResponseData); + if (redirect) { + global.location.replace(redirect); + return true; + } + } catch (exc) { + // ignore any json parse errors, might be an actual 403 without redirect json content + } + } + + // Did not do any redirecting + return false; +} + export async function getCourseHomeCourseMetadata(courseId) { let url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`; url = appendBrowserTimezoneToUrl(url); @@ -202,11 +222,18 @@ export async function getDatesTabData(courseId) { } catch (error) { const { httpErrorStatus } = error && error.customAttributes; if (httpErrorStatus === 404) { + // TODO: remove this - not needed once the backend uses 403s for redirects global.location.replace(`${getConfig().LMS_BASE_URL}/courses/${courseId}/dates`); + return null; } // 401 can be returned for unauthenticated users or users who are not enrolled if (httpErrorStatus === 401) { + // TODO: remove this - not needed once the backend uses 403s for redirects global.location.replace(`${getConfig().BASE_URL}/course/${courseId}/home`); + return null; + } + if (processTabDataErrorRedirect(error)) { + return null; // keeps loading screen active } throw error; } @@ -249,11 +276,18 @@ export async function getProgressTabData(courseId, targetUserId) { } catch (error) { const { httpErrorStatus } = error && error.customAttributes; if (httpErrorStatus === 404) { + // TODO: remove this - not needed once the backend uses 403s for redirects global.location.replace(`${getConfig().LMS_BASE_URL}/courses/${courseId}/progress`); + return null; } // 401 can be returned for unauthenticated users or users who are not enrolled if (httpErrorStatus === 401) { + // TODO: remove this - not needed once the backend uses 403s for redirects global.location.replace(`${getConfig().BASE_URL}/course/${courseId}/home`); + return null; + } + if (processTabDataErrorRedirect(error)) { + return null; // keeps loading screen active } throw error; } @@ -303,8 +337,12 @@ export async function getOutlineTabData(courseId) { } catch (error) { const { httpErrorStatus } = error && error.customAttributes; if (httpErrorStatus === 404) { + // TODO: remove this - not needed once the backend uses 403s for redirects global.location.replace(`${getConfig().LMS_BASE_URL}/courses/${courseId}/course/`); - return {}; + return null; + } + if (processTabDataErrorRedirect(error)) { + return null; // keeps loading screen active } throw error; } diff --git a/src/course-home/data/thunks.js b/src/course-home/data/thunks.js index f95a7052..479da706 100644 --- a/src/course-home/data/thunks.js +++ b/src/course-home/data/thunks.js @@ -49,6 +49,11 @@ export function fetchTab(courseId, tab, getTabData, targetUserId) { logError(courseHomeCourseMetadataResult.reason); } + if (fetchedTabData && tabDataResult.value === null) { + // null tab data indicates that we have redirected elsewhere - just exit and don't visibly stop loading + return; + } + if (fetchedTabData) { dispatch(addModel({ modelType: tab,