feat(site): dismiss health section warnings (#11059)

This commit is contained in:
Bruno Quaresma
2023-12-06 12:50:35 -03:00
committed by GitHub
parent 2bc11d2e63
commit 44f9613bf2
11 changed files with 179 additions and 4 deletions
+16
View File
@@ -1602,3 +1602,19 @@ export const getHealth = async (force: boolean = false) => {
);
return response.data;
};
export const getHealthSettings = async () => {
return (
await axios.get<TypesGen.HealthSettings>(`/api/v2/debug/health/settings`)
).data;
};
export const updateHealthSettings = async (
data: TypesGen.UpdateHealthSettings,
) => {
const response = await axios.put<TypesGen.HealthSettings>(
`/api/v2/debug/health/settings`,
data,
);
return response.data;
};
+32 -4
View File
@@ -1,17 +1,45 @@
import * as API from "api/api";
import { QueryClient } from "react-query";
import { HealthSettings, UpdateHealthSettings } from "api/typesGenerated";
import { QueryClient, UseMutationOptions } from "react-query";
export const HEALTH_QUERY_KEY = ["health"];
export const HEALTH_QUERY_SETTINGS_KEY = ["health", "settings"];
export const health = () => ({
queryKey: ["health"],
queryKey: HEALTH_QUERY_KEY,
queryFn: async () => API.getHealth(),
});
export const refreshHealth = (queryClient: QueryClient) => {
return {
mutationFn: async () => {
await queryClient.cancelQueries(["health"]);
await queryClient.cancelQueries(HEALTH_QUERY_KEY);
const newHealthData = await API.getHealth(true);
queryClient.setQueryData(["health"], newHealthData);
queryClient.setQueryData(HEALTH_QUERY_KEY, newHealthData);
},
};
};
export const healthSettings = () => {
return {
queryKey: HEALTH_QUERY_SETTINGS_KEY,
queryFn: API.getHealthSettings,
};
};
export const updateHealthSettings = (
queryClient: QueryClient,
): UseMutationOptions<
HealthSettings,
unknown,
UpdateHealthSettings,
unknown
> => {
return {
mutationFn: API.updateHealthSettings,
onSuccess: async (_, newSettings) => {
await queryClient.invalidateQueries(HEALTH_QUERY_KEY);
queryClient.setQueryData(HEALTH_QUERY_SETTINGS_KEY, newSettings);
},
};
};
@@ -12,6 +12,7 @@ import { HealthcheckReport } from "api/typesGenerated";
import { Alert } from "components/Alert/Alert";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { DismissWarningButton } from "./DismissWarningButton";
export const AccessURLPage = () => {
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -28,6 +29,7 @@ export const AccessURLPage = () => {
<HealthyDot severity={accessUrl.severity} />
Access URL
</HeaderTitle>
<DismissWarningButton healthcheck="AccessURL" />
</Header>
<Main>
+2
View File
@@ -20,6 +20,7 @@ import { Alert } from "components/Alert/Alert";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useTheme } from "@mui/material/styles";
import { DismissWarningButton } from "./DismissWarningButton";
const flags = [
"UDP",
@@ -52,6 +53,7 @@ export const DERPPage = () => {
<HealthyDot severity={derp.severity as HealthSeverity} />
DERP
</HeaderTitle>
<DismissWarningButton healthcheck="DERP" />
</Header>
<Main>
@@ -12,6 +12,7 @@ import { HealthcheckReport } from "api/typesGenerated";
import { Alert } from "components/Alert/Alert";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { DismissWarningButton } from "./DismissWarningButton";
export const DatabasePage = () => {
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -28,6 +29,7 @@ export const DatabasePage = () => {
<HealthyDot severity={database.severity} />
Database
</HeaderTitle>
<DismissWarningButton healthcheck="Database" />
</Header>
<Main>
@@ -0,0 +1,74 @@
import NotificationOutlined from "@mui/icons-material/NotificationsOutlined";
import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined";
import { healthSettings, updateHealthSettings } from "api/queries/debug";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import LoadingButton from "@mui/lab/LoadingButton";
import Skeleton from "@mui/material/Skeleton";
import { HealthSection } from "api/typesGenerated";
export const DismissWarningButton = (props: { healthcheck: HealthSection }) => {
const queryClient = useQueryClient();
const healthSettingsQuery = useQuery(healthSettings());
// They call the same mutation but are used in diff contexts so we don't want
// to merge their states. Eg. You dismiss a warning and when it is done it
// will show the enable button but since the mutation is still invalidating
// other queries it will be in the loading state when it should be idle.
const enableMutation = useMutation(updateHealthSettings(queryClient));
const dismissMutation = useMutation(updateHealthSettings(queryClient));
if (!healthSettingsQuery.data) {
return (
<Skeleton
variant="rectangular"
height={36}
width={170}
css={{ borderRadius: 8 }}
/>
);
}
const { dismissed_healthchecks } = healthSettingsQuery.data;
const isDismissed = dismissed_healthchecks.includes(props.healthcheck);
if (isDismissed) {
return (
<LoadingButton
disabled={healthSettingsQuery.isLoading}
loading={enableMutation.isLoading}
loadingPosition="start"
startIcon={<NotificationsOffOutlined />}
onClick={async () => {
const updatedSettings = dismissed_healthchecks.filter(
(dismissedHealthcheck) =>
dismissedHealthcheck !== props.healthcheck,
);
await enableMutation.mutateAsync({
dismissed_healthchecks: updatedSettings,
});
displaySuccess("Warnings enabled successfully!");
}}
>
Enable warnings
</LoadingButton>
);
}
return (
<LoadingButton
disabled={healthSettingsQuery.isLoading}
loading={dismissMutation.isLoading}
loadingPosition="start"
startIcon={<NotificationOutlined />}
onClick={async () => {
const updatedSettings = [...dismissed_healthchecks, props.healthcheck];
await dismissMutation.mutateAsync({
dismissed_healthchecks: updatedSettings,
});
displaySuccess("Warnings dismissed successfully!");
}}
>
Dismiss warnings
</LoadingButton>
);
};
@@ -16,6 +16,7 @@ import { kebabCase } from "lodash/fp";
import { Suspense } from "react";
import { HealthIcon } from "./Content";
import { HealthSeverity } from "api/typesGenerated";
import NotificationsOffOutlined from "@mui/icons-material/NotificationsOffOutlined";
const sections = {
derp: "DERP",
@@ -189,6 +190,15 @@ export function HealthLayout() {
severity={healthSection.severity as HealthSeverity}
/>
{label}
{healthSection.dismissed && (
<NotificationsOffOutlined
css={{
fontSize: 14,
marginLeft: "auto",
color: theme.palette.text.disabled,
}}
/>
)}
</NavLink>
);
})}
@@ -15,6 +15,7 @@ import { MONOSPACE_FONT_FAMILY } from "theme/constants";
import { Alert } from "components/Alert/Alert";
import { pageTitle } from "utils/page";
import { Helmet } from "react-helmet-async";
import { DismissWarningButton } from "./DismissWarningButton";
export const WebsocketPage = () => {
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -32,6 +33,7 @@ export const WebsocketPage = () => {
<HealthyDot severity={websocket.severity} />
Websocket
</HeaderTitle>
<DismissWarningButton healthcheck="Websocket" />
</Header>
<Main>
@@ -16,6 +16,7 @@ import TagOutlined from "@mui/icons-material/TagOutlined";
import { Alert } from "components/Alert/Alert";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { DismissWarningButton } from "./DismissWarningButton";
export const WorkspaceProxyPage = () => {
const healthStatus = useOutletContext<HealthcheckReport>();
@@ -34,6 +35,7 @@ export const WorkspaceProxyPage = () => {
<HealthyDot severity={workspace_proxy.severity} />
Workspace Proxy
</HeaderTitle>
<DismissWarningButton healthcheck="WorkspaceProxy" />
</Header>
<Main>
+33
View File
@@ -0,0 +1,33 @@
import { useQueryClient } from "react-query";
import {
reactRouterParameters,
reactRouterOutlet,
RouteDefinition,
} from "storybook-addon-react-router-v6";
import { MockHealth, MockHealthSettings } from "testHelpers/entities";
import { Meta } from "@storybook/react";
import { HEALTH_QUERY_KEY, HEALTH_QUERY_SETTINGS_KEY } from "api/queries/debug";
type MetaOptions = {
element: RouteDefinition;
path: string;
};
export const generateMeta = ({ element, path }: MetaOptions): Meta => {
return {
parameters: {
layout: "fullscreen",
reactRouter: reactRouterParameters({
routing: reactRouterOutlet({ path: `/health/${path}` }, element),
}),
},
decorators: [
(Story) => {
const queryClient = useQueryClient();
queryClient.setQueryData(HEALTH_QUERY_KEY, MockHealth);
queryClient.setQueryData(HEALTH_QUERY_SETTINGS_KEY, MockHealthSettings);
return <Story />;
},
],
};
};
+4
View File
@@ -3149,6 +3149,10 @@ export const DeploymentHealthUnhealthy: TypesGen.HealthcheckReport = {
},
};
export const MockHealthSettings: TypesGen.HealthSettings = {
dismissed_healthchecks: [],
};
export const MockGithubExternalProvider: TypesGen.ExternalAuthLinkProvider = {
id: "github",
type: "github",