From 436e900d629e023701233a13b8d253f2841b7bc8 Mon Sep 17 00:00:00 2001 From: Jacob Shandling <61553566+jacobshandling@users.noreply.github.com> Date: Tue, 20 Feb 2024 10:28:16 -0800 Subject: [PATCH] =?UTF-8?q?UI=20=E2=80=93=C2=A0update=204=20Software=20>?= =?UTF-8?q?=20details=20pages=20with=20desired=20empty=20state=20for=20404?= =?UTF-8?q?=20responses=20(#16944)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Addresses #16948 --------- Co-authored-by: Jacob Shandling --- .../SoftwareOSDetailsPage.tsx | 64 +++++++------ .../SoftwareTitleDetailsPage.tsx | 73 ++++++++------- .../SoftwareVersionDetailsPage.tsx | 70 +++++++------- .../SoftwareVulnerabilityDetailsPage.tsx | 92 +++++++++---------- 4 files changed, 156 insertions(+), 143 deletions(-) diff --git a/frontend/pages/SoftwarePage/SoftwareOSDetailsPage/SoftwareOSDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareOSDetailsPage/SoftwareOSDetailsPage.tsx index be89e32bf5..fa07fa6e65 100644 --- a/frontend/pages/SoftwarePage/SoftwareOSDetailsPage/SoftwareOSDetailsPage.tsx +++ b/frontend/pages/SoftwarePage/SoftwareOSDetailsPage/SoftwareOSDetailsPage.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react"; import { useQuery } from "react-query"; import { useErrorHandler } from "react-error-boundary"; import { RouteComponentProps } from "react-router"; -import { AxiosError } from "axios"; +import { AxiosError, isAxiosError } from "axios"; import useTeamIdParam from "hooks/useTeamIdParam"; @@ -18,7 +18,6 @@ import { IOperatingSystemVersion } from "interfaces/operating_system"; import { SUPPORT_LINK } from "utilities/constants"; import Spinner from "components/Spinner"; -import TableDataError from "components/DataError"; import MainContent from "components/MainContent"; import EmptyTable from "components/EmptyTable"; import CustomLink from "components/CustomLink"; @@ -103,7 +102,13 @@ const SoftwareOSDetailsPage = ({ { enabled: !!osVersionIdFromURL, select: (data) => data.os_version, - onError: (error) => handlePageError(error), + onError: (error) => { + // 403s returned for both non-existent and non-accessable entities + // which we intentionally handle with the same empty state for security + if (isAxiosError(error) && error.response?.status !== 403) { + handlePageError(error); + } + }, } ); @@ -142,11 +147,7 @@ const SoftwareOSDetailsPage = ({ return ; } - if (isOsVersionError) { - return ; - } - - if (!osVersionDetails) { + if (!osVersionDetails && !isOsVersionError) { return null; } @@ -160,32 +161,35 @@ const SoftwareOSDetailsPage = ({ onTeamChange={onTeamChange} /> )} - - {osVersionDetails.hosts_count === 0 ? ( + {/* at this point, error can only be 403 per above handling */} + {isOsVersionError ? ( ) : ( - -

Vulnerabilities

- {renderTable()} -
+ <> + + +

Vulnerabilities

+ {renderTable()} +
+ )} ); diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsx index ff62336dc8..d03c131fbd 100644 --- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsx +++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react"; import { useQuery } from "react-query"; import { useErrorHandler } from "react-error-boundary"; import { RouteComponentProps } from "react-router"; -import { AxiosError } from "axios"; +import { AxiosError, isAxiosError } from "axios"; import useTeamIdParam from "hooks/useTeamIdParam"; @@ -17,7 +17,6 @@ import softwareAPI, { } from "services/entities/software"; import Spinner from "components/Spinner"; -import TableDataError from "components/DataError"; import MainContent from "components/MainContent"; import TeamsHeader from "components/TeamsHeader"; import Card from "components/Card"; @@ -75,7 +74,13 @@ const SoftwareTitleDetailsPage = ({ ({ queryKey }) => softwareAPI.getSoftwareTitle(queryKey[0]), { select: (data) => data.software_title, - onError: (error) => handlePageError(error), + onError: (error) => { + // 403s returned for both non-existent and non-accessable entities + // which we intentionally handle with the same empty state for security + if (isAxiosError(error) && error.response?.status !== 403) { + handlePageError(error); + } + }, } ); @@ -91,11 +96,7 @@ const SoftwareTitleDetailsPage = ({ return ; } - if (isSoftwareTitleError) { - return ; - } - - if (!softwareTitle) { + if (!softwareTitle && !isSoftwareTitleError) { return null; } return ( @@ -108,36 +109,42 @@ const SoftwareTitleDetailsPage = ({ onTeamChange={onTeamChange} /> )} - - {softwareTitle.hosts_count === 0 ? ( + {/* at this point, error can only be 403 per above handling */} + {isSoftwareTitleError ? ( ) : ( - -

Versions

- + -
+ +

Versions

+ +
+ )} ); diff --git a/frontend/pages/SoftwarePage/SoftwareVersionDetailsPage/SoftwareVersionDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareVersionDetailsPage/SoftwareVersionDetailsPage.tsx index 60d4a81ffe..ac8c32f9f4 100644 --- a/frontend/pages/SoftwarePage/SoftwareVersionDetailsPage/SoftwareVersionDetailsPage.tsx +++ b/frontend/pages/SoftwarePage/SoftwareVersionDetailsPage/SoftwareVersionDetailsPage.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react"; import { useQuery } from "react-query"; import { useErrorHandler } from "react-error-boundary"; import { RouteComponentProps } from "react-router"; -import { AxiosError } from "axios"; +import { AxiosError, isAxiosError } from "axios"; import useTeamIdParam from "hooks/useTeamIdParam"; @@ -21,7 +21,6 @@ import hostsCountAPI, { import { ISoftwareVersion, formatSoftwareType } from "interfaces/software"; import Spinner from "components/Spinner"; -import TableDataError from "components/DataError"; import MainContent from "components/MainContent"; import TeamsHeader from "components/TeamsHeader"; import Card from "components/Card"; @@ -78,7 +77,13 @@ const SoftwareVersionDetailsPage = ({ ({ queryKey }) => softwareAPI.getSoftwareVersion(queryKey[0]), { select: (data) => data.software, - onError: (error) => handlePageError(error), + onError: (error) => { + // 403s returned for both non-existent and non-accessable entities + // which we intentionally handle with the same empty state for security + if (isAxiosError(error) && error.response?.status !== 403) { + handlePageError(error); + } + }, } ); @@ -109,11 +114,7 @@ const SoftwareVersionDetailsPage = ({ return ; } - if (isSoftwareVersionError) { - return ; - } - - if (!softwareVersion) { + if (!softwareVersion && !isSoftwareVersionError) { return null; } @@ -127,18 +128,8 @@ const SoftwareVersionDetailsPage = ({ onTeamChange={onTeamChange} /> )} - - {softwareVersion.hosts_count === 0 ? ( + {/* at this point, error can only be 403 per above handling */} + {isSoftwareVersionError ? ( ) : ( - -

Vulnerabilities

- + -
+ +

Vulnerabilities

+ +
+ )} ); diff --git a/frontend/pages/SoftwarePage/SoftwareVulnerabilityDetailsPage/SoftwareVulnerabilityDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareVulnerabilityDetailsPage/SoftwareVulnerabilityDetailsPage.tsx index 85efe98a6f..5900afb05e 100644 --- a/frontend/pages/SoftwarePage/SoftwareVulnerabilityDetailsPage/SoftwareVulnerabilityDetailsPage.tsx +++ b/frontend/pages/SoftwarePage/SoftwareVulnerabilityDetailsPage/SoftwareVulnerabilityDetailsPage.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react"; import { useQuery } from "react-query"; import { useErrorHandler } from "react-error-boundary"; import { RouteComponentProps } from "react-router"; -import { AxiosError } from "axios"; +import { AxiosError, isAxiosError } from "axios"; import useTeamIdParam from "hooks/useTeamIdParam"; @@ -17,7 +17,6 @@ import softwareVulnAPI, { } from "services/entities/vulnerabilities"; import Spinner from "components/Spinner"; -import DataError from "components/DataError"; import MainContent from "components/MainContent"; import TeamsHeader from "components/TeamsHeader"; @@ -80,7 +79,13 @@ const SoftwareVulnerabilityDetailsPage = ({ }, { select: (data) => data.vulnerability, - onError: (error) => handlePageError(error), + onError: (error) => { + // 403s returned for both non-existent and non-accessable entities + // which we intentionally handle with the same empty state for security + if (isAxiosError(error) && error.response?.status !== 403) { + handlePageError(error); + } + }, } ); @@ -91,49 +96,37 @@ const SoftwareVulnerabilityDetailsPage = ({ [handleTeamChange] ); - const renderVulnTables = () => { - // always the case, just for typing - if (vuln) { - if (vuln.hosts_count === 0) { - return ( - - ); - } - return ( - <> - {!!vuln.os_versions && vuln.os_versions.length > 0 && ( - - )} - {!!vuln.software && vuln.software.length > 0 && ( - - )} - - ); - } - }; + const renderCards = (v: IVulnerability) => ( + <> + + {!!v.os_versions && v.os_versions.length > 0 && ( + + )} + + {!!v.software && v.software.length > 0 && ( + + )} + + ); const renderContent = () => { if (isVulnLoading || !vuln) { return ; } - if (isVulnError) { - return ; - } return ( <> {isPremiumTier && ( @@ -144,12 +137,17 @@ const SoftwareVulnerabilityDetailsPage = ({ onTeamChange={onTeamChange} /> )} - - {renderVulnTables()} + {/* at this point, error can only be 403 per above handling */} + {isVulnError ? ( + + ) : ( + renderCards(vuln) + )} ); };