Skip to content

Commit 44f9613

Browse files
feat(site): dismiss health section warnings (#11059)
1 parent 2bc11d2 commit 44f9613

11 files changed

+179
-4
lines changed

site/src/api/api.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,3 +1602,19 @@ export const getHealth = async (force: boolean = false) => {
16021602
);
16031603
return response.data;
16041604
};
1605+
1606+
export const getHealthSettings = async () => {
1607+
return (
1608+
await axios.get<TypesGen.HealthSettings>(`/api/v2/debug/health/settings`)
1609+
).data;
1610+
};
1611+
1612+
export const updateHealthSettings = async (
1613+
data: TypesGen.UpdateHealthSettings,
1614+
) => {
1615+
const response = await axios.put<TypesGen.HealthSettings>(
1616+
`/api/v2/debug/health/settings`,
1617+
data,
1618+
);
1619+
return response.data;
1620+
};

site/src/api/queries/debug.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,45 @@
11
import * as API from "api/api";
2-
import { QueryClient } from "react-query";
2+
import { HealthSettings, UpdateHealthSettings } from "api/typesGenerated";
3+
import { QueryClient, UseMutationOptions } from "react-query";
4+
5+
export const HEALTH_QUERY_KEY = ["health"];
6+
export const HEALTH_QUERY_SETTINGS_KEY = ["health", "settings"];
37

48
export const health = () => ({
5-
queryKey: ["health"],
9+
queryKey: HEALTH_QUERY_KEY,
610
queryFn: async () => API.getHealth(),
711
});
812

913
export const refreshHealth = (queryClient: QueryClient) => {
1014
return {
1115
mutationFn: async () => {
12-
await queryClient.cancelQueries(["health"]);
16+
await queryClient.cancelQueries(HEALTH_QUERY_KEY);
1317
const newHealthData = await API.getHealth(true);
14-
queryClient.setQueryData(["health"], newHealthData);
18+
queryClient.setQueryData(HEALTH_QUERY_KEY, newHealthData);
19+
},
20+
};
21+
};
22+
23+
export const healthSettings = () => {
24+
return {
25+
queryKey: HEALTH_QUERY_SETTINGS_KEY,
26+
queryFn: API.getHealthSettings,
27+
};
28+
};
29+
30+
export const updateHealthSettings = (
31+
queryClient: QueryClient,
32+
): UseMutationOptions<
33+
HealthSettings,
34+
unknown,
35+
UpdateHealthSettings,
36+
unknown
37+
> => {
38+
return {
39+
mutationFn: API.updateHealthSettings,
40+
onSuccess: async (_, newSettings) => {
41+
await queryClient.invalidateQueries(HEALTH_QUERY_KEY);
42+
queryClient.setQueryData(HEALTH_QUERY_SETTINGS_KEY, newSettings);
1543
},
1644
};
1745
};

site/src/pages/HealthPage/AccessURLPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { HealthcheckReport } from "api/typesGenerated";
1212
import { Alert } from "components/Alert/Alert";
1313
import { Helmet } from "react-helmet-async";
1414
import { pageTitle } from "utils/page";
15+
import { DismissWarningButton } from "./DismissWarningButton";
1516

1617
export const AccessURLPage = () => {
1718
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -28,6 +29,7 @@ export const AccessURLPage = () => {
2829
<HealthyDot severity={accessUrl.severity} />
2930
Access URL
3031
</HeaderTitle>
32+
<DismissWarningButton healthcheck="AccessURL" />
3133
</Header>
3234

3335
<Main>

site/src/pages/HealthPage/DERPPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Alert } from "components/Alert/Alert";
2020
import { Helmet } from "react-helmet-async";
2121
import { pageTitle } from "utils/page";
2222
import { useTheme } from "@mui/material/styles";
23+
import { DismissWarningButton } from "./DismissWarningButton";
2324

2425
const flags = [
2526
"UDP",
@@ -52,6 +53,7 @@ export const DERPPage = () => {
5253
<HealthyDot severity={derp.severity as HealthSeverity} />
5354
DERP
5455
</HeaderTitle>
56+
<DismissWarningButton healthcheck="DERP" />
5557
</Header>
5658

5759
<Main>

site/src/pages/HealthPage/DatabasePage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { HealthcheckReport } from "api/typesGenerated";
1212
import { Alert } from "components/Alert/Alert";
1313
import { Helmet } from "react-helmet-async";
1414
import { pageTitle } from "utils/page";
15+
import { DismissWarningButton } from "./DismissWarningButton";
1516

1617
export const DatabasePage = () => {
1718
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -28,6 +29,7 @@ export const DatabasePage = () => {
2829
<HealthyDot severity={database.severity} />
2930
Database
3031
</HeaderTitle>
32+
<DismissWarningButton healthcheck="Database" />
3133
</Header>
3234

3335
<Main>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import NotificationOutlined from "@mui/icons-material/NotificationsOutlined";
2+
import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined";
3+
import { healthSettings, updateHealthSettings } from "api/queries/debug";
4+
import { useMutation, useQuery, useQueryClient } from "react-query";
5+
import { displaySuccess } from "components/GlobalSnackbar/utils";
6+
import LoadingButton from "@mui/lab/LoadingButton";
7+
import Skeleton from "@mui/material/Skeleton";
8+
import { HealthSection } from "api/typesGenerated";
9+
10+
export const DismissWarningButton = (props: { healthcheck: HealthSection }) => {
11+
const queryClient = useQueryClient();
12+
const healthSettingsQuery = useQuery(healthSettings());
13+
// They call the same mutation but are used in diff contexts so we don't want
14+
// to merge their states. Eg. You dismiss a warning and when it is done it
15+
// will show the enable button but since the mutation is still invalidating
16+
// other queries it will be in the loading state when it should be idle.
17+
const enableMutation = useMutation(updateHealthSettings(queryClient));
18+
const dismissMutation = useMutation(updateHealthSettings(queryClient));
19+
20+
if (!healthSettingsQuery.data) {
21+
return (
22+
<Skeleton
23+
variant="rectangular"
24+
height={36}
25+
width={170}
26+
css={{ borderRadius: 8 }}
27+
/>
28+
);
29+
}
30+
31+
const { dismissed_healthchecks } = healthSettingsQuery.data;
32+
const isDismissed = dismissed_healthchecks.includes(props.healthcheck);
33+
34+
if (isDismissed) {
35+
return (
36+
<LoadingButton
37+
disabled={healthSettingsQuery.isLoading}
38+
loading={enableMutation.isLoading}
39+
loadingPosition="start"
40+
startIcon={<NotificationsOffOutlined />}
41+
onClick={async () => {
42+
const updatedSettings = dismissed_healthchecks.filter(
43+
(dismissedHealthcheck) =>
44+
dismissedHealthcheck !== props.healthcheck,
45+
);
46+
await enableMutation.mutateAsync({
47+
dismissed_healthchecks: updatedSettings,
48+
});
49+
displaySuccess("Warnings enabled successfully!");
50+
}}
51+
>
52+
Enable warnings
53+
</LoadingButton>
54+
);
55+
}
56+
57+
return (
58+
<LoadingButton
59+
disabled={healthSettingsQuery.isLoading}
60+
loading={dismissMutation.isLoading}
61+
loadingPosition="start"
62+
startIcon={<NotificationOutlined />}
63+
onClick={async () => {
64+
const updatedSettings = [...dismissed_healthchecks, props.healthcheck];
65+
await dismissMutation.mutateAsync({
66+
dismissed_healthchecks: updatedSettings,
67+
});
68+
displaySuccess("Warnings dismissed successfully!");
69+
}}
70+
>
71+
Dismiss warnings
72+
</LoadingButton>
73+
);
74+
};

site/src/pages/HealthPage/HealthLayout.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { kebabCase } from "lodash/fp";
1616
import { Suspense } from "react";
1717
import { HealthIcon } from "./Content";
1818
import { HealthSeverity } from "api/typesGenerated";
19+
import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined";
1920

2021
const sections = {
2122
derp: "DERP",
@@ -189,6 +190,15 @@ export function HealthLayout() {
189190
severity={healthSection.severity as HealthSeverity}
190191
/>
191192
{label}
193+
{healthSection.dismissed && (
194+
<NotificationsOffOutlined
195+
css={{
196+
fontSize: 14,
197+
marginLeft: "auto",
198+
color: theme.palette.text.disabled,
199+
}}
200+
/>
201+
)}
192202
</NavLink>
193203
);
194204
})}

site/src/pages/HealthPage/WebsocketPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { MONOSPACE_FONT_FAMILY } from "theme/constants";
1515
import { Alert } from "components/Alert/Alert";
1616
import { pageTitle } from "utils/page";
1717
import { Helmet } from "react-helmet-async";
18+
import { DismissWarningButton } from "./DismissWarningButton";
1819

1920
export const WebsocketPage = () => {
2021
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -32,6 +33,7 @@ export const WebsocketPage = () => {
3233
<HealthyDot severity={websocket.severity} />
3334
Websocket
3435
</HeaderTitle>
36+
<DismissWarningButton healthcheck="Websocket" />
3537
</Header>
3638

3739
<Main>

site/src/pages/HealthPage/WorkspaceProxyPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import TagOutlined from "@mui/icons-material/TagOutlined";
1616
import { Alert } from "components/Alert/Alert";
1717
import { Helmet } from "react-helmet-async";
1818
import { pageTitle } from "utils/page";
19+
import { DismissWarningButton } from "./DismissWarningButton";
1920

2021
export const WorkspaceProxyPage = () => {
2122
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -34,6 +35,7 @@ export const WorkspaceProxyPage = () => {
3435
<HealthyDot severity={workspace_proxy.severity} />
3536
Workspace Proxy
3637
</HeaderTitle>
38+
<DismissWarningButton healthcheck="WorkspaceProxy" />
3739
</Header>
3840

3941
<Main>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useQueryClient } from "react-query";
2+
import {
3+
reactRouterParameters,
4+
reactRouterOutlet,
5+
RouteDefinition,
6+
} from "storybook-addon-react-router-v6";
7+
import { MockHealth, MockHealthSettings } from "testHelpers/entities";
8+
import { Meta } from "@storybook/react";
9+
import { HEALTH_QUERY_KEY, HEALTH_QUERY_SETTINGS_KEY } from "api/queries/debug";
10+
11+
type MetaOptions = {
12+
element: RouteDefinition;
13+
path: string;
14+
};
15+
16+
export const generateMeta = ({ element, path }: MetaOptions): Meta => {
17+
return {
18+
parameters: {
19+
layout: "fullscreen",
20+
reactRouter: reactRouterParameters({
21+
routing: reactRouterOutlet({ path: `/health/${path}` }, element),
22+
}),
23+
},
24+
decorators: [
25+
(Story) => {
26+
const queryClient = useQueryClient();
27+
queryClient.setQueryData(HEALTH_QUERY_KEY, MockHealth);
28+
queryClient.setQueryData(HEALTH_QUERY_SETTINGS_KEY, MockHealthSettings);
29+
return <Story />;
30+
},
31+
],
32+
};
33+
};

site/src/testHelpers/entities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3149,6 +3149,10 @@ export const DeploymentHealthUnhealthy: TypesGen.HealthcheckReport = {
31493149
},
31503150
};
31513151

3152+
export const MockHealthSettings: TypesGen.HealthSettings = {
3153+
dismissed_healthchecks: [],
3154+
};
3155+
31523156
export const MockGithubExternalProvider: TypesGen.ExternalAuthLinkProvider = {
31533157
id: "github",
31543158
type: "github",

0 commit comments

Comments
 (0)