mirror of
https://github.com/ansible/awx.git
synced 2026-01-10 15:32:07 -03:30
Adds toast to notification template list whenever test notification finishes
This commit is contained in:
parent
c9ec0d31f1
commit
83a9c3470e
@ -1,8 +1,14 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useLocation, useRouteMatch } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Card, PageSection } from '@patternfly/react-core';
|
||||
import {
|
||||
Alert,
|
||||
AlertActionCloseButton,
|
||||
AlertGroup,
|
||||
Card,
|
||||
PageSection,
|
||||
} from '@patternfly/react-core';
|
||||
import { NotificationTemplatesAPI } from '../../../api';
|
||||
import PaginatedTable, {
|
||||
HeaderRow,
|
||||
@ -29,6 +35,7 @@ const QS_CONFIG = getQSConfig('notification-templates', {
|
||||
function NotificationTemplatesList({ i18n }) {
|
||||
const location = useLocation();
|
||||
const match = useRouteMatch();
|
||||
const [testToasts, setTestToasts] = useState([]);
|
||||
|
||||
const addUrl = `${match.url}/add`;
|
||||
|
||||
@ -102,6 +109,14 @@ function NotificationTemplatesList({ i18n }) {
|
||||
setSelected([]);
|
||||
};
|
||||
|
||||
const addTestToast = useCallback(notification => {
|
||||
setTestToasts(oldToasts => [...oldToasts, notification]);
|
||||
}, []);
|
||||
|
||||
const removeTestToast = notificationId => {
|
||||
setTestToasts(testToasts.filter(toast => toast.id !== notificationId));
|
||||
};
|
||||
|
||||
const canAdd = actions && actions.POST;
|
||||
|
||||
return (
|
||||
@ -185,6 +200,7 @@ function NotificationTemplatesList({ i18n }) {
|
||||
}
|
||||
renderRow={(template, index) => (
|
||||
<NotificationTemplateListItem
|
||||
onAddToast={addTestToast}
|
||||
key={template.id}
|
||||
fetchTemplates={fetchTemplates}
|
||||
template={template}
|
||||
@ -209,6 +225,39 @@ function NotificationTemplatesList({ i18n }) {
|
||||
{i18n._(t`Failed to delete one or more notification template.`)}
|
||||
<ErrorDetail error={deletionError} />
|
||||
</AlertModal>
|
||||
<AlertGroup isToast>
|
||||
{testToasts
|
||||
.filter(notification => notification.status !== 'pending')
|
||||
.map(notification => (
|
||||
<Alert
|
||||
actionClose={
|
||||
<AlertActionCloseButton
|
||||
onClose={() => removeTestToast(notification.id)}
|
||||
/>
|
||||
}
|
||||
onTimeout={() => removeTestToast(notification.id)}
|
||||
timeout={notification.status !== 'failed'}
|
||||
title={notification.summary_fields.notification_template.name}
|
||||
variant={notification.status === 'failed' ? 'danger' : 'success'}
|
||||
key={`notification-template-alert-${notification.id}`}
|
||||
ouiaId={`notification-template-alert-${notification.id}`}
|
||||
>
|
||||
<>
|
||||
{notification.status === 'successful' && (
|
||||
<p>{i18n._(t`Notification sent successfully`)}</p>
|
||||
)}
|
||||
{notification.status === 'failed' &&
|
||||
notification?.error === 'timed out' && (
|
||||
<p>{i18n._(t`Notification timed out`)}</p>
|
||||
)}
|
||||
{notification.status === 'failed' &&
|
||||
notification?.error !== 'timed out' && (
|
||||
<p>{notification.error}</p>
|
||||
)}
|
||||
</>
|
||||
</Alert>
|
||||
))}
|
||||
</AlertGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { OrganizationsAPI } from '../../../api';
|
||||
import {
|
||||
NotificationsAPI,
|
||||
NotificationTemplatesAPI,
|
||||
OrganizationsAPI,
|
||||
} from '../../../api';
|
||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||
import NotificationTemplateList from './NotificationTemplateList';
|
||||
|
||||
jest.mock('../../../api');
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const mockTemplates = {
|
||||
data: {
|
||||
count: 3,
|
||||
@ -197,6 +203,43 @@ describe('<NotificationTemplateList />', () => {
|
||||
expect(wrapper.find('ToolbarAddButton').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should show toast after test resolves', async () => {
|
||||
NotificationTemplatesAPI.test.mockResolvedValueOnce({
|
||||
data: {
|
||||
notification: 9182,
|
||||
},
|
||||
});
|
||||
NotificationsAPI.readDetail.mockResolvedValueOnce({
|
||||
data: {
|
||||
id: 9182,
|
||||
status: 'failed',
|
||||
error: 'There was an error with the notification',
|
||||
summary_fields: {
|
||||
notification_template: {
|
||||
name: 'foobar',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<NotificationTemplateList />);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('button[aria-label="Test Notification"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
});
|
||||
wrapper.update();
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Alert').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should hide add button (rbac)', async () => {
|
||||
OrganizationsAPI.readOptions.mockResolvedValue({
|
||||
data: {
|
||||
|
||||
@ -11,13 +11,16 @@ import { timeOfDay } from '../../../util/dates';
|
||||
import { NotificationTemplatesAPI, NotificationsAPI } from '../../../api';
|
||||
import StatusLabel from '../../../components/StatusLabel';
|
||||
import CopyButton from '../../../components/CopyButton';
|
||||
import useRequest from '../../../util/useRequest';
|
||||
import AlertModal from '../../../components/AlertModal';
|
||||
import ErrorDetail from '../../../components/ErrorDetail';
|
||||
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
||||
import { NOTIFICATION_TYPES } from '../constants';
|
||||
|
||||
const NUM_RETRIES = 25;
|
||||
const RETRY_TIMEOUT = 5000;
|
||||
|
||||
function NotificationTemplateListItem({
|
||||
onAddToast,
|
||||
template,
|
||||
detailUrl,
|
||||
fetchTemplates,
|
||||
@ -66,6 +69,7 @@ function NotificationTemplateListItem({
|
||||
notificationId
|
||||
);
|
||||
if (notification.status !== 'pending') {
|
||||
onAddToast(notification);
|
||||
setStatus(notification.status);
|
||||
return;
|
||||
}
|
||||
@ -76,9 +80,11 @@ function NotificationTemplateListItem({
|
||||
}
|
||||
|
||||
setTimeout(pollForStatusChange, RETRY_TIMEOUT);
|
||||
}, [template.id])
|
||||
}, [template.id, onAddToast])
|
||||
);
|
||||
|
||||
const { error: sendTestError, dismissError } = useDismissableError(error);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
setStatus('error');
|
||||
@ -88,65 +94,78 @@ function NotificationTemplateListItem({
|
||||
const labelId = `template-name-${template.id}`;
|
||||
|
||||
return (
|
||||
<Tr id={`notification-template-row-${template.id}`}>
|
||||
<Td
|
||||
select={{
|
||||
rowIndex,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}}
|
||||
dataLabel={i18n._(t`Selected`)}
|
||||
/>
|
||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{template.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Status`)}>
|
||||
{status && <StatusLabel status={status} />}
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Type`)}>
|
||||
{NOTIFICATION_TYPES[template.notification_type] ||
|
||||
template.notification_type}
|
||||
</Td>
|
||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||
<ActionItem visible tooltip={i18n._(t`Test notification`)}>
|
||||
<Button
|
||||
aria-label={i18n._(t`Test Notification`)}
|
||||
variant="plain"
|
||||
onClick={sendTestNotification}
|
||||
isDisabled={isLoading || status === 'running'}
|
||||
<>
|
||||
<Tr id={`notification-template-row-${template.id}`}>
|
||||
<Td
|
||||
select={{
|
||||
rowIndex,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}}
|
||||
dataLabel={i18n._(t`Selected`)}
|
||||
/>
|
||||
<Td id={labelId} dataLabel={i18n._(t`Name`)}>
|
||||
<Link to={`${detailUrl}`}>
|
||||
<b>{template.name}</b>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Status`)}>
|
||||
{status && <StatusLabel status={status} />}
|
||||
</Td>
|
||||
<Td dataLabel={i18n._(t`Type`)}>
|
||||
{NOTIFICATION_TYPES[template.notification_type] ||
|
||||
template.notification_type}
|
||||
</Td>
|
||||
<ActionsTd dataLabel={i18n._(t`Actions`)}>
|
||||
<ActionItem visible tooltip={i18n._(t`Test notification`)}>
|
||||
<Button
|
||||
aria-label={i18n._(t`Test Notification`)}
|
||||
variant="plain"
|
||||
onClick={sendTestNotification}
|
||||
isDisabled={isLoading || status === 'running'}
|
||||
>
|
||||
<BellIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
visible={template.summary_fields.user_capabilities.edit}
|
||||
tooltip={i18n._(t`Edit`)}
|
||||
>
|
||||
<BellIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
visible={template.summary_fields.user_capabilities.edit}
|
||||
tooltip={i18n._(t`Edit`)}
|
||||
>
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit Notification Template`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/notification_templates/${template.id}/edit`}
|
||||
<Button
|
||||
aria-label={i18n._(t`Edit Notification Template`)}
|
||||
variant="plain"
|
||||
component={Link}
|
||||
to={`/notification_templates/${template.id}/edit`}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
visible={template.summary_fields.user_capabilities.copy}
|
||||
tooltip={i18n._(t`Copy Notification Template`)}
|
||||
>
|
||||
<PencilAltIcon />
|
||||
</Button>
|
||||
</ActionItem>
|
||||
<ActionItem
|
||||
visible={template.summary_fields.user_capabilities.copy}
|
||||
tooltip={i18n._(t`Copy Notification Template`)}
|
||||
<CopyButton
|
||||
copyItem={copyTemplate}
|
||||
isCopyDisabled={isCopyDisabled}
|
||||
onCopyStart={handleCopyStart}
|
||||
onCopyFinish={handleCopyFinish}
|
||||
errorMessage={i18n._(t`Failed to copy template.`)}
|
||||
/>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
{sendTestError && (
|
||||
<AlertModal
|
||||
isOpen
|
||||
variant="error"
|
||||
title={i18n._(t`Error!`)}
|
||||
onClose={dismissError}
|
||||
>
|
||||
<CopyButton
|
||||
copyItem={copyTemplate}
|
||||
isCopyDisabled={isCopyDisabled}
|
||||
onCopyStart={handleCopyStart}
|
||||
onCopyFinish={handleCopyFinish}
|
||||
errorMessage={i18n._(t`Failed to copy template.`)}
|
||||
/>
|
||||
</ActionItem>
|
||||
</ActionsTd>
|
||||
</Tr>
|
||||
{i18n._(t`Failed to send test notification.`)}
|
||||
<ErrorDetail error={sendTestError} />
|
||||
</AlertModal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user