Add health check toast notification for Instance list and detail views.

This commit is contained in:
Kia Lam
2022-09-13 08:53:31 -07:00
committed by Jeff Bradberry
parent 0510978516
commit 4a41098b24
7 changed files with 226 additions and 277 deletions

View File

@@ -1,7 +1,15 @@
import React from 'react';
import { t } from '@lingui/macro';
import { Alert, Button, AlertActionCloseButton } from '@patternfly/react-core';
import {
Alert as PFAlert,
Button,
AlertActionCloseButton,
} from '@patternfly/react-core';
import styled from 'styled-components';
const Alert = styled(PFAlert)`
z-index: 1;
`;
function HealthCheckAlert({ onSetHealthCheckAlert }) {
return (
<Alert
@@ -15,7 +23,7 @@ function HealthCheckAlert({ onSetHealthCheckAlert }) {
<Button
variant="link"
isInline
onClick={() => window.location.reload(false)}
onClick={() => window.location.reload()}
>{t`Reload`}</Button>
</>
}

View File

@@ -28,6 +28,7 @@ import RoutedTabs from 'components/RoutedTabs';
import ContentError from 'components/ContentError';
import ContentLoading from 'components/ContentLoading';
import { Detail, DetailList } from 'components/DetailList';
import HealthCheckAlert from 'components/HealthCheckAlert';
import StatusLabel from 'components/StatusLabel';
import useRequest, {
useDeleteItems,
@@ -66,6 +67,7 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
const history = useHistory();
const [healthCheck, setHealthCheck] = useState({});
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
const [forks, setForks] = useState();
const {
@@ -79,7 +81,6 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
data: { results },
} = await InstanceGroupsAPI.readInstances(instanceGroup.id);
let instanceDetails;
let healthCheckDetails;
const isAssociated = results.some(
({ id: instId }) => instId === parseInt(instanceId, 10)
);
@@ -92,7 +93,7 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
]);
instanceDetails = details;
healthCheckDetails = healthCheckData;
setHealthCheck(healthCheckData);
} else {
throw new Error(
`This instance is not associated with this instance group`
@@ -100,7 +101,6 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
}
setBreadcrumb(instanceGroup, instanceDetails);
setHealthCheck(healthCheckDetails);
setForks(
computeForks(
instanceDetails.mem_capacity,
@@ -121,8 +121,12 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
request: fetchHealthCheck,
} = useRequest(
useCallback(async () => {
const { data } = await InstancesAPI.healthCheck(instanceId);
const { status } = await InstancesAPI.healthCheck(instanceId);
const { data } = await InstancesAPI.readHealthCheckDetail(instanceId);
setHealthCheck(data);
if (status === 200) {
setShowHealthCheckAlert(true);
}
}, [instanceId])
);
@@ -188,6 +192,9 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
return (
<>
<RoutedTabs tabsArray={tabsArray} />
{showHealthCheckAlert ? (
<HealthCheckAlert onSetHealthCheckAlert={setShowHealthCheckAlert} />
) : null}
<CardBody>
<DetailList gutter="sm">
<Detail

View File

@@ -298,58 +298,6 @@ describe('<InstanceDetails/>', () => {
expect(InstancesAPI.readDetail).not.toBeCalled();
});
test('Should make request for Health Check', async () => {
InstancesAPI.healthCheck.mockResolvedValue({
data: {
uuid: '00000000-0000-0000-0000-000000000000',
hostname: 'awx_1',
version: '19.1.0',
last_health_check: '2021-09-15T18:02:07.270664Z',
errors: '',
cpu: 8,
memory: 6232231936,
cpu_capacity: 32,
mem_capacity: 38,
capacity: 38,
},
});
InstanceGroupsAPI.readInstances.mockResolvedValue({
data: {
results: [
{
id: 1,
},
{
id: 2,
},
],
},
});
jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({
me: { is_superuser: true },
}));
await act(async () => {
wrapper = mountWithContexts(
<InstanceDetails
instanceGroup={instanceGroup}
setBreadcrumb={() => {}}
/>
);
});
await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0);
expect(
wrapper.find("Button[ouiaId='health-check-button']").prop('isDisabled')
).toBe(false);
await act(async () => {
wrapper.find("Button[ouiaId='health-check-button']").prop('onClick')();
});
expect(InstancesAPI.healthCheck).toBeCalledWith(1);
wrapper.update();
expect(
wrapper.find("Detail[label='Last Health Check']").prop('value')
).toBe('9/15/2021, 6:02:07 PM');
});
test('Should handle api error for health check', async () => {
InstancesAPI.healthCheck.mockRejectedValue(
new Error({

View File

@@ -23,6 +23,7 @@ import useSelected from 'hooks/useSelected';
import { InstanceGroupsAPI, InstancesAPI } from 'api';
import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
import HealthCheckButton from 'components/HealthCheckButton/HealthCheckButton';
import HealthCheckAlert from 'components/HealthCheckAlert';
import InstanceListItem from './InstanceListItem';
const QS_CONFIG = getQSConfig('instance', {
@@ -33,6 +34,7 @@ const QS_CONFIG = getQSConfig('instance', {
function InstanceList({ instanceGroup }) {
const [isModalOpen, setIsModalOpen] = useState(false);
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
const location = useLocation();
const { id: instanceGroupId } = useParams();
@@ -86,9 +88,15 @@ function InstanceList({ instanceGroup }) {
isLoading: isHealthCheckLoading,
} = useRequest(
useCallback(async () => {
await Promise.all(selected.map(({ id }) => InstancesAPI.healthCheck(id)));
fetchInstances();
}, [selected, fetchInstances])
const [...response] = await Promise.all(
selected
.filter(({ node_type }) => node_type !== 'hop')
.map(({ id }) => InstancesAPI.healthCheck(id))
);
if (response) {
setShowHealthCheckAlert(true);
}
}, [selected])
);
const handleHealthCheck = async () => {
@@ -171,6 +179,9 @@ function InstanceList({ instanceGroup }) {
return (
<>
{showHealthCheckAlert ? (
<HealthCheckAlert onSetHealthCheckAlert={setShowHealthCheckAlert} />
) : null}
<PaginatedTable
contentError={contentError}
hasContentLoading={

View File

@@ -32,6 +32,7 @@ import useRequest, {
useDeleteItems,
useDismissableError,
} from 'hooks/useRequest';
import HealthCheckAlert from 'components/HealthCheckAlert';
import RemoveInstanceButton from '../Shared/RemoveInstanceButton';
const Unavailable = styled.span`
@@ -66,6 +67,7 @@ function InstanceDetail({ setBreadcrumb, isK8s }) {
const [forks, setForks] = useState();
const history = useHistory();
const [healthCheck, setHealthCheck] = useState({});
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
const {
isLoading,
@@ -119,8 +121,12 @@ function InstanceDetail({ setBreadcrumb, isK8s }) {
request: fetchHealthCheck,
} = useRequest(
useCallback(async () => {
const { data } = await InstancesAPI.healthCheck(id);
const { status } = await InstancesAPI.healthCheck(id);
const { data } = await InstancesAPI.readHealthCheckDetail(id);
setHealthCheck(data);
if (status === 200) {
setShowHealthCheckAlert(true);
}
}, [id])
);
@@ -175,6 +181,10 @@ function InstanceDetail({ setBreadcrumb, isK8s }) {
const isHopNode = instance.node_type === 'hop';
return (
<>
{showHealthCheckAlert ? (
<HealthCheckAlert onSetHealthCheckAlert={setShowHealthCheckAlert} />
) : null}
<CardBody>
<DetailList gutter="sm">
<Detail
@@ -361,6 +371,7 @@ function InstanceDetail({ setBreadcrumb, isK8s }) {
</AlertModal>
)}
</CardBody>
</>
);
}

View File

@@ -165,41 +165,6 @@ describe('<InstanceDetail/>', () => {
expect(wrapper.find('InstanceToggle').length).toBe(1);
});
test('Should make request for Health Check', async () => {
InstancesAPI.healthCheck.mockResolvedValue({
data: {
uuid: '00000000-0000-0000-0000-000000000000',
hostname: 'awx_1',
version: '19.1.0',
last_health_check: '2021-09-15T18:02:07.270664Z',
errors: '',
cpu: 8,
memory: 6232231936,
cpu_capacity: 32,
mem_capacity: 38,
capacity: 38,
},
});
jest.spyOn(ConfigContext, 'useConfig').mockImplementation(() => ({
me: { is_superuser: true },
}));
await act(async () => {
wrapper = mountWithContexts(<InstanceDetail setBreadcrumb={() => {}} />);
});
await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0);
expect(
wrapper.find("Button[ouiaId='health-check-button']").prop('isDisabled')
).toBe(false);
await act(async () => {
wrapper.find("Button[ouiaId='health-check-button']").prop('onClick')();
});
expect(InstancesAPI.healthCheck).toBeCalledWith(1);
wrapper.update();
expect(
wrapper.find("Detail[label='Last Health Check']").prop('value')
).toBe('9/15/2021, 6:02:07 PM');
});
test('Should handle api error for health check', async () => {
InstancesAPI.healthCheck.mockRejectedValue(
new Error({

View File

@@ -93,10 +93,9 @@ function InstanceList() {
if (response) {
setShowHealthCheckAlert(true);
}
return response;
}, [selected])
);
const handleHealthCheck = async () => {
await fetchHealthCheck();
clearSelected();