diff --git a/awx/ui_next/src/api/index.js b/awx/ui_next/src/api/index.js
index c7dab6f76b..3c0d5e3237 100644
--- a/awx/ui_next/src/api/index.js
+++ b/awx/ui_next/src/api/index.js
@@ -17,6 +17,7 @@ import Jobs from './models/Jobs';
import Labels from './models/Labels';
import Me from './models/Me';
import NotificationTemplates from './models/NotificationTemplates';
+import Notifications from './models/Notifications';
import Organizations from './models/Organizations';
import ProjectUpdates from './models/ProjectUpdates';
import Projects from './models/Projects';
@@ -53,6 +54,7 @@ const JobsAPI = new Jobs();
const LabelsAPI = new Labels();
const MeAPI = new Me();
const NotificationTemplatesAPI = new NotificationTemplates();
+const NotificationsAPI = new Notifications();
const OrganizationsAPI = new Organizations();
const ProjectUpdatesAPI = new ProjectUpdates();
const ProjectsAPI = new Projects();
@@ -90,6 +92,7 @@ export {
LabelsAPI,
MeAPI,
NotificationTemplatesAPI,
+ NotificationsAPI,
OrganizationsAPI,
ProjectUpdatesAPI,
ProjectsAPI,
diff --git a/awx/ui_next/src/api/models/Notifications.js b/awx/ui_next/src/api/models/Notifications.js
new file mode 100644
index 0000000000..68405c0986
--- /dev/null
+++ b/awx/ui_next/src/api/models/Notifications.js
@@ -0,0 +1,10 @@
+import Base from '../Base';
+
+class Notifications extends Base {
+ constructor(http) {
+ super(http);
+ this.baseUrl = '/api/v2/notifications/';
+ }
+}
+
+export default Notifications;
diff --git a/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx b/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx
index 0f2be56fdc..31917b9597 100644
--- a/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx
+++ b/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx
@@ -26,6 +26,7 @@ const RunningIcon = styled(SyncAltIcon)`
const colors = {
success: 'green',
+ successful: 'green',
failed: 'red',
error: 'red',
running: 'blue',
@@ -35,6 +36,7 @@ const colors = {
};
const icons = {
success: CheckCircleIcon,
+ successful: CheckCircleIcon,
failed: ExclamationCircleIcon,
error: ExclamationCircleIcon,
running: RunningIcon,
@@ -58,6 +60,7 @@ export default function StatusLabel({ status }) {
StatusLabel.propTypes = {
status: oneOf([
'success',
+ 'successful',
'failed',
'error',
'running',
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx
index 0087e7f9a8..59d9fb748d 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx
@@ -14,7 +14,7 @@ import {
Tooltip,
} from '@patternfly/react-core';
import { PencilAltIcon, BellIcon } from '@patternfly/react-icons';
-import { NotificationTemplatesAPI } from '../../../api';
+import { NotificationTemplatesAPI, NotificationsAPI } from '../../../api';
import DataListCell from '../../../components/DataListCell';
import StatusLabel from '../../../components/StatusLabel';
import useRequest from '../../../util/useRequest';
@@ -27,6 +27,9 @@ const DataListAction = styled(_DataListAction)`
grid-template-columns: 40px 40px;
`;
+const NUM_RETRIES = 25;
+const RETRY_TIMEOUT = 5000;
+
function NotificationTemplateListItem({
template,
detailUrl,
@@ -45,9 +48,29 @@ function NotificationTemplateListItem({
}, [latestStatus]);
const { request: sendTestNotification, isLoading, error } = useRequest(
- useCallback(() => {
- NotificationTemplatesAPI.test(template.id);
+ useCallback(async () => {
+ const request = NotificationTemplatesAPI.test(template.id);
setStatus('running');
+ let retries = NUM_RETRIES;
+ const {
+ data: { notification: notificationId },
+ } = await request;
+
+ async function pollForStatusChange() {
+ const { data: notification } = await NotificationsAPI.readDetail(
+ notificationId
+ );
+ if (notification.status !== 'pending') {
+ setStatus(notification.status);
+ return;
+ }
+ retries--;
+ if (retries > 0) {
+ setTimeout(pollForStatusChange, RETRY_TIMEOUT);
+ }
+ }
+
+ setTimeout(pollForStatusChange, RETRY_TIMEOUT);
}, [template.id])
);
diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.jsx
index 5a4566779e..58878d3b96 100644
--- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.jsx
+++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.jsx
@@ -39,7 +39,9 @@ describe('', () => {
});
test('should send test notification', async () => {
- NotificationTemplatesAPI.test.mockResolvedValue({});
+ NotificationTemplatesAPI.test.mockResolvedValue({
+ data: { notification: 1 },
+ });
const wrapper = mountWithContexts(