Skip to content

Commit 436e900

Browse files
jacobshandlingJacob Shandling
and
Jacob Shandling
authored
UI – update 4 Software > details pages with desired empty state for 404 responses (#16944)
## Addresses #16948 --------- Co-authored-by: Jacob Shandling <[email protected]>
1 parent 9ed0c19 commit 436e900

File tree

4 files changed

+156
-143
lines changed

4 files changed

+156
-143
lines changed

frontend/pages/SoftwarePage/SoftwareOSDetailsPage/SoftwareOSDetailsPage.tsx

+34-30
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react";
44
import { useQuery } from "react-query";
55
import { useErrorHandler } from "react-error-boundary";
66
import { RouteComponentProps } from "react-router";
7-
import { AxiosError } from "axios";
7+
import { AxiosError, isAxiosError } from "axios";
88

99
import useTeamIdParam from "hooks/useTeamIdParam";
1010

@@ -18,7 +18,6 @@ import { IOperatingSystemVersion } from "interfaces/operating_system";
1818
import { SUPPORT_LINK } from "utilities/constants";
1919

2020
import Spinner from "components/Spinner";
21-
import TableDataError from "components/DataError";
2221
import MainContent from "components/MainContent";
2322
import EmptyTable from "components/EmptyTable";
2423
import CustomLink from "components/CustomLink";
@@ -103,7 +102,13 @@ const SoftwareOSDetailsPage = ({
103102
{
104103
enabled: !!osVersionIdFromURL,
105104
select: (data) => data.os_version,
106-
onError: (error) => handlePageError(error),
105+
onError: (error) => {
106+
// 403s returned for both non-existent and non-accessable entities
107+
// which we intentionally handle with the same empty state for security
108+
if (isAxiosError(error) && error.response?.status !== 403) {
109+
handlePageError(error);
110+
}
111+
},
107112
}
108113
);
109114

@@ -142,11 +147,7 @@ const SoftwareOSDetailsPage = ({
142147
return <Spinner />;
143148
}
144149

145-
if (isOsVersionError) {
146-
return <TableDataError className={`${baseClass}__table-error`} />;
147-
}
148-
149-
if (!osVersionDetails) {
150+
if (!osVersionDetails && !isOsVersionError) {
150151
return null;
151152
}
152153

@@ -160,32 +161,35 @@ const SoftwareOSDetailsPage = ({
160161
onTeamChange={onTeamChange}
161162
/>
162163
)}
163-
<SoftwareDetailsSummary
164-
title={osVersionDetails.name}
165-
hosts={osVersionDetails.hosts_count}
166-
queryParams={{
167-
os_name: osVersionDetails.name_only,
168-
os_version: osVersionDetails.version,
169-
team_id: teamIdForApi,
170-
}}
171-
name={osVersionDetails.platform}
172-
/>
173-
{osVersionDetails.hosts_count === 0 ? (
164+
{/* at this point, error can only be 403 per above handling */}
165+
{isOsVersionError ? (
174166
<DetailsNoHosts
175167
header="OS not detected"
176-
details={`No hosts ${teamIdForApi ? "on this team " : ""}have ${
177-
osVersionDetails.name
178-
} installed.`}
168+
details={`No hosts ${
169+
teamIdForApi ? "on this team " : ""
170+
}have this OS installed.`}
179171
/>
180172
) : (
181-
<Card
182-
borderRadiusSize="large"
183-
includeShadow
184-
className={`${baseClass}__vulnerabilities-section`}
185-
>
186-
<h2>Vulnerabilities</h2>
187-
{renderTable()}
188-
</Card>
173+
<>
174+
<SoftwareDetailsSummary
175+
title={osVersionDetails.name}
176+
hosts={osVersionDetails.hosts_count}
177+
queryParams={{
178+
os_name: osVersionDetails.name_only,
179+
os_version: osVersionDetails.version,
180+
team_id: teamIdForApi,
181+
}}
182+
name={osVersionDetails.platform}
183+
/>
184+
<Card
185+
borderRadiusSize="large"
186+
includeShadow
187+
className={`${baseClass}__vulnerabilities-section`}
188+
>
189+
<h2>Vulnerabilities</h2>
190+
{renderTable()}
191+
</Card>
192+
</>
189193
)}
190194
</>
191195
);

frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/SoftwareTitleDetailsPage.tsx

+40-33
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react";
44
import { useQuery } from "react-query";
55
import { useErrorHandler } from "react-error-boundary";
66
import { RouteComponentProps } from "react-router";
7-
import { AxiosError } from "axios";
7+
import { AxiosError, isAxiosError } from "axios";
88

99
import useTeamIdParam from "hooks/useTeamIdParam";
1010

@@ -17,7 +17,6 @@ import softwareAPI, {
1717
} from "services/entities/software";
1818

1919
import Spinner from "components/Spinner";
20-
import TableDataError from "components/DataError";
2120
import MainContent from "components/MainContent";
2221
import TeamsHeader from "components/TeamsHeader";
2322
import Card from "components/Card";
@@ -75,7 +74,13 @@ const SoftwareTitleDetailsPage = ({
7574
({ queryKey }) => softwareAPI.getSoftwareTitle(queryKey[0]),
7675
{
7776
select: (data) => data.software_title,
78-
onError: (error) => handlePageError(error),
77+
onError: (error) => {
78+
// 403s returned for both non-existent and non-accessable entities
79+
// which we intentionally handle with the same empty state for security
80+
if (isAxiosError(error) && error.response?.status !== 403) {
81+
handlePageError(error);
82+
}
83+
},
7984
}
8085
);
8186

@@ -91,11 +96,7 @@ const SoftwareTitleDetailsPage = ({
9196
return <Spinner />;
9297
}
9398

94-
if (isSoftwareTitleError) {
95-
return <TableDataError className={`${baseClass}__table-error`} />;
96-
}
97-
98-
if (!softwareTitle) {
99+
if (!softwareTitle && !isSoftwareTitleError) {
99100
return null;
100101
}
101102
return (
@@ -108,36 +109,42 @@ const SoftwareTitleDetailsPage = ({
108109
onTeamChange={onTeamChange}
109110
/>
110111
)}
111-
<SoftwareDetailsSummary
112-
title={softwareTitle.name}
113-
type={formatSoftwareType(softwareTitle)}
114-
versions={softwareTitle.versions?.length ?? 0}
115-
hosts={softwareTitle.hosts_count}
116-
queryParams={{ software_title_id: softwareId, team_id: teamIdForApi }}
117-
name={softwareTitle.name}
118-
source={softwareTitle.source}
119-
/>
120-
{softwareTitle.hosts_count === 0 ? (
112+
{/* at this point, error can only be 403 per above handling */}
113+
{isSoftwareTitleError ? (
121114
<DetailsNoHosts
122115
header="Software not detected"
123-
details={`No hosts ${teamIdForApi ? "on this team " : ""}have ${
124-
softwareTitle.name
125-
} installed.`}
116+
details={`No hosts ${
117+
teamIdForApi ? "on this team " : ""
118+
}have this software installed.`}
126119
/>
127120
) : (
128-
<Card
129-
borderRadiusSize="large"
130-
includeShadow
131-
className={`${baseClass}__versions-section`}
132-
>
133-
<h2>Versions</h2>
134-
<SoftwareTitleDetailsTable
135-
router={router}
136-
data={softwareTitle.versions ?? []}
137-
isLoading={isSoftwareTitleLoading}
138-
teamIdForApi={teamIdForApi}
121+
<>
122+
<SoftwareDetailsSummary
123+
title={softwareTitle.name}
124+
type={formatSoftwareType(softwareTitle)}
125+
versions={softwareTitle.versions?.length ?? 0}
126+
hosts={softwareTitle.hosts_count}
127+
queryParams={{
128+
software_title_id: softwareId,
129+
team_id: teamIdForApi,
130+
}}
131+
name={softwareTitle.name}
132+
source={softwareTitle.source}
139133
/>
140-
</Card>
134+
<Card
135+
borderRadiusSize="large"
136+
includeShadow
137+
className={`${baseClass}__versions-section`}
138+
>
139+
<h2>Versions</h2>
140+
<SoftwareTitleDetailsTable
141+
router={router}
142+
data={softwareTitle.versions ?? []}
143+
isLoading={isSoftwareTitleLoading}
144+
teamIdForApi={teamIdForApi}
145+
/>
146+
</Card>
147+
</>
141148
)}
142149
</>
143150
);

frontend/pages/SoftwarePage/SoftwareVersionDetailsPage/SoftwareVersionDetailsPage.tsx

+37-33
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useCallback, useContext } from "react";
44
import { useQuery } from "react-query";
55
import { useErrorHandler } from "react-error-boundary";
66
import { RouteComponentProps } from "react-router";
7-
import { AxiosError } from "axios";
7+
import { AxiosError, isAxiosError } from "axios";
88

99
import useTeamIdParam from "hooks/useTeamIdParam";
1010

@@ -21,7 +21,6 @@ import hostsCountAPI, {
2121
import { ISoftwareVersion, formatSoftwareType } from "interfaces/software";
2222

2323
import Spinner from "components/Spinner";
24-
import TableDataError from "components/DataError";
2524
import MainContent from "components/MainContent";
2625
import TeamsHeader from "components/TeamsHeader";
2726
import Card from "components/Card";
@@ -78,7 +77,13 @@ const SoftwareVersionDetailsPage = ({
7877
({ queryKey }) => softwareAPI.getSoftwareVersion(queryKey[0]),
7978
{
8079
select: (data) => data.software,
81-
onError: (error) => handlePageError(error),
80+
onError: (error) => {
81+
// 403s returned for both non-existent and non-accessable entities
82+
// which we intentionally handle with the same empty state for security
83+
if (isAxiosError(error) && error.response?.status !== 403) {
84+
handlePageError(error);
85+
}
86+
},
8287
}
8388
);
8489

@@ -109,11 +114,7 @@ const SoftwareVersionDetailsPage = ({
109114
return <Spinner />;
110115
}
111116

112-
if (isSoftwareVersionError) {
113-
return <TableDataError className={`${baseClass}__table-error`} />;
114-
}
115-
116-
if (!softwareVersion) {
117+
if (!softwareVersion && !isSoftwareVersionError) {
117118
return null;
118119
}
119120

@@ -127,39 +128,42 @@ const SoftwareVersionDetailsPage = ({
127128
onTeamChange={onTeamChange}
128129
/>
129130
)}
130-
<SoftwareDetailsSummary
131-
title={`${softwareVersion.name}, ${softwareVersion.version}`}
132-
type={formatSoftwareType(softwareVersion)}
133-
hosts={hostsCount ?? 0}
134-
queryParams={{
135-
software_version_id: softwareVersion.id,
136-
team_id: teamIdForApi,
137-
}}
138-
name={softwareVersion.name}
139-
source={softwareVersion.source}
140-
/>
141-
{softwareVersion.hosts_count === 0 ? (
131+
{/* at this point, error can only be 403 per above handling */}
132+
{isSoftwareVersionError ? (
142133
<DetailsNoHosts
143134
header="Software not detected"
144135
details={`No hosts ${
145136
teamIdForApi ? "on this team " : ""
146137
}have this software installed.`}
147138
/>
148139
) : (
149-
<Card
150-
borderRadiusSize="large"
151-
includeShadow
152-
className={`${baseClass}__vulnerabilities-section`}
153-
>
154-
<h2 className="section__header">Vulnerabilities</h2>
155-
<SoftwareVulnerabilitiesTable
156-
data={softwareVersion.vulnerabilities ?? []}
157-
itemName="software item"
158-
isLoading={isSoftwareVersionLoading}
159-
router={router}
160-
teamIdForApi={teamIdForApi}
140+
<>
141+
<SoftwareDetailsSummary
142+
title={`${softwareVersion.name}, ${softwareVersion.version}`}
143+
type={formatSoftwareType(softwareVersion)}
144+
hosts={hostsCount ?? 0}
145+
queryParams={{
146+
software_version_id: softwareVersion.id,
147+
team_id: teamIdForApi,
148+
}}
149+
name={softwareVersion.name}
150+
source={softwareVersion.source}
161151
/>
162-
</Card>
152+
<Card
153+
borderRadiusSize="large"
154+
includeShadow
155+
className={`${baseClass}__vulnerabilities-section`}
156+
>
157+
<h2 className="section__header">Vulnerabilities</h2>
158+
<SoftwareVulnerabilitiesTable
159+
data={softwareVersion.vulnerabilities ?? []}
160+
itemName="software item"
161+
isLoading={isSoftwareVersionLoading}
162+
router={router}
163+
teamIdForApi={teamIdForApi}
164+
/>
165+
</Card>
166+
</>
163167
)}
164168
</>
165169
);

0 commit comments

Comments
 (0)