mirror of
https://github.com/ansible/awx.git
synced 2026-05-25 01:27:45 -02:30
Add health check toast notification for Instance list and detail views.
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -93,10 +93,9 @@ function InstanceList() {
|
||||
if (response) {
|
||||
setShowHealthCheckAlert(true);
|
||||
}
|
||||
|
||||
return response;
|
||||
}, [selected])
|
||||
);
|
||||
|
||||
const handleHealthCheck = async () => {
|
||||
await fetchHealthCheck();
|
||||
clearSelected();
|
||||
|
||||
Reference in New Issue
Block a user