From 4a6a8555d2107af15cf304e6cd9e115b255a60d1 Mon Sep 17 00:00:00 2001 From: Ryan Lewis <93001277+rylew1@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:30:53 -0700 Subject: [PATCH] [Issue #1823]: Fix prod FE error (#1824) ## Summary Fixes #1823 ## Changes proposed - Add error handling when we have invalid JSON returned (when API is up but not endpoints are enabled) --- frontend/src/app/api/BaseApi.ts | 2 + frontend/src/app/search/error.tsx | 62 +++++++++++++++++++++++++------ frontend/src/errors.ts | 1 + 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/api/BaseApi.ts b/frontend/src/app/api/BaseApi.ts index 284fb7bdb..4c60bae16 100644 --- a/frontend/src/app/api/BaseApi.ts +++ b/frontend/src/app/api/BaseApi.ts @@ -222,6 +222,8 @@ const throwError = ( searchInputs: SearchFetcherProps, firstError?: APIResponseError, ) => { + console.log("Throwing error: ", message, status_code, searchInputs); + // Include just firstError for now, we can expand this // If we need ValidationErrors to be more expanded const error = firstError ? { message, firstError } : { message }; diff --git a/frontend/src/app/search/error.tsx b/frontend/src/app/search/error.tsx index 01ea3cf2a..bbcd1cc48 100644 --- a/frontend/src/app/search/error.tsx +++ b/frontend/src/app/search/error.tsx @@ -27,18 +27,29 @@ export interface ParsedError { export default function Error({ error }: ErrorProps) { // The error message is passed as an object that's been stringified. // Parse it here. - const parsedErrorData = JSON.parse(error.message) as ParsedError; + let parsedErrorData; const pagination_info = getErrorPaginationInfo(); + let convertedSearchParams; + if (!isValidJSON(error.message)) { + // the error likely is just a string with a non-specific Server Component error when running the built app + // "An error occurred in the Server Components render. The specific message is omitted in production builds..." + parsedErrorData = getParsedError(); + convertedSearchParams = parsedErrorData.searchInputs; + } else { + // Valid error thrown from server component + parsedErrorData = JSON.parse(error.message) as ParsedError; + + // The error message search inputs had to be converted to arrays in order to be stringified, + // convert those back to sets as we do in non-error flow. + convertedSearchParams = convertSearchInputArraysToSets( + parsedErrorData.searchInputs, + ); + } + const initialSearchResults: SearchAPIResponse = getErrorInitialSearchResults( - parsedErrorData, pagination_info, - ); - - // The error message search inputs had to be converted to arrays in order to be stringified, - // convert those back to sets as we do in non-error flow. - const convertedSearchParams = convertSearchInputArraysToSets( - parsedErrorData.searchInputs, + parsedErrorData, ); useEffect(() => { @@ -65,15 +76,15 @@ export default function Error({ error }: ErrorProps) { * which otherwise may not have any data. */ function getErrorInitialSearchResults( - parsedError: ParsedError, pagination_info: PaginationInfo, + parsedError: ParsedError, ) { return { - errors: [{ ...parsedError }], + errors: parsedError ? [{ ...parsedError }] : [{}], data: [], pagination_info, - status_code: parsedError.status, - message: parsedError.message, + status_code: parsedError?.status || -1, + message: parsedError?.message || "Unable to parse thrown error", }; } @@ -103,3 +114,30 @@ function convertSearchInputArraysToSets( category: new Set(searchInputs.category || []), }; } + +function isValidJSON(str: string) { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; // String is not valid JSON + } +} + +function getParsedError() { + return { + type: "NetworkError", + searchInputs: { + status: new Set(), + fundingInstrument: new Set(), + eligibility: new Set(), + agency: new Set(), + category: new Set(), + sortby: null, + page: 1, + actionType: "initialLoad", + }, + message: "Invalid JSON returned", + status: -1, + } as ParsedError; +} diff --git a/frontend/src/errors.ts b/frontend/src/errors.ts index c1b1b8805..d0a97b0f4 100644 --- a/frontend/src/errors.ts +++ b/frontend/src/errors.ts @@ -33,6 +33,7 @@ export class BaseFrontendError extends Error { type: string, status?: number, ) { + // Sets cannot be properly serialized so convert to arrays first const serializedSearchInputs = convertSearchInputSetsToArrays(searchInputs); const serializedData = JSON.stringify({