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:
Alex Corey 2021-10-20 16:42:37 -04:00
parent 0ae67edaba
commit f92a49fda9
5 changed files with 87 additions and 26 deletions

View File

@ -36,10 +36,17 @@ class SlackBackend(AWXBaseEmailBackend, CustomNotificationBase):
for r in m.recipients():
if r.startswith('#'):
r = r[1:]
thread = None
channel = r
thread = None
if ',' in r:
channel, thread = r.split(',')
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:
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)
if response['ok']:
sent_messages += 1

View File

@ -24,7 +24,7 @@ def test_send_messages():
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
@ -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

View File

@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useState, useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom';
import {
Button,
@ -20,13 +20,20 @@ import {
import CodeDetail from 'components/DetailList/CodeDetail';
import DeleteButton from 'components/DeleteButton';
import ErrorDetail from 'components/ErrorDetail';
import { NotificationTemplatesAPI } from 'api';
import { NotificationTemplatesAPI, NotificationsAPI } from 'api';
import useRequest, { useDismissableError } from 'hooks/useRequest';
import StatusLabel from 'components/StatusLabel';
import hasCustomMessages from '../shared/hasCustomMessages';
import { NOTIFICATION_TYPES } from '../constants';
const NUM_RETRIES = 25;
const RETRY_TIMEOUT = 5000;
function NotificationTemplateDetail({ template, defaultMessages }) {
const history = useHistory();
const [testStatus, setTestStatus] = useState(
template.summary_fields?.recent_notifications[0]?.status ?? undefined
);
const {
created,
@ -64,9 +71,35 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
}, [template.id, history])
);
const { error, dismissError } = useDismissableError(deleteError);
const typeMessageDefaults = defaultMessages[template.notification_type];
const { request: sendTestNotification, error: testError } = useRequest(
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 (
<CardBody>
<DetailList gutter="sm">
@ -76,6 +109,12 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
value={template.description}
dataCy="nt-detail-description"
/>
{summary_fields.recent_notifications.length && (
<Detail
label={t`Status`}
value={<StatusLabel status={testStatus} />}
/>
)}
{summary_fields.organization ? (
<Detail
label={t`Organization`}
@ -354,8 +393,8 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
)}
</DetailList>
<CardActionsRow>
{summary_fields.user_capabilities &&
summary_fields.user_capabilities.edit && (
{summary_fields.user_capabilities?.edit && (
<>
<Button
ouiaId="notification-template-detail-edit-button"
component={Link}
@ -364,18 +403,23 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
>
{t`Edit`}
</Button>
)}
{summary_fields.user_capabilities &&
summary_fields.user_capabilities.delete && (
<DeleteButton
name={template.name}
modalTitle={t`Delete Notification`}
onConfirm={deleteTemplate}
isDisabled={isLoading}
>
{t`Delete`}
</DeleteButton>
)}
<Button
onClick={sendTestNotification}
variant="secondary"
isDisabled={testStatus === ('running' || 'pending')}
>{t`Test`}</Button>
</>
)}
{summary_fields.user_capabilities?.delete && (
<DeleteButton
name={template.name}
modalTitle={t`Delete Notification`}
onConfirm={deleteTemplate}
isDisabled={isLoading}
>
{t`Delete`}
</DeleteButton>
)}
</CardActionsRow>
{error && (
<AlertModal
@ -384,7 +428,9 @@ function NotificationTemplateDetail({ template, defaultMessages }) {
title={t`Error!`}
onClose={dismissError}
>
{t`Failed to delete notification.`}
{deleteError
? t`Failed to delete notification.`
: t`Notification test failed.`}
<ErrorDetail error={error} />
</AlertModal>
)}

View File

@ -45,7 +45,7 @@ const mockTemplate = {
delete: true,
copy: true,
},
recent_notifications: [],
recent_notifications: [{ status: 'success' }],
},
created: '2021-06-16T18:52:23.811374Z',
modified: '2021-06-16T18:53:37.631371Z',

View File

@ -362,8 +362,14 @@ function SlackFields() {
type="textarea"
validate={required(null)}
isRequired
tooltip={t`Enter one Slack channel per line. The pound symbol (#)
is required for channels.`}
tooltip={
<>
{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
id="slack-token"