mirror of
https://github.com/ansible/awx.git
synced 2026-03-22 03:17:39 -02:30
Adds ability to send slack notification to a thread, updates tooltip in ui, and adds test button to notification details view
This commit is contained in:
@@ -36,10 +36,17 @@ class SlackBackend(AWXBaseEmailBackend, CustomNotificationBase):
|
|||||||
for r in m.recipients():
|
for r in m.recipients():
|
||||||
if r.startswith('#'):
|
if r.startswith('#'):
|
||||||
r = r[1:]
|
r = r[1:]
|
||||||
|
thread = None
|
||||||
|
channel = r
|
||||||
|
thread = None
|
||||||
|
if ',' in r:
|
||||||
|
channel, thread = r.split(',')
|
||||||
if self.color:
|
if self.color:
|
||||||
response = client.chat_postMessage(channel=r, as_user=True, attachments=[{"color": self.color, "text": m.subject}])
|
response = client.chat_postMessage(
|
||||||
|
channel=channel, thread_ts=thread, as_user=True, attachments=[{"color": self.color, "text": m.subject}]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
response = client.chat_postMessage(channel=r, as_user=True, text=m.subject)
|
response = client.chat_postMessage(channel=channel, thread_ts=thread, as_user=True, text=m.subject)
|
||||||
logger.debug(response)
|
logger.debug(response)
|
||||||
if response['ok']:
|
if response['ok']:
|
||||||
sent_messages += 1
|
sent_messages += 1
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def test_send_messages():
|
|||||||
message,
|
message,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
WebClient_mock.chat_postMessage.assert_called_once_with(channel='random', as_user=True, text='test subject')
|
WebClient_mock.chat_postMessage.assert_called_once_with(channel='random', thread_ts=None, as_user=True, text='test subject')
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +47,9 @@ def test_send_messages_with_color():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
WebClient_mock.chat_postMessage.assert_called_once_with(channel='random', as_user=True, attachments=[{'color': '#006699', 'text': 'test subject'}])
|
WebClient_mock.chat_postMessage.assert_called_once_with(
|
||||||
|
channel='random', as_user=True, thread_ts=None, attachments=[{'color': '#006699', 'text': 'test subject'}]
|
||||||
|
)
|
||||||
assert sent_messages == 1
|
assert sent_messages == 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -20,13 +20,20 @@ import {
|
|||||||
import CodeDetail from 'components/DetailList/CodeDetail';
|
import CodeDetail from 'components/DetailList/CodeDetail';
|
||||||
import DeleteButton from 'components/DeleteButton';
|
import DeleteButton from 'components/DeleteButton';
|
||||||
import ErrorDetail from 'components/ErrorDetail';
|
import ErrorDetail from 'components/ErrorDetail';
|
||||||
import { NotificationTemplatesAPI } from 'api';
|
import { NotificationTemplatesAPI, NotificationsAPI } from 'api';
|
||||||
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
||||||
|
import StatusLabel from 'components/StatusLabel';
|
||||||
import hasCustomMessages from '../shared/hasCustomMessages';
|
import hasCustomMessages from '../shared/hasCustomMessages';
|
||||||
import { NOTIFICATION_TYPES } from '../constants';
|
import { NOTIFICATION_TYPES } from '../constants';
|
||||||
|
|
||||||
|
const NUM_RETRIES = 25;
|
||||||
|
const RETRY_TIMEOUT = 5000;
|
||||||
|
|
||||||
function NotificationTemplateDetail({ template, defaultMessages }) {
|
function NotificationTemplateDetail({ template, defaultMessages }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const [testStatus, setTestStatus] = useState(
|
||||||
|
template.summary_fields?.recent_notifications[0]?.status ?? undefined
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
created,
|
created,
|
||||||
@@ -64,9 +71,35 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
|
|||||||
}, [template.id, history])
|
}, [template.id, history])
|
||||||
);
|
);
|
||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { request: sendTestNotification, error: testError } = useRequest(
|
||||||
const typeMessageDefaults = defaultMessages[template.notification_type];
|
useCallback(async () => {
|
||||||
|
setTestStatus('running');
|
||||||
|
|
||||||
|
let retries = NUM_RETRIES;
|
||||||
|
const {
|
||||||
|
data: { notification: notificationId },
|
||||||
|
} = await NotificationTemplatesAPI.test(template.id);
|
||||||
|
|
||||||
|
async function pollForStatusChange() {
|
||||||
|
const { data: notification } = await NotificationsAPI.readDetail(
|
||||||
|
notificationId
|
||||||
|
);
|
||||||
|
if (notification.status !== 'pending') {
|
||||||
|
setTestStatus(notification.status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
retries--;
|
||||||
|
if (retries > 0) {
|
||||||
|
setTimeout(pollForStatusChange, RETRY_TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(pollForStatusChange, RETRY_TIMEOUT);
|
||||||
|
}, [template.id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const { error, dismissError } = useDismissableError(deleteError || testError);
|
||||||
|
const typeMessageDefaults = defaultMessages[template.notification_type];
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<DetailList gutter="sm">
|
<DetailList gutter="sm">
|
||||||
@@ -76,6 +109,12 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
|
|||||||
value={template.description}
|
value={template.description}
|
||||||
dataCy="nt-detail-description"
|
dataCy="nt-detail-description"
|
||||||
/>
|
/>
|
||||||
|
{summary_fields.recent_notifications.length && (
|
||||||
|
<Detail
|
||||||
|
label={t`Status`}
|
||||||
|
value={<StatusLabel status={testStatus} />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{summary_fields.organization ? (
|
{summary_fields.organization ? (
|
||||||
<Detail
|
<Detail
|
||||||
label={t`Organization`}
|
label={t`Organization`}
|
||||||
@@ -354,8 +393,8 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
|
|||||||
)}
|
)}
|
||||||
</DetailList>
|
</DetailList>
|
||||||
<CardActionsRow>
|
<CardActionsRow>
|
||||||
{summary_fields.user_capabilities &&
|
{summary_fields.user_capabilities?.edit && (
|
||||||
summary_fields.user_capabilities.edit && (
|
<>
|
||||||
<Button
|
<Button
|
||||||
ouiaId="notification-template-detail-edit-button"
|
ouiaId="notification-template-detail-edit-button"
|
||||||
component={Link}
|
component={Link}
|
||||||
@@ -364,18 +403,23 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
|
|||||||
>
|
>
|
||||||
{t`Edit`}
|
{t`Edit`}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
<Button
|
||||||
{summary_fields.user_capabilities &&
|
onClick={sendTestNotification}
|
||||||
summary_fields.user_capabilities.delete && (
|
variant="secondary"
|
||||||
<DeleteButton
|
isDisabled={testStatus === ('running' || 'pending')}
|
||||||
name={template.name}
|
>{t`Test`}</Button>
|
||||||
modalTitle={t`Delete Notification`}
|
</>
|
||||||
onConfirm={deleteTemplate}
|
)}
|
||||||
isDisabled={isLoading}
|
{summary_fields.user_capabilities?.delete && (
|
||||||
>
|
<DeleteButton
|
||||||
{t`Delete`}
|
name={template.name}
|
||||||
</DeleteButton>
|
modalTitle={t`Delete Notification`}
|
||||||
)}
|
onConfirm={deleteTemplate}
|
||||||
|
isDisabled={isLoading}
|
||||||
|
>
|
||||||
|
{t`Delete`}
|
||||||
|
</DeleteButton>
|
||||||
|
)}
|
||||||
</CardActionsRow>
|
</CardActionsRow>
|
||||||
{error && (
|
{error && (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
@@ -384,7 +428,9 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
|
|||||||
title={t`Error!`}
|
title={t`Error!`}
|
||||||
onClose={dismissError}
|
onClose={dismissError}
|
||||||
>
|
>
|
||||||
{t`Failed to delete notification.`}
|
{deleteError
|
||||||
|
? t`Failed to delete notification.`
|
||||||
|
: t`Notification test failed.`}
|
||||||
<ErrorDetail error={error} />
|
<ErrorDetail error={error} />
|
||||||
</AlertModal>
|
</AlertModal>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const mockTemplate = {
|
|||||||
delete: true,
|
delete: true,
|
||||||
copy: true,
|
copy: true,
|
||||||
},
|
},
|
||||||
recent_notifications: [],
|
recent_notifications: [{ status: 'success' }],
|
||||||
},
|
},
|
||||||
created: '2021-06-16T18:52:23.811374Z',
|
created: '2021-06-16T18:52:23.811374Z',
|
||||||
modified: '2021-06-16T18:53:37.631371Z',
|
modified: '2021-06-16T18:53:37.631371Z',
|
||||||
|
|||||||
@@ -362,8 +362,14 @@ function SlackFields() {
|
|||||||
type="textarea"
|
type="textarea"
|
||||||
validate={required(null)}
|
validate={required(null)}
|
||||||
isRequired
|
isRequired
|
||||||
tooltip={t`Enter one Slack channel per line. The pound symbol (#)
|
tooltip={
|
||||||
is required for channels.`}
|
<>
|
||||||
|
{t`Enter one Slack channel per line. The pound symbol (#)
|
||||||
|
is required for channels. To respond to or start a thread to a specific message add the parent message Id to the channel where the parent message Id is 16 digits. A dot (.) must be manually inserted after the 10th digit. ie:#destination-channel, 1231257890.006423. See Slack`}{' '}
|
||||||
|
<a href="https://api.slack.com/messaging/retrieving#individual_messages">{t`documentation`}</a>{' '}
|
||||||
|
<span>{t`for more information.`}</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<PasswordField
|
<PasswordField
|
||||||
id="slack-token"
|
id="slack-token"
|
||||||
|
|||||||
Reference in New Issue
Block a user