mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-25 05:24:26 +08:00
Pagerduty - Create, Edit and Test Features (#1016)
* enabled sending alerts to pagerduty
This commit is contained in:
parent
642ece288e
commit
3ef9d96678
@ -12,9 +12,27 @@
|
||||
"field_slack_description": "Description",
|
||||
"field_webhook_username": "User Name (optional)",
|
||||
"field_webhook_password": "Password (optional)",
|
||||
"field_pager_routing_key": "Routing Key",
|
||||
"field_pager_description": "Description",
|
||||
"field_pager_severity": "Severity",
|
||||
"field_pager_details": "Additional Information",
|
||||
"field_pager_component": "Component",
|
||||
"field_pager_group": "Group",
|
||||
"field_pager_class": "Class",
|
||||
"field_pager_client": "Client",
|
||||
"field_pager_client_url": "Client URL",
|
||||
"placeholder_slack_description": "Description",
|
||||
"placeholder_pager_description": "Description",
|
||||
"help_pager_client": "Shows up as event source in Pagerduty",
|
||||
"help_pager_client_url": "Shows up as event source link in Pagerduty",
|
||||
"help_pager_class": "The class/type of the event",
|
||||
"help_pager_details": "Specify a key-value format (must be a valid json)",
|
||||
"help_pager_group": "A cluster or grouping of sources",
|
||||
"help_pager_component": "The part or component of the affected system that is broke",
|
||||
"help_pager_severity": "Severity of the incident, must be one of: must be one of the following: 'critical', 'warning', 'error' or 'info'",
|
||||
"help_webhook_username": "Leave empty for bearer auth or when authentication is not necessary.",
|
||||
"help_webhook_password": "Specify a password or bearer token",
|
||||
"help_pager_description": "Shows up as description in pagerduty",
|
||||
"channel_creation_done": "Successfully created the channel",
|
||||
"channel_creation_failed": "An unexpected error occurred while creating this channel",
|
||||
"channel_edit_done": "Channels Edited Successfully",
|
||||
|
@ -12,9 +12,27 @@
|
||||
"field_slack_description": "Description",
|
||||
"field_webhook_username": "User Name (optional)",
|
||||
"field_webhook_password": "Password (optional)",
|
||||
"field_pager_routing_key": "Routing Key",
|
||||
"field_pager_description": "Description",
|
||||
"field_pager_severity": "Severity",
|
||||
"field_pager_details": "Additional Information",
|
||||
"field_pager_component": "Component",
|
||||
"field_pager_group": "Group",
|
||||
"field_pager_class": "Class",
|
||||
"field_pager_client": "Client",
|
||||
"field_pager_client_url": "Client URL",
|
||||
"placeholder_slack_description": "Description",
|
||||
"placeholder_pager_description": "Description",
|
||||
"help_pager_client": "Shows up as event source in Pagerduty",
|
||||
"help_pager_client_url": "Shows up as event source link in Pagerduty",
|
||||
"help_pager_class": "The class/type of the event",
|
||||
"help_pager_details": "Specify a key-value format (must be a valid json)",
|
||||
"help_pager_group": "A cluster or grouping of sources",
|
||||
"help_pager_component": "The part or component of the affected system that is broke",
|
||||
"help_pager_severity": "Severity of the incident, must be one of: must be one of the following: 'critical', 'warning', 'error' or 'info'",
|
||||
"help_webhook_username": "Leave empty for bearer auth or when authentication is not necessary.",
|
||||
"help_webhook_password": "Specify a password or bearer token",
|
||||
"help_pager_description": "Shows up as description in pagerduty",
|
||||
"channel_creation_done": "Successfully created the channel",
|
||||
"channel_creation_failed": "An unexpected error occurred while creating this channel",
|
||||
"channel_edit_done": "Channels Edited Successfully",
|
||||
|
42
frontend/src/api/channels/createPager.ts
Normal file
42
frontend/src/api/channels/createPager.ts
Normal file
@ -0,0 +1,42 @@
|
||||
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/createPager';
|
||||
|
||||
const create = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post('/channels', {
|
||||
name: props.name,
|
||||
pagerduty_configs: [
|
||||
{
|
||||
send_resolved: true,
|
||||
routing_key: props.routing_key,
|
||||
client: props.client,
|
||||
client_url: props.client_url,
|
||||
description: props.description,
|
||||
severity: props.severity,
|
||||
class: props.class,
|
||||
component: props.component,
|
||||
group: props.group,
|
||||
details: {
|
||||
...props.detailsArray,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default create;
|
42
frontend/src/api/channels/editPager.ts
Normal file
42
frontend/src/api/channels/editPager.ts
Normal file
@ -0,0 +1,42 @@
|
||||
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/editPager';
|
||||
|
||||
const editPager = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.put(`/channels/${props.id}`, {
|
||||
name: props.name,
|
||||
pagerduty_configs: [
|
||||
{
|
||||
send_resolved: true,
|
||||
routing_key: props.routing_key,
|
||||
client: props.client,
|
||||
client_url: props.client_url,
|
||||
description: props.description,
|
||||
severity: props.severity,
|
||||
class: props.class,
|
||||
component: props.component,
|
||||
group: props.group,
|
||||
details: {
|
||||
...props.detailsArray,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default editPager;
|
42
frontend/src/api/channels/testPager.ts
Normal file
42
frontend/src/api/channels/testPager.ts
Normal file
@ -0,0 +1,42 @@
|
||||
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/createPager';
|
||||
|
||||
const testPager = async (
|
||||
props: Props,
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post('/testChannel', {
|
||||
name: props.name,
|
||||
pagerduty_configs: [
|
||||
{
|
||||
send_resolved: true,
|
||||
routing_key: props.routing_key,
|
||||
client: props.client,
|
||||
client_url: props.client_url,
|
||||
description: props.description,
|
||||
severity: props.severity,
|
||||
class: props.class,
|
||||
component: props.component,
|
||||
group: props.group,
|
||||
details: {
|
||||
...props.detailsArray,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default testPager;
|
@ -1,6 +1,7 @@
|
||||
export interface Channel {
|
||||
send_resolved?: boolean;
|
||||
name: string;
|
||||
filter?: Partial<Array<LabelFilterStatement>>;
|
||||
}
|
||||
|
||||
export interface SlackChannel extends Channel {
|
||||
@ -17,6 +18,66 @@ export interface WebhookChannel extends Channel {
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export type ChannelType = 'slack' | 'email' | 'webhook';
|
||||
// PagerChannel configures alert manager to send
|
||||
// events to pagerduty
|
||||
export interface PagerChannel extends Channel {
|
||||
// ref: https://prometheus.io/docs/alerting/latest/configuration/#pagerduty_config
|
||||
routing_key?: string;
|
||||
// displays source of the event in pager duty
|
||||
client?: string;
|
||||
client_url?: string;
|
||||
// A description of the incident
|
||||
description?: string;
|
||||
// Severity of the incident
|
||||
severity?: string;
|
||||
// The part or component of the affected system that is broken
|
||||
component?: string;
|
||||
// A cluster or grouping of sources
|
||||
group?: string;
|
||||
// The class/type of the event.
|
||||
class?: string;
|
||||
|
||||
details?: string;
|
||||
detailsArray?: Record<string, string>;
|
||||
}
|
||||
export const ValidatePagerChannel = (p: PagerChannel): string => {
|
||||
if (!p) {
|
||||
return 'Received unexpected input for this channel, please contact your administrator ';
|
||||
}
|
||||
|
||||
if (!p.name || p.name === '') {
|
||||
return 'Name is mandatory for creating a channel';
|
||||
}
|
||||
|
||||
if (!p.routing_key || p.routing_key === '') {
|
||||
return 'Routing Key is mandatory for creating pagerduty channel';
|
||||
}
|
||||
|
||||
// validate details json
|
||||
try {
|
||||
JSON.parse(p.details || '{}');
|
||||
} catch (e) {
|
||||
return 'failed to parse additional information, please enter a valid json';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
export type ChannelType = 'slack' | 'email' | 'webhook' | 'pagerduty';
|
||||
export const SlackType: ChannelType = 'slack';
|
||||
export const WebhookType: ChannelType = 'webhook';
|
||||
export const PagerType: ChannelType = 'pagerduty';
|
||||
|
||||
// LabelFilterStatement will be used for preparing filter conditions / matchers
|
||||
export interface LabelFilterStatement {
|
||||
// ref: https://prometheus.io/docs/alerting/latest/configuration/#matcher
|
||||
|
||||
// label name
|
||||
name: string;
|
||||
|
||||
// comparators supported by promql are =, !=, =~, or !~. =
|
||||
comparator: string;
|
||||
|
||||
// filter value
|
||||
value: string;
|
||||
}
|
||||
|
22
frontend/src/container/CreateAlertChannels/defaults.ts
Normal file
22
frontend/src/container/CreateAlertChannels/defaults.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { PagerChannel } from './config';
|
||||
|
||||
export const PagerInitialConfig: Partial<PagerChannel> = {
|
||||
description: `{{ range .Alerts -}}
|
||||
*Alert:* {{ if .Annotations.title }} {{ .Annotations.title }} {{ else }} {{ .Annotations.summary }} {{end}} {{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
||||
|
||||
*Description:* {{ .Annotations.description }}
|
||||
|
||||
*Details:*
|
||||
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}
|
||||
{{ end }}
|
||||
{{ end }}`,
|
||||
severity: '{{ (index .Alerts 0).Labels.severity }}',
|
||||
client: 'SigNoz Alert Manager',
|
||||
client_url: 'https://enter-signoz-host-n-port-here/alerts',
|
||||
details: JSON.stringify({
|
||||
firing: `{{ template "pagerduty.default.instances" .Alerts.Firing }}`,
|
||||
resolved: `{{ template "pagerduty.default.instances" .Alerts.Resolved }}`,
|
||||
num_firing: '{{ .Alerts.Firing | len }}',
|
||||
num_resolved: '{{ .Alerts.Resolved | len }}',
|
||||
}),
|
||||
};
|
@ -1,6 +1,8 @@
|
||||
import { Form, notification } from 'antd';
|
||||
import createPagerApi from 'api/channels/createPager';
|
||||
import createSlackApi from 'api/channels/createSlack';
|
||||
import createWebhookApi from 'api/channels/createWebhook';
|
||||
import testPagerApi from 'api/channels/testPager';
|
||||
import testSlackApi from 'api/channels/testSlack';
|
||||
import testWebhookApi from 'api/channels/testWebhook';
|
||||
import ROUTES from 'constants/routes';
|
||||
@ -11,11 +13,15 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
ChannelType,
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
SlackType,
|
||||
ValidatePagerChannel,
|
||||
WebhookChannel,
|
||||
WebhookType,
|
||||
} from './config';
|
||||
import { PagerInitialConfig } from './defaults';
|
||||
|
||||
function CreateAlertChannels({
|
||||
preType = 'slack',
|
||||
@ -26,7 +32,7 @@ function CreateAlertChannels({
|
||||
const [formInstance] = Form.useForm();
|
||||
|
||||
const [selectedConfig, setSelectedConfig] = useState<
|
||||
Partial<SlackChannel & WebhookChannel>
|
||||
Partial<SlackChannel & WebhookChannel & PagerChannel>
|
||||
>({
|
||||
text: `{{ range .Alerts -}}
|
||||
*Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }}
|
||||
@ -55,9 +61,22 @@ function CreateAlertChannels({
|
||||
const [notifications, NotificationElement] = notification.useNotification();
|
||||
|
||||
const [type, setType] = useState<ChannelType>(preType);
|
||||
const onTypeChangeHandler = useCallback((value: string) => {
|
||||
setType(value as ChannelType);
|
||||
}, []);
|
||||
const onTypeChangeHandler = useCallback(
|
||||
(value: string) => {
|
||||
const currentType = type;
|
||||
setType(value as ChannelType);
|
||||
|
||||
if (value === PagerType && currentType !== value) {
|
||||
// reset config to pager defaults
|
||||
setSelectedConfig({
|
||||
name: selectedConfig?.name,
|
||||
send_resolved: selectedConfig.send_resolved,
|
||||
...PagerInitialConfig,
|
||||
});
|
||||
}
|
||||
},
|
||||
[type, selectedConfig],
|
||||
);
|
||||
|
||||
const prepareSlackRequest = useCallback(() => {
|
||||
return {
|
||||
@ -71,8 +90,9 @@ function CreateAlertChannels({
|
||||
}, [selectedConfig]);
|
||||
|
||||
const onSlackHandler = useCallback(async () => {
|
||||
setSavingState(true);
|
||||
|
||||
try {
|
||||
setSavingState(true);
|
||||
const response = await createSlackApi(prepareSlackRequest());
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
@ -89,14 +109,13 @@ function CreateAlertChannels({
|
||||
description: response.error || t('channel_creation_failed'),
|
||||
});
|
||||
}
|
||||
setSavingState(false);
|
||||
} catch (error) {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: t('channel_creation_failed'),
|
||||
});
|
||||
setSavingState(false);
|
||||
}
|
||||
setSavingState(false);
|
||||
}, [prepareSlackRequest, t, notifications]);
|
||||
|
||||
const prepareWebhookRequest = useCallback(() => {
|
||||
@ -161,6 +180,65 @@ function CreateAlertChannels({
|
||||
}
|
||||
setSavingState(false);
|
||||
}, [prepareWebhookRequest, t, notifications]);
|
||||
|
||||
const preparePagerRequest = useCallback(() => {
|
||||
const validationError = ValidatePagerChannel(selectedConfig as PagerChannel);
|
||||
if (validationError !== '') {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: validationError,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: selectedConfig?.name || '',
|
||||
send_resolved: true,
|
||||
routing_key: selectedConfig?.routing_key || '',
|
||||
client: selectedConfig?.client || '',
|
||||
client_url: selectedConfig?.client_url || '',
|
||||
description: selectedConfig?.description || '',
|
||||
severity: selectedConfig?.severity || '',
|
||||
component: selectedConfig?.component || '',
|
||||
group: selectedConfig?.group || '',
|
||||
class: selectedConfig?.class || '',
|
||||
details: selectedConfig.details || '',
|
||||
detailsArray: JSON.parse(selectedConfig.details || '{}'),
|
||||
};
|
||||
}, [selectedConfig, notifications]);
|
||||
|
||||
const onPagerHandler = useCallback(async () => {
|
||||
setSavingState(true);
|
||||
const request = preparePagerRequest();
|
||||
|
||||
if (request) {
|
||||
try {
|
||||
const response = await createPagerApi(request);
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success',
|
||||
description: t('channel_creation_done'),
|
||||
});
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: response.error || t('channel_creation_failed'),
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: t('channel_creation_failed'),
|
||||
});
|
||||
}
|
||||
}
|
||||
setSavingState(false);
|
||||
}, [t, notifications, preparePagerRequest]);
|
||||
|
||||
const onSaveHandler = useCallback(
|
||||
async (value: ChannelType) => {
|
||||
switch (value) {
|
||||
@ -170,6 +248,9 @@ function CreateAlertChannels({
|
||||
case WebhookType:
|
||||
onWebhookHandler();
|
||||
break;
|
||||
case PagerType:
|
||||
onPagerHandler();
|
||||
break;
|
||||
default:
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@ -177,7 +258,7 @@ function CreateAlertChannels({
|
||||
});
|
||||
}
|
||||
},
|
||||
[onSlackHandler, t, onWebhookHandler, notifications],
|
||||
[onSlackHandler, t, onPagerHandler, onWebhookHandler, notifications],
|
||||
);
|
||||
|
||||
const performChannelTest = useCallback(
|
||||
@ -195,6 +276,10 @@ function CreateAlertChannels({
|
||||
request = prepareSlackRequest();
|
||||
response = await testSlackApi(request);
|
||||
break;
|
||||
case PagerType:
|
||||
request = preparePagerRequest();
|
||||
if (request) response = await testPagerApi(request);
|
||||
break;
|
||||
default:
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@ -204,7 +289,7 @@ function CreateAlertChannels({
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
if (response && response.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success',
|
||||
description: t('channel_test_done'),
|
||||
@ -223,7 +308,13 @@ function CreateAlertChannels({
|
||||
}
|
||||
setTestingState(false);
|
||||
},
|
||||
[prepareWebhookRequest, t, prepareSlackRequest, notifications],
|
||||
[
|
||||
prepareWebhookRequest,
|
||||
t,
|
||||
preparePagerRequest,
|
||||
prepareSlackRequest,
|
||||
notifications,
|
||||
],
|
||||
);
|
||||
|
||||
const onTestHandler = useCallback(
|
||||
@ -249,6 +340,7 @@ function CreateAlertChannels({
|
||||
initialValue: {
|
||||
type,
|
||||
...selectedConfig,
|
||||
...PagerInitialConfig,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
@ -1,13 +1,18 @@
|
||||
import { Form, notification } from 'antd';
|
||||
import editPagerApi from 'api/channels/editPager';
|
||||
import editSlackApi from 'api/channels/editSlack';
|
||||
import editWebhookApi from 'api/channels/editWebhook';
|
||||
import testPagerApi from 'api/channels/testPager';
|
||||
import testSlackApi from 'api/channels/testSlack';
|
||||
import testWebhookApi from 'api/channels/testWebhook';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
ChannelType,
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
SlackType,
|
||||
ValidatePagerChannel,
|
||||
WebhookChannel,
|
||||
WebhookType,
|
||||
} from 'container/CreateAlertChannels/config';
|
||||
@ -25,7 +30,7 @@ function EditAlertChannels({
|
||||
|
||||
const [formInstance] = Form.useForm();
|
||||
const [selectedConfig, setSelectedConfig] = useState<
|
||||
Partial<SlackChannel & WebhookChannel>
|
||||
Partial<SlackChannel & WebhookChannel & PagerChannel>
|
||||
>({
|
||||
...initialValue,
|
||||
});
|
||||
@ -138,15 +143,66 @@ function EditAlertChannels({
|
||||
setSavingState(false);
|
||||
}, [prepareWebhookRequest, t, notifications, selectedConfig]);
|
||||
|
||||
const preparePagerRequest = useCallback(() => {
|
||||
return {
|
||||
name: selectedConfig.name || '',
|
||||
routing_key: selectedConfig.routing_key,
|
||||
client: selectedConfig.client,
|
||||
client_url: selectedConfig.client_url,
|
||||
description: selectedConfig.description,
|
||||
severity: selectedConfig.severity,
|
||||
component: selectedConfig.component,
|
||||
class: selectedConfig.class,
|
||||
group: selectedConfig.group,
|
||||
details: selectedConfig.details,
|
||||
detailsArray: JSON.parse(selectedConfig.details || '{}'),
|
||||
id,
|
||||
};
|
||||
}, [id, selectedConfig]);
|
||||
|
||||
const onPagerEditHandler = useCallback(async () => {
|
||||
setSavingState(true);
|
||||
const validationError = ValidatePagerChannel(selectedConfig as PagerChannel);
|
||||
|
||||
if (validationError !== '') {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: validationError,
|
||||
});
|
||||
setSavingState(false);
|
||||
return;
|
||||
}
|
||||
const response = await editPagerApi(preparePagerRequest());
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success',
|
||||
description: t('channel_edit_done'),
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
history.replace(ROUTES.SETTINGS);
|
||||
}, 2000);
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description: response.error || t('channel_edit_failed'),
|
||||
});
|
||||
}
|
||||
setSavingState(false);
|
||||
}, [preparePagerRequest, notifications, selectedConfig, t]);
|
||||
|
||||
const onSaveHandler = useCallback(
|
||||
(value: ChannelType) => {
|
||||
if (value === SlackType) {
|
||||
onSlackEditHandler();
|
||||
} else if (value === WebhookType) {
|
||||
onWebhookEditHandler();
|
||||
} else if (value === PagerType) {
|
||||
onPagerEditHandler();
|
||||
}
|
||||
},
|
||||
[onSlackEditHandler, onWebhookEditHandler],
|
||||
[onSlackEditHandler, onWebhookEditHandler, onPagerEditHandler],
|
||||
);
|
||||
|
||||
const performChannelTest = useCallback(
|
||||
@ -164,6 +220,10 @@ function EditAlertChannels({
|
||||
request = prepareSlackRequest();
|
||||
response = await testSlackApi(request);
|
||||
break;
|
||||
case PagerType:
|
||||
request = preparePagerRequest();
|
||||
if (request) response = await testPagerApi(request);
|
||||
break;
|
||||
default:
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
@ -173,7 +233,7 @@ function EditAlertChannels({
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
if (response && response.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success',
|
||||
description: t('channel_test_done'),
|
||||
@ -192,7 +252,13 @@ function EditAlertChannels({
|
||||
}
|
||||
setTestingState(false);
|
||||
},
|
||||
[prepareWebhookRequest, t, prepareSlackRequest, notifications],
|
||||
[
|
||||
t,
|
||||
prepareWebhookRequest,
|
||||
preparePagerRequest,
|
||||
prepareSlackRequest,
|
||||
notifications,
|
||||
],
|
||||
);
|
||||
|
||||
const onTestHandler = useCallback(
|
||||
@ -216,7 +282,7 @@ function EditAlertChannels({
|
||||
NotificationElement,
|
||||
title: t('page_title_edit'),
|
||||
initialValue,
|
||||
nameDisable: true,
|
||||
editing: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -0,0 +1,64 @@
|
||||
import { Input, Select } from 'antd';
|
||||
import FormItem from 'antd/lib/form/FormItem';
|
||||
import { LabelFilterStatement } from 'container/CreateAlertChannels/config';
|
||||
import React from 'react';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
// LabelFilterForm supports filters or matchers on alert notifications
|
||||
// presently un-used but will be introduced to the channel creation at some
|
||||
// point
|
||||
function LabelFilterForm({ setFilter }: LabelFilterProps): JSX.Element {
|
||||
return (
|
||||
<FormItem name="label_filter" label="Notify When (Optional)">
|
||||
<Input.Group compact>
|
||||
<Select
|
||||
defaultValue="Severity"
|
||||
style={{ width: '15%' }}
|
||||
onChange={(event): void => {
|
||||
setFilter((value) => {
|
||||
const first: LabelFilterStatement = value[0] as LabelFilterStatement;
|
||||
first.name = event;
|
||||
return [first];
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Option value="severity">Severity</Option>
|
||||
<Option value="service">Service</Option>
|
||||
</Select>
|
||||
<Select
|
||||
defaultValue="="
|
||||
onChange={(event): void => {
|
||||
setFilter((value) => {
|
||||
const first: LabelFilterStatement = value[0] as LabelFilterStatement;
|
||||
first.comparator = event;
|
||||
return [first];
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Option value="=">=</Option>
|
||||
<Option value="!=">!=</Option>
|
||||
</Select>
|
||||
<Input
|
||||
style={{ width: '20%' }}
|
||||
placeholder="enter a text here"
|
||||
onChange={(event): void => {
|
||||
setFilter((value) => {
|
||||
const first: LabelFilterStatement = value[0] as LabelFilterStatement;
|
||||
first.value = event.target.value;
|
||||
return [first];
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Input.Group>
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
|
||||
export interface LabelFilterProps {
|
||||
setFilter: React.Dispatch<
|
||||
React.SetStateAction<Partial<Array<LabelFilterStatement>>>
|
||||
>;
|
||||
}
|
||||
|
||||
export default LabelFilterForm;
|
155
frontend/src/container/FormAlertChannels/Settings/Pager.tsx
Normal file
155
frontend/src/container/FormAlertChannels/Settings/Pager.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
import { Input } from 'antd';
|
||||
import FormItem from 'antd/lib/form/FormItem';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { PagerChannel } from '../../CreateAlertChannels/config';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element {
|
||||
const { t } = useTranslation('channels');
|
||||
return (
|
||||
<>
|
||||
<FormItem name="routing_key" label={t('field_pager_routing_key')} required>
|
||||
<Input
|
||||
onChange={(event): void => {
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
routing_key: event.target.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="description"
|
||||
help={t('help_pager_description')}
|
||||
label={t('field_pager_description')}
|
||||
required
|
||||
>
|
||||
<TextArea
|
||||
rows={4}
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
description: event.target.value,
|
||||
}))
|
||||
}
|
||||
placeholder={t('placeholder_pager_description')}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="severity"
|
||||
help={t('help_pager_severity')}
|
||||
label={t('field_pager_severity')}
|
||||
>
|
||||
<Input
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
severity: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="details"
|
||||
help={t('help_pager_details')}
|
||||
label={t('field_pager_details')}
|
||||
>
|
||||
<TextArea
|
||||
rows={4}
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
details: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="component"
|
||||
help={t('help_pager_component')}
|
||||
label={t('field_pager_component')}
|
||||
>
|
||||
<Input
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
component: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="group"
|
||||
help={t('help_pager_group')}
|
||||
label={t('field_pager_group')}
|
||||
>
|
||||
<Input
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
group: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="class"
|
||||
help={t('help_pager_class')}
|
||||
label={t('field_pager_class')}
|
||||
>
|
||||
<Input
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
class: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
name="client"
|
||||
help={t('help_pager_client')}
|
||||
label={t('field_pager_client')}
|
||||
>
|
||||
<Input
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
client: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
name="client_url"
|
||||
help={t('help_pager_client_url')}
|
||||
label={t('field_pager_client_url')}
|
||||
>
|
||||
<Input
|
||||
onChange={(event): void =>
|
||||
setSelectedConfig((value) => ({
|
||||
...value,
|
||||
client_url: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</FormItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface PagerFormProps {
|
||||
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<PagerChannel>>>;
|
||||
}
|
||||
|
||||
export default PagerForm;
|
@ -4,14 +4,18 @@ import { Store } from 'antd/lib/form/interface';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
ChannelType,
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
SlackType,
|
||||
WebhookChannel,
|
||||
WebhookType,
|
||||
} from 'container/CreateAlertChannels/config';
|
||||
import history from 'lib/history';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import PagerSettings from './Settings/Pager';
|
||||
import SlackSettings from './Settings/Slack';
|
||||
import WebhookSettings from './Settings/Webhook';
|
||||
import { Button } from './styles';
|
||||
@ -31,7 +35,7 @@ function FormAlertChannels({
|
||||
NotificationElement,
|
||||
title,
|
||||
initialValue,
|
||||
nameDisable = false,
|
||||
editing = false,
|
||||
}: FormAlertChannelsProps): JSX.Element {
|
||||
const { t } = useTranslation('channels');
|
||||
|
||||
@ -41,6 +45,9 @@ function FormAlertChannels({
|
||||
return <SlackSettings setSelectedConfig={setSelectedConfig} />;
|
||||
case WebhookType:
|
||||
return <WebhookSettings setSelectedConfig={setSelectedConfig} />;
|
||||
case PagerType:
|
||||
return <PagerSettings setSelectedConfig={setSelectedConfig} />;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -54,7 +61,7 @@ function FormAlertChannels({
|
||||
<Form initialValues={initialValue} layout="vertical" form={formInstance}>
|
||||
<FormItem label={t('field_channel_name')} labelAlign="left" name="name">
|
||||
<Input
|
||||
disabled={nameDisable}
|
||||
disabled={editing}
|
||||
onChange={(event): void => {
|
||||
setSelectedConfig((state) => ({
|
||||
...state,
|
||||
@ -65,13 +72,16 @@ function FormAlertChannels({
|
||||
</FormItem>
|
||||
|
||||
<FormItem label={t('field_channel_type')} labelAlign="left" name="type">
|
||||
<Select onChange={onTypeChangeHandler} value={type}>
|
||||
<Select disabled={editing} onChange={onTypeChangeHandler} value={type}>
|
||||
<Option value="slack" key="slack">
|
||||
Slack
|
||||
</Option>
|
||||
<Option value="webhook" key="webhook">
|
||||
Webhook
|
||||
</Option>
|
||||
<Option value="pagerduty" key="pagerduty">
|
||||
Pagerduty
|
||||
</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
|
||||
@ -109,7 +119,9 @@ function FormAlertChannels({
|
||||
interface FormAlertChannelsProps {
|
||||
formInstance: FormInstance;
|
||||
type: ChannelType;
|
||||
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
||||
setSelectedConfig: React.Dispatch<
|
||||
React.SetStateAction<Partial<SlackChannel & WebhookChannel & PagerChannel>>
|
||||
>;
|
||||
onTypeChangeHandler: (value: ChannelType) => void;
|
||||
onSaveHandler: (props: ChannelType) => void;
|
||||
onTestHandler: (props: ChannelType) => void;
|
||||
@ -121,11 +133,12 @@ interface FormAlertChannelsProps {
|
||||
>;
|
||||
title: string;
|
||||
initialValue: Store;
|
||||
nameDisable?: boolean;
|
||||
// editing indicates if the form is opened in edit mode
|
||||
editing?: boolean;
|
||||
}
|
||||
|
||||
FormAlertChannels.defaultProps = {
|
||||
nameDisable: undefined,
|
||||
editing: undefined,
|
||||
};
|
||||
|
||||
export default FormAlertChannels;
|
||||
|
@ -2,6 +2,8 @@ import { Typography } from 'antd';
|
||||
import get from 'api/channels/get';
|
||||
import Spinner from 'components/Spinner';
|
||||
import {
|
||||
PagerChannel,
|
||||
PagerType,
|
||||
SlackChannel,
|
||||
SlackType,
|
||||
WebhookChannel,
|
||||
@ -35,36 +37,64 @@ function ChannelsEdit(): JSX.Element {
|
||||
const { data: ChannelData } = data.payload;
|
||||
|
||||
const value = JSON.parse(ChannelData);
|
||||
let type = '';
|
||||
let channel: SlackChannel & WebhookChannel = { name: '' };
|
||||
|
||||
if (value && 'slack_configs' in value) {
|
||||
const slackConfig = value.slack_configs[0];
|
||||
channel = slackConfig;
|
||||
type = SlackType;
|
||||
} else if (value && 'webhook_configs' in value) {
|
||||
const webhookConfig = value.webhook_configs[0];
|
||||
channel = webhookConfig;
|
||||
channel.api_url = webhookConfig.url;
|
||||
|
||||
if ('http_config' in webhookConfig) {
|
||||
const httpConfig = webhookConfig.http_config;
|
||||
if ('basic_auth' in httpConfig) {
|
||||
channel.username = webhookConfig.http_config?.basic_auth?.username;
|
||||
channel.password = webhookConfig.http_config?.basic_auth?.password;
|
||||
} else if ('authorization' in httpConfig) {
|
||||
channel.password = webhookConfig.http_config?.authorization?.credentials;
|
||||
}
|
||||
const prepChannelConfig = (): {
|
||||
type: string;
|
||||
channel: SlackChannel & WebhookChannel & PagerChannel;
|
||||
} => {
|
||||
let channel: SlackChannel & WebhookChannel & PagerChannel = { name: '' };
|
||||
if (value && 'slack_configs' in value) {
|
||||
const slackConfig = value.slack_configs[0];
|
||||
channel = slackConfig;
|
||||
return {
|
||||
type: SlackType,
|
||||
channel,
|
||||
};
|
||||
}
|
||||
type = WebhookType;
|
||||
}
|
||||
if (value && 'pagerduty_configs' in value) {
|
||||
const pagerConfig = value.pagerduty_configs[0];
|
||||
channel = pagerConfig;
|
||||
channel.details = JSON.stringify(pagerConfig.details);
|
||||
channel.detailsArray = { ...pagerConfig.details };
|
||||
return {
|
||||
type: PagerType,
|
||||
channel,
|
||||
};
|
||||
}
|
||||
|
||||
if (value && 'webhook_configs' in value) {
|
||||
const webhookConfig = value.webhook_configs[0];
|
||||
channel = webhookConfig;
|
||||
channel.api_url = webhookConfig.url;
|
||||
|
||||
if ('http_config' in webhookConfig) {
|
||||
const httpConfig = webhookConfig.http_config;
|
||||
if ('basic_auth' in httpConfig) {
|
||||
channel.username = webhookConfig.http_config?.basic_auth?.username;
|
||||
channel.password = webhookConfig.http_config?.basic_auth?.password;
|
||||
} else if ('authorization' in httpConfig) {
|
||||
channel.password = webhookConfig.http_config?.authorization?.credentials;
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: WebhookType,
|
||||
channel,
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: SlackType,
|
||||
channel,
|
||||
};
|
||||
};
|
||||
|
||||
const target = prepChannelConfig();
|
||||
|
||||
return (
|
||||
<EditAlertChannels
|
||||
{...{
|
||||
initialValue: {
|
||||
...channel,
|
||||
type,
|
||||
...target.channel,
|
||||
type: target.type,
|
||||
name: value.name,
|
||||
},
|
||||
}}
|
||||
|
8
frontend/src/types/api/channels/createPager.ts
Normal file
8
frontend/src/types/api/channels/createPager.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { PagerChannel } from 'container/CreateAlertChannels/config';
|
||||
|
||||
export type Props = PagerChannel;
|
||||
|
||||
export interface PayloadProps {
|
||||
data: string;
|
||||
status: string;
|
||||
}
|
10
frontend/src/types/api/channels/editPager.ts
Normal file
10
frontend/src/types/api/channels/editPager.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { PagerChannel } from 'container/CreateAlertChannels/config';
|
||||
|
||||
export interface Props extends PagerChannel {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface PayloadProps {
|
||||
data: string;
|
||||
status: string;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user