mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 21:45:56 +08:00
(feature): UI for Test alert channels (#994)
* (feature): Implemented test channel function for webhook and slack
This commit is contained in:
parent
508c6ced80
commit
2b5b79e34a
30
frontend/public/locales/en-GB/channels.json
Normal file
30
frontend/public/locales/en-GB/channels.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"page_title_create": "New Notification Channels",
|
||||||
|
"page_title_edit": "Edit Notification Channels",
|
||||||
|
"button_save_channel": "Save",
|
||||||
|
"button_test_channel": "Test",
|
||||||
|
"button_return": "Back",
|
||||||
|
"field_channel_name": "Name",
|
||||||
|
"field_channel_type": "Type",
|
||||||
|
"field_webhook_url": "Webhook URL",
|
||||||
|
"field_slack_recipient": "Recipient",
|
||||||
|
"field_slack_title": "Title",
|
||||||
|
"field_slack_description": "Description",
|
||||||
|
"field_webhook_username": "User Name (optional)",
|
||||||
|
"field_webhook_password": "Password (optional)",
|
||||||
|
"placeholder_slack_description": "Description",
|
||||||
|
"help_webhook_username": "Leave empty for bearer auth or when authentication is not necessary.",
|
||||||
|
"help_webhook_password": "Specify a password or bearer token",
|
||||||
|
"channel_creation_done": "Successfully created the channel",
|
||||||
|
"channel_creation_failed": "An unexpected error occurred while creating this channel",
|
||||||
|
"channel_edit_done": "Channels Edited Successfully",
|
||||||
|
"channel_edit_failed": "An unexpected error occurred while updating this channel",
|
||||||
|
"selected_channel_invalid": "Channel type selected is invalid",
|
||||||
|
"username_no_password": "A Password must be provided with user name",
|
||||||
|
"test_unsupported": "Sorry, this channel type does not support test yet",
|
||||||
|
"channel_test_done": "An alert has been sent to this channel",
|
||||||
|
"channel_test_failed": "Failed to send a test message to this channel, please confirm that the parameters are set correctly",
|
||||||
|
"channel_test_unexpected": "An unexpected error occurred while sending a message to this channel, please try again",
|
||||||
|
"webhook_url_required": "Webhook URL is mandatory",
|
||||||
|
"slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)"
|
||||||
|
}
|
30
frontend/public/locales/en/channels.json
Normal file
30
frontend/public/locales/en/channels.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"page_title_create": "New Notification Channels",
|
||||||
|
"page_title_edit": "Edit Notification Channels",
|
||||||
|
"button_save_channel": "Save",
|
||||||
|
"button_test_channel": "Test",
|
||||||
|
"button_return": "Back",
|
||||||
|
"field_channel_name": "Name",
|
||||||
|
"field_channel_type": "Type",
|
||||||
|
"field_webhook_url": "Webhook URL",
|
||||||
|
"field_slack_recipient": "Recipient",
|
||||||
|
"field_slack_title": "Title",
|
||||||
|
"field_slack_description": "Description",
|
||||||
|
"field_webhook_username": "User Name (optional)",
|
||||||
|
"field_webhook_password": "Password (optional)",
|
||||||
|
"placeholder_slack_description": "Description",
|
||||||
|
"help_webhook_username": "Leave empty for bearer auth or when authentication is not necessary.",
|
||||||
|
"help_webhook_password": "Specify a password or bearer token",
|
||||||
|
"channel_creation_done": "Successfully created the channel",
|
||||||
|
"channel_creation_failed": "An unexpected error occurred while creating this channel",
|
||||||
|
"channel_edit_done": "Channels Edited Successfully",
|
||||||
|
"channel_edit_failed": "An unexpected error occurred while updating this channel",
|
||||||
|
"selected_channel_invalid": "Channel type selected is invalid",
|
||||||
|
"username_no_password": "A Password must be provided with user name",
|
||||||
|
"test_unsupported": "Sorry, this channel type does not support test yet",
|
||||||
|
"channel_test_done": "An alert has been sent to this channel",
|
||||||
|
"channel_test_failed": "Failed to send a test message to this channel, please confirm that the parameters are set correctly",
|
||||||
|
"channel_test_unexpected": "An unexpected error occurred while sending a message to this channel, please try again",
|
||||||
|
"webhook_url_required": "Webhook URL is mandatory",
|
||||||
|
"slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)"
|
||||||
|
}
|
35
frontend/src/api/channels/testSlack.ts
Normal file
35
frontend/src/api/channels/testSlack.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps, Props } from 'types/api/channels/createSlack';
|
||||||
|
|
||||||
|
const testSlack = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/testChannel', {
|
||||||
|
name: props.name,
|
||||||
|
slack_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
api_url: props.api_url,
|
||||||
|
channel: props.channel,
|
||||||
|
title: props.title,
|
||||||
|
text: props.text,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default testSlack;
|
51
frontend/src/api/channels/testWebhook.ts
Normal file
51
frontend/src/api/channels/testWebhook.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import axios from 'api';
|
||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps, Props } from 'types/api/channels/createWebhook';
|
||||||
|
|
||||||
|
const testWebhook = async (
|
||||||
|
props: Props,
|
||||||
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
|
try {
|
||||||
|
let httpConfig = {};
|
||||||
|
|
||||||
|
if (props.username !== '' && props.password !== '') {
|
||||||
|
httpConfig = {
|
||||||
|
basic_auth: {
|
||||||
|
username: props.username,
|
||||||
|
password: props.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (props.username === '' && props.password !== '') {
|
||||||
|
httpConfig = {
|
||||||
|
authorization: {
|
||||||
|
type: 'bearer',
|
||||||
|
credentials: props.password,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.post('/testChannel', {
|
||||||
|
name: props.name,
|
||||||
|
webhook_configs: [
|
||||||
|
{
|
||||||
|
send_resolved: true,
|
||||||
|
url: props.api_url,
|
||||||
|
http_config: httpConfig,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: 'Success',
|
||||||
|
payload: response.data.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default testWebhook;
|
@ -1,10 +1,13 @@
|
|||||||
import { Form, notification } from 'antd';
|
import { Form, notification } from 'antd';
|
||||||
import createSlackApi from 'api/channels/createSlack';
|
import createSlackApi from 'api/channels/createSlack';
|
||||||
import createWebhookApi from 'api/channels/createWebhook';
|
import createWebhookApi from 'api/channels/createWebhook';
|
||||||
|
import testSlackApi from 'api/channels/testSlack';
|
||||||
|
import testWebhookApi from 'api/channels/testWebhook';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import FormAlertChannels from 'container/FormAlertChannels';
|
import FormAlertChannels from 'container/FormAlertChannels';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
@ -17,6 +20,9 @@ import {
|
|||||||
function CreateAlertChannels({
|
function CreateAlertChannels({
|
||||||
preType = 'slack',
|
preType = 'slack',
|
||||||
}: CreateAlertChannelsProps): JSX.Element {
|
}: CreateAlertChannelsProps): JSX.Element {
|
||||||
|
// init namespace for translations
|
||||||
|
const { t } = useTranslation('channels');
|
||||||
|
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
|
|
||||||
const [selectedConfig, setSelectedConfig] = useState<
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
@ -45,6 +51,7 @@ function CreateAlertChannels({
|
|||||||
{{- end }}`,
|
{{- end }}`,
|
||||||
});
|
});
|
||||||
const [savingState, setSavingState] = useState<boolean>(false);
|
const [savingState, setSavingState] = useState<boolean>(false);
|
||||||
|
const [testingState, setTestingState] = useState<boolean>(false);
|
||||||
const [notifications, NotificationElement] = notification.useNotification();
|
const [notifications, NotificationElement] = notification.useNotification();
|
||||||
|
|
||||||
const [type, setType] = useState<ChannelType>(preType);
|
const [type, setType] = useState<ChannelType>(preType);
|
||||||
@ -52,26 +59,26 @@ function CreateAlertChannels({
|
|||||||
setType(value as ChannelType);
|
setType(value as ChannelType);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onTestHandler = useCallback(() => {
|
const prepareSlackRequest = useCallback(() => {
|
||||||
console.log('test');
|
return {
|
||||||
}, []);
|
api_url: selectedConfig?.api_url || '',
|
||||||
|
channel: selectedConfig?.channel || '',
|
||||||
|
name: selectedConfig?.name || '',
|
||||||
|
send_resolved: true,
|
||||||
|
text: selectedConfig?.text || '',
|
||||||
|
title: selectedConfig?.title || '',
|
||||||
|
};
|
||||||
|
}, [selectedConfig]);
|
||||||
|
|
||||||
const onSlackHandler = useCallback(async () => {
|
const onSlackHandler = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setSavingState(true);
|
setSavingState(true);
|
||||||
const response = await createSlackApi({
|
const response = await createSlackApi(prepareSlackRequest());
|
||||||
api_url: selectedConfig?.api_url || '',
|
|
||||||
channel: selectedConfig?.channel || '',
|
|
||||||
name: selectedConfig?.name || '',
|
|
||||||
send_resolved: true,
|
|
||||||
text: selectedConfig?.text || '',
|
|
||||||
title: selectedConfig?.title || '',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: 'Successfully created the channel',
|
description: t('channel_creation_done'),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
history.replace(ROUTES.SETTINGS);
|
history.replace(ROUTES.SETTINGS);
|
||||||
@ -79,21 +86,20 @@ function CreateAlertChannels({
|
|||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description: response.error || 'Error while creating the channel',
|
description: response.error || t('channel_creation_failed'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description:
|
description: t('channel_creation_failed'),
|
||||||
'An unexpected error occurred while creating this channel, please try again',
|
|
||||||
});
|
});
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}
|
}
|
||||||
}, [notifications, selectedConfig]);
|
}, [prepareSlackRequest, t, notifications]);
|
||||||
|
|
||||||
const onWebhookHandler = useCallback(async () => {
|
const prepareWebhookRequest = useCallback(() => {
|
||||||
// initial api request without auth params
|
// initial api request without auth params
|
||||||
let request: WebhookChannel = {
|
let request: WebhookChannel = {
|
||||||
api_url: selectedConfig?.api_url || '',
|
api_url: selectedConfig?.api_url || '',
|
||||||
@ -101,39 +107,42 @@ function CreateAlertChannels({
|
|||||||
send_resolved: true,
|
send_resolved: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
setSavingState(true);
|
if (selectedConfig?.username !== '' || selectedConfig?.password !== '') {
|
||||||
|
if (selectedConfig?.username !== '') {
|
||||||
try {
|
// if username is not null then password must be passed
|
||||||
if (selectedConfig?.username !== '' || selectedConfig?.password !== '') {
|
if (selectedConfig?.password !== '') {
|
||||||
if (selectedConfig?.username !== '') {
|
|
||||||
// if username is not null then password must be passed
|
|
||||||
if (selectedConfig?.password !== '') {
|
|
||||||
request = {
|
|
||||||
...request,
|
|
||||||
username: selectedConfig.username,
|
|
||||||
password: selectedConfig.password,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
notifications.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: 'A Password must be provided with user name',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (selectedConfig?.password !== '') {
|
|
||||||
// only password entered, set bearer token
|
|
||||||
request = {
|
request = {
|
||||||
...request,
|
...request,
|
||||||
username: '',
|
username: selectedConfig.username,
|
||||||
password: selectedConfig.password,
|
password: selectedConfig.password,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('username_no_password'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else if (selectedConfig?.password !== '') {
|
||||||
|
// only password entered, set bearer token
|
||||||
|
request = {
|
||||||
|
...request,
|
||||||
|
username: '',
|
||||||
|
password: selectedConfig.password,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}, [notifications, t, selectedConfig]);
|
||||||
|
|
||||||
|
const onWebhookHandler = useCallback(async () => {
|
||||||
|
setSavingState(true);
|
||||||
|
try {
|
||||||
|
const request = prepareWebhookRequest();
|
||||||
const response = await createWebhookApi(request);
|
const response = await createWebhookApi(request);
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: 'Successfully created the channel',
|
description: t('channel_creation_done'),
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
history.replace(ROUTES.SETTINGS);
|
history.replace(ROUTES.SETTINGS);
|
||||||
@ -141,19 +150,17 @@ function CreateAlertChannels({
|
|||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description: response.error || 'Error while creating the channel',
|
description: response.error || t('channel_creation_failed'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description:
|
description: t('channel_creation_failed'),
|
||||||
'An unexpected error occurred while creating this channel, please try again',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [notifications, selectedConfig]);
|
}, [prepareWebhookRequest, t, notifications]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
async (value: ChannelType) => {
|
async (value: ChannelType) => {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
@ -166,11 +173,64 @@ function CreateAlertChannels({
|
|||||||
default:
|
default:
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description: 'channel type selected is invalid',
|
description: t('selected_channel_invalid'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSlackHandler, onWebhookHandler, notifications],
|
[onSlackHandler, t, onWebhookHandler, notifications],
|
||||||
|
);
|
||||||
|
|
||||||
|
const performChannelTest = useCallback(
|
||||||
|
async (channelType: ChannelType) => {
|
||||||
|
setTestingState(true);
|
||||||
|
try {
|
||||||
|
let request;
|
||||||
|
let response;
|
||||||
|
switch (channelType) {
|
||||||
|
case WebhookType:
|
||||||
|
request = prepareWebhookRequest();
|
||||||
|
response = await testWebhookApi(request);
|
||||||
|
break;
|
||||||
|
case SlackType:
|
||||||
|
request = prepareSlackRequest();
|
||||||
|
response = await testSlackApi(request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('test_unsupported'),
|
||||||
|
});
|
||||||
|
setTestingState(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success',
|
||||||
|
description: t('channel_test_done'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('channel_test_failed'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('channel_test_unexpected'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTestingState(false);
|
||||||
|
},
|
||||||
|
[prepareWebhookRequest, t, prepareSlackRequest, notifications],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onTestHandler = useCallback(
|
||||||
|
async (value: ChannelType) => {
|
||||||
|
performChannelTest(value);
|
||||||
|
},
|
||||||
|
[performChannelTest],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -183,8 +243,9 @@ function CreateAlertChannels({
|
|||||||
onTestHandler,
|
onTestHandler,
|
||||||
onSaveHandler,
|
onSaveHandler,
|
||||||
savingState,
|
savingState,
|
||||||
|
testingState,
|
||||||
NotificationElement,
|
NotificationElement,
|
||||||
title: 'New Notification Channels',
|
title: t('page_title_create'),
|
||||||
initialValue: {
|
initialValue: {
|
||||||
type,
|
type,
|
||||||
...selectedConfig,
|
...selectedConfig,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { Form, notification } from 'antd';
|
import { Form, notification } from 'antd';
|
||||||
import editSlackApi from 'api/channels/editSlack';
|
import editSlackApi from 'api/channels/editSlack';
|
||||||
import editWebhookApi from 'api/channels/editWebhook';
|
import editWebhookApi from 'api/channels/editWebhook';
|
||||||
|
import testSlackApi from 'api/channels/testSlack';
|
||||||
|
import testWebhookApi from 'api/channels/testWebhook';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
@ -12,11 +14,15 @@ import {
|
|||||||
import FormAlertChannels from 'container/FormAlertChannels';
|
import FormAlertChannels from 'container/FormAlertChannels';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
function EditAlertChannels({
|
function EditAlertChannels({
|
||||||
initialValue,
|
initialValue,
|
||||||
}: EditAlertChannelsProps): JSX.Element {
|
}: EditAlertChannelsProps): JSX.Element {
|
||||||
|
// init namespace for translations
|
||||||
|
const { t } = useTranslation('channels');
|
||||||
|
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<
|
const [selectedConfig, setSelectedConfig] = useState<
|
||||||
Partial<SlackChannel & WebhookChannel>
|
Partial<SlackChannel & WebhookChannel>
|
||||||
@ -24,6 +30,7 @@ function EditAlertChannels({
|
|||||||
...initialValue,
|
...initialValue,
|
||||||
});
|
});
|
||||||
const [savingState, setSavingState] = useState<boolean>(false);
|
const [savingState, setSavingState] = useState<boolean>(false);
|
||||||
|
const [testingState, setTestingState] = useState<boolean>(false);
|
||||||
const [notifications, NotificationElement] = notification.useNotification();
|
const [notifications, NotificationElement] = notification.useNotification();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
@ -35,9 +42,8 @@ function EditAlertChannels({
|
|||||||
setType(value as ChannelType);
|
setType(value as ChannelType);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSlackEditHandler = useCallback(async () => {
|
const prepareSlackRequest = useCallback(() => {
|
||||||
setSavingState(true);
|
return {
|
||||||
const response = await editSlackApi({
|
|
||||||
api_url: selectedConfig?.api_url || '',
|
api_url: selectedConfig?.api_url || '',
|
||||||
channel: selectedConfig?.channel || '',
|
channel: selectedConfig?.channel || '',
|
||||||
name: selectedConfig?.name || '',
|
name: selectedConfig?.name || '',
|
||||||
@ -45,12 +51,27 @@ function EditAlertChannels({
|
|||||||
text: selectedConfig?.text || '',
|
text: selectedConfig?.text || '',
|
||||||
title: selectedConfig?.title || '',
|
title: selectedConfig?.title || '',
|
||||||
id,
|
id,
|
||||||
});
|
};
|
||||||
|
}, [id, selectedConfig]);
|
||||||
|
|
||||||
|
const onSlackEditHandler = useCallback(async () => {
|
||||||
|
setSavingState(true);
|
||||||
|
|
||||||
|
if (selectedConfig?.api_url === '') {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('webhook_url_required'),
|
||||||
|
});
|
||||||
|
setSavingState(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await editSlackApi(prepareSlackRequest());
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: 'Channels Edited Successfully',
|
description: t('channel_edit_done'),
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -59,15 +80,27 @@ function EditAlertChannels({
|
|||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Error',
|
message: 'Error',
|
||||||
description: response.error || 'error while updating the Channels',
|
description: response.error || t('channel_edit_failed'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [selectedConfig, notifications, id]);
|
}, [prepareSlackRequest, t, notifications, selectedConfig]);
|
||||||
|
|
||||||
|
const prepareWebhookRequest = useCallback(() => {
|
||||||
|
const { name, username, password } = selectedConfig;
|
||||||
|
return {
|
||||||
|
api_url: selectedConfig?.api_url || '',
|
||||||
|
name: name || '',
|
||||||
|
send_resolved: true,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
}, [id, selectedConfig]);
|
||||||
|
|
||||||
const onWebhookEditHandler = useCallback(async () => {
|
const onWebhookEditHandler = useCallback(async () => {
|
||||||
setSavingState(true);
|
setSavingState(true);
|
||||||
const { name, username, password } = selectedConfig;
|
const { username, password } = selectedConfig;
|
||||||
|
|
||||||
const showError = (msg: string): void => {
|
const showError = (msg: string): void => {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
@ -77,40 +110,33 @@ function EditAlertChannels({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (selectedConfig?.api_url === '') {
|
if (selectedConfig?.api_url === '') {
|
||||||
showError('Webhook URL is mandatory');
|
showError(t('webhook_url_required'));
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username && (!password || password === '')) {
|
if (username && (!password || password === '')) {
|
||||||
showError('Please enter a password');
|
showError(t('username_no_password'));
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await editWebhookApi({
|
const response = await editWebhookApi(prepareWebhookRequest());
|
||||||
api_url: selectedConfig?.api_url || '',
|
|
||||||
name: name || '',
|
|
||||||
send_resolved: true,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: 'Channels Edited Successfully',
|
description: t('channel_edit_done'),
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
history.replace(ROUTES.SETTINGS);
|
history.replace(ROUTES.SETTINGS);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
} else {
|
} else {
|
||||||
showError(response.error || 'error while updating the Channels');
|
showError(response.error || t('channel_edit_failed'));
|
||||||
}
|
}
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [selectedConfig, notifications, id]);
|
}, [prepareWebhookRequest, t, notifications, selectedConfig]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
(value: ChannelType) => {
|
(value: ChannelType) => {
|
||||||
@ -123,9 +149,58 @@ function EditAlertChannels({
|
|||||||
[onSlackEditHandler, onWebhookEditHandler],
|
[onSlackEditHandler, onWebhookEditHandler],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onTestHandler = useCallback(() => {
|
const performChannelTest = useCallback(
|
||||||
console.log('test');
|
async (channelType: ChannelType) => {
|
||||||
}, []);
|
setTestingState(true);
|
||||||
|
try {
|
||||||
|
let request;
|
||||||
|
let response;
|
||||||
|
switch (channelType) {
|
||||||
|
case WebhookType:
|
||||||
|
request = prepareWebhookRequest();
|
||||||
|
response = await testWebhookApi(request);
|
||||||
|
break;
|
||||||
|
case SlackType:
|
||||||
|
request = prepareSlackRequest();
|
||||||
|
response = await testSlackApi(request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('test_unsupported'),
|
||||||
|
});
|
||||||
|
setTestingState(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
notifications.success({
|
||||||
|
message: 'Success',
|
||||||
|
description: t('channel_test_done'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('channel_test_failed'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('channel_test_failed'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTestingState(false);
|
||||||
|
},
|
||||||
|
[prepareWebhookRequest, t, prepareSlackRequest, notifications],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onTestHandler = useCallback(
|
||||||
|
async (value: ChannelType) => {
|
||||||
|
performChannelTest(value);
|
||||||
|
},
|
||||||
|
[performChannelTest],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormAlertChannels
|
<FormAlertChannels
|
||||||
@ -136,9 +211,10 @@ function EditAlertChannels({
|
|||||||
type,
|
type,
|
||||||
onTestHandler,
|
onTestHandler,
|
||||||
onSaveHandler,
|
onSaveHandler,
|
||||||
|
testingState,
|
||||||
savingState,
|
savingState,
|
||||||
NotificationElement,
|
NotificationElement,
|
||||||
title: 'Edit Notification Channels',
|
title: t('page_title_edit'),
|
||||||
initialValue,
|
initialValue,
|
||||||
nameDisable: true,
|
nameDisable: true,
|
||||||
}}
|
}}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import FormItem from 'antd/lib/form/FormItem';
|
import FormItem from 'antd/lib/form/FormItem';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { SlackChannel } from '../../CreateAlertChannels/config';
|
import { SlackChannel } from '../../CreateAlertChannels/config';
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
||||||
|
const { t } = useTranslation('channels');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormItem name="api_url" label="Webhook URL">
|
<FormItem name="api_url" label={t('field_webhook_url')}>
|
||||||
<Input
|
<Input
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
setSelectedConfig((value) => ({
|
setSelectedConfig((value) => ({
|
||||||
@ -22,8 +25,8 @@ function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
|||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
name="channel"
|
name="channel"
|
||||||
help="Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace),"
|
help={t('slack_channel_help')}
|
||||||
label="Recipient"
|
label={t('field_slack_recipient')}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
onChange={(event): void =>
|
onChange={(event): void =>
|
||||||
@ -35,7 +38,7 @@ function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem name="title" label="Title">
|
<FormItem name="title" label={t('field_slack_title')}>
|
||||||
<TextArea
|
<TextArea
|
||||||
rows={4}
|
rows={4}
|
||||||
// value={`[{{ .Status | toUpper }}{{ if eq .Status \"firing\" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\n{{- if gt (len .CommonLabels) (len .GroupLabels) -}}\n{{\" \"}}(\n{{- with .CommonLabels.Remove .GroupLabels.Names }}\n {{- range $index, $label := .SortedPairs -}}\n {{ if $index }}, {{ end }}\n {{- $label.Name }}=\"{{ $label.Value -}}\"\n {{- end }}\n{{- end -}}\n)\n{{- end }}`}
|
// value={`[{{ .Status | toUpper }}{{ if eq .Status \"firing\" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}\n{{- if gt (len .CommonLabels) (len .GroupLabels) -}}\n{{\" \"}}(\n{{- with .CommonLabels.Remove .GroupLabels.Names }}\n {{- range $index, $label := .SortedPairs -}}\n {{ if $index }}, {{ end }}\n {{- $label.Name }}=\"{{ $label.Value -}}\"\n {{- end }}\n{{- end -}}\n)\n{{- end }}`}
|
||||||
@ -48,7 +51,7 @@ function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem name="text" label="Description">
|
<FormItem name="text" label={t('field_slack_description')}>
|
||||||
<TextArea
|
<TextArea
|
||||||
onChange={(event): void =>
|
onChange={(event): void =>
|
||||||
setSelectedConfig((value) => ({
|
setSelectedConfig((value) => ({
|
||||||
@ -56,7 +59,7 @@ function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
|||||||
text: event.target.value,
|
text: event.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
placeholder="description"
|
placeholder={t('placeholder_slack_description')}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</>
|
</>
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import FormItem from 'antd/lib/form/FormItem';
|
import FormItem from 'antd/lib/form/FormItem';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { WebhookChannel } from '../../CreateAlertChannels/config';
|
import { WebhookChannel } from '../../CreateAlertChannels/config';
|
||||||
|
|
||||||
function WebhookSettings({ setSelectedConfig }: WebhookProps): JSX.Element {
|
function WebhookSettings({ setSelectedConfig }: WebhookProps): JSX.Element {
|
||||||
|
const { t } = useTranslation('channels');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormItem name="api_url" label="Webhook URL">
|
<FormItem name="api_url" label={t('field_webhook_url')}>
|
||||||
<Input
|
<Input
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
setSelectedConfig((value) => ({
|
setSelectedConfig((value) => ({
|
||||||
@ -19,8 +22,8 @@ function WebhookSettings({ setSelectedConfig }: WebhookProps): JSX.Element {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem
|
<FormItem
|
||||||
name="username"
|
name="username"
|
||||||
label="User Name (optional)"
|
label={t('field_webhook_username')}
|
||||||
help="Leave empty for bearer auth or when authentication is not necessary."
|
help={t('help_webhook_username')}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
@ -34,7 +37,7 @@ function WebhookSettings({ setSelectedConfig }: WebhookProps): JSX.Element {
|
|||||||
<FormItem
|
<FormItem
|
||||||
name="password"
|
name="password"
|
||||||
label="Password (optional)"
|
label="Password (optional)"
|
||||||
help="Specify a password or bearer token"
|
help={t('help_webhook_password')}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import SlackSettings from './Settings/Slack';
|
import SlackSettings from './Settings/Slack';
|
||||||
import WebhookSettings from './Settings/Webhook';
|
import WebhookSettings from './Settings/Webhook';
|
||||||
@ -23,14 +24,17 @@ function FormAlertChannels({
|
|||||||
type,
|
type,
|
||||||
setSelectedConfig,
|
setSelectedConfig,
|
||||||
onTypeChangeHandler,
|
onTypeChangeHandler,
|
||||||
// onTestHandler,
|
onTestHandler,
|
||||||
onSaveHandler,
|
onSaveHandler,
|
||||||
savingState,
|
savingState,
|
||||||
|
testingState,
|
||||||
NotificationElement,
|
NotificationElement,
|
||||||
title,
|
title,
|
||||||
initialValue,
|
initialValue,
|
||||||
nameDisable = false,
|
nameDisable = false,
|
||||||
}: FormAlertChannelsProps): JSX.Element {
|
}: FormAlertChannelsProps): JSX.Element {
|
||||||
|
const { t } = useTranslation('channels');
|
||||||
|
|
||||||
const renderSettings = (): React.ReactElement | null => {
|
const renderSettings = (): React.ReactElement | null => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SlackType:
|
case SlackType:
|
||||||
@ -48,7 +52,7 @@ function FormAlertChannels({
|
|||||||
<Title level={3}>{title}</Title>
|
<Title level={3}>{title}</Title>
|
||||||
|
|
||||||
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
|
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
|
||||||
<FormItem label="Name" labelAlign="left" name="name">
|
<FormItem label={t('field_channel_name')} labelAlign="left" name="name">
|
||||||
<Input
|
<Input
|
||||||
disabled={nameDisable}
|
disabled={nameDisable}
|
||||||
onChange={(event): void => {
|
onChange={(event): void => {
|
||||||
@ -60,7 +64,7 @@ function FormAlertChannels({
|
|||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem label="Type" labelAlign="left" name="type">
|
<FormItem label={t('field_channel_type')} labelAlign="left" name="type">
|
||||||
<Select onChange={onTypeChangeHandler} value={type}>
|
<Select onChange={onTypeChangeHandler} value={type}>
|
||||||
<Option value="slack" key="slack">
|
<Option value="slack" key="slack">
|
||||||
Slack
|
Slack
|
||||||
@ -80,15 +84,21 @@ function FormAlertChannels({
|
|||||||
type="primary"
|
type="primary"
|
||||||
onClick={(): void => onSaveHandler(type)}
|
onClick={(): void => onSaveHandler(type)}
|
||||||
>
|
>
|
||||||
Save
|
{t('button_save_channel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={testingState}
|
||||||
|
loading={testingState}
|
||||||
|
onClick={(): void => onTestHandler(type)}
|
||||||
|
>
|
||||||
|
{t('button_test_channel')}
|
||||||
</Button>
|
</Button>
|
||||||
{/* <Button onClick={onTestHandler}>Test</Button> */}
|
|
||||||
<Button
|
<Button
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
history.replace(ROUTES.SETTINGS);
|
history.replace(ROUTES.SETTINGS);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Back
|
{t('button_return')}
|
||||||
</Button>
|
</Button>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
@ -102,6 +112,8 @@ interface FormAlertChannelsProps {
|
|||||||
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
||||||
onTypeChangeHandler: (value: ChannelType) => void;
|
onTypeChangeHandler: (value: ChannelType) => void;
|
||||||
onSaveHandler: (props: ChannelType) => void;
|
onSaveHandler: (props: ChannelType) => void;
|
||||||
|
onTestHandler: (props: ChannelType) => void;
|
||||||
|
testingState: boolean;
|
||||||
savingState: boolean;
|
savingState: boolean;
|
||||||
NotificationElement: React.ReactElement<
|
NotificationElement: React.ReactElement<
|
||||||
unknown,
|
unknown,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user