Adds new templates cheatsheats (#3643)
Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com>
This commit is contained in:
parent
c7895c2308
commit
36d2c3bdb7
12 changed files with 314 additions and 175 deletions
|
|
@ -334,7 +334,48 @@ def test_preview_alert_receive_channel_backend_templater(
|
|||
response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {"preview": "title: alert!"}
|
||||
assert response.json() == {"preview": "title: alert!", "is_valid_json_object": False}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_alert_receive_channel_template_is_valid_json_check(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_alert_receive_channel,
|
||||
make_channel_filter,
|
||||
make_alert_group,
|
||||
make_alert,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token()
|
||||
alert_receive_channel = make_alert_receive_channel(organization)
|
||||
default_channel_filter = make_channel_filter(alert_receive_channel, is_default=True)
|
||||
alert_group = make_alert_group(alert_receive_channel, channel_filter=default_channel_filter)
|
||||
make_alert(alert_group=alert_group, raw_request_data={"title": "alert!"})
|
||||
client = APIClient()
|
||||
|
||||
url = reverse(
|
||||
"api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key}
|
||||
)
|
||||
|
||||
# template which should produce valid json string
|
||||
data = {
|
||||
"template_body": "{{ payload | tojson }}",
|
||||
"template_name": "alert_group_multi_label",
|
||||
}
|
||||
response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["is_valid_json_object"] is True
|
||||
|
||||
# template which produce not avalid json string
|
||||
data = {
|
||||
"template_body": "{{ payload.title }}",
|
||||
"template_name": "alert_group_multi_label",
|
||||
}
|
||||
response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json()["is_valid_json_object"] is False
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
@ -360,12 +401,12 @@ def test_preview_alert_group_labels(
|
|||
|
||||
data = {
|
||||
"template_body": "{{ payload.labels | tojson }}",
|
||||
"template_name": "alert_group_labels",
|
||||
"template_name": "alert_group_multi_label",
|
||||
}
|
||||
response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.json() == {"preview": '{"1": "2"}'}
|
||||
assert response.json() == {"preview": '{"1": "2"}', "is_valid_json_object": True}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
|||
|
|
@ -250,6 +250,8 @@ GROUPING_ID = "grouping_id"
|
|||
SOURCE_LINK = "source_link"
|
||||
ROUTE = "route"
|
||||
ALERT_GROUP_LABELS = "alert_group_labels"
|
||||
ALERT_GROUP_MULTI_LABEL = "alert_group_multi_label"
|
||||
ALERT_GROUP_DYNAMIC_LABEL = "alert_group_dynamic_label"
|
||||
|
||||
NOTIFICATION_CHANNEL_TO_TEMPLATER_MAP = {
|
||||
SLACK: AlertSlackTemplater,
|
||||
|
|
@ -272,7 +274,8 @@ BEHAVIOUR_TEMPLATE_NAMES = [
|
|||
GROUPING_ID,
|
||||
SOURCE_LINK,
|
||||
ROUTE,
|
||||
ALERT_GROUP_LABELS,
|
||||
ALERT_GROUP_MULTI_LABEL,
|
||||
ALERT_GROUP_DYNAMIC_LABEL,
|
||||
]
|
||||
ALL_TEMPLATE_NAMES = APPEARANCE_TEMPLATE_NAMES + BEHAVIOUR_TEMPLATE_NAMES
|
||||
|
||||
|
|
@ -294,7 +297,10 @@ class PreviewTemplateMixin:
|
|||
),
|
||||
responses=inline_serializer(
|
||||
name="PreviewTemplateResponse",
|
||||
fields={"preview": serializers.CharField(allow_null=True)},
|
||||
fields={
|
||||
"preview": serializers.CharField(allow_null=True),
|
||||
"is_valid_json_object": serializers.BooleanField(),
|
||||
},
|
||||
),
|
||||
)
|
||||
@action(methods=["post"], detail=True)
|
||||
|
|
@ -351,9 +357,15 @@ class PreviewTemplateMixin:
|
|||
return Response({"preview": e.fallback_message}, status.HTTP_200_OK)
|
||||
else:
|
||||
templated_attr = None
|
||||
response = {"preview": templated_attr}
|
||||
response = {"preview": templated_attr, "is_valid_json_object": self.is_valid_json_object(templated_attr)}
|
||||
return Response(response, status=status.HTTP_200_OK)
|
||||
|
||||
def is_valid_json_object(self, json_str):
|
||||
try:
|
||||
return isinstance(json.loads(json_str), dict)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def get_alert_to_template(self, payload=None):
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BaseTemplateOptions } from 'pages/integration/IntegrationCommon.config';
|
||||
import { IntegrationTemplateOptions } from 'pages/integration/IntegrationCommon.config';
|
||||
|
||||
export interface Template {
|
||||
name: string;
|
||||
|
|
@ -22,19 +22,19 @@ export interface TemplateForEdit {
|
|||
export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
||||
web_title_template: {
|
||||
displayName: 'Web title',
|
||||
name: BaseTemplateOptions.WebTitle.key,
|
||||
name: IntegrationTemplateOptions.WebTitle.key,
|
||||
description: '',
|
||||
type: 'html',
|
||||
},
|
||||
web_message_template: {
|
||||
displayName: 'Web message',
|
||||
name: BaseTemplateOptions.WebMessage.key,
|
||||
name: IntegrationTemplateOptions.WebMessage.key,
|
||||
description: '',
|
||||
type: 'html',
|
||||
},
|
||||
slack_title_template: {
|
||||
displayName: 'Slack title',
|
||||
name: BaseTemplateOptions.SlackTitle.key,
|
||||
name: IntegrationTemplateOptions.SlackTitle.key,
|
||||
description: '',
|
||||
additionalData: {
|
||||
chatOpsName: 'slack',
|
||||
|
|
@ -44,26 +44,26 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
|||
type: 'plain',
|
||||
},
|
||||
sms_title_template: {
|
||||
name: BaseTemplateOptions.SMS.key,
|
||||
name: IntegrationTemplateOptions.SMS.key,
|
||||
displayName: 'Sms title',
|
||||
description:
|
||||
"Result of this template will be used as title of SMS message. Please don't include any urls, or phone numbers, to avoid SMS message being blocked by carriers.",
|
||||
type: 'plain',
|
||||
},
|
||||
phone_call_title_template: {
|
||||
name: BaseTemplateOptions.Phone.key,
|
||||
name: IntegrationTemplateOptions.Phone.key,
|
||||
displayName: 'Phone Call title',
|
||||
description: '',
|
||||
type: 'plain',
|
||||
},
|
||||
email_title_template: {
|
||||
name: BaseTemplateOptions.EmailTitle.key,
|
||||
name: IntegrationTemplateOptions.EmailTitle.key,
|
||||
displayName: 'Email title',
|
||||
description: '',
|
||||
type: 'plain',
|
||||
},
|
||||
telegram_title_template: {
|
||||
name: BaseTemplateOptions.TelegramTitle.key,
|
||||
name: IntegrationTemplateOptions.TelegramTitle.key,
|
||||
displayName: 'Telegram title',
|
||||
description: '',
|
||||
additionalData: {
|
||||
|
|
@ -73,7 +73,7 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
|||
type: 'plain',
|
||||
},
|
||||
slack_message_template: {
|
||||
name: BaseTemplateOptions.SlackMessage.key,
|
||||
name: IntegrationTemplateOptions.SlackMessage.key,
|
||||
displayName: 'Slack message',
|
||||
description: '',
|
||||
additionalData: {
|
||||
|
|
@ -84,13 +84,13 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
|||
type: 'plain',
|
||||
},
|
||||
email_message_template: {
|
||||
name: BaseTemplateOptions.EmailMessage.key,
|
||||
name: IntegrationTemplateOptions.EmailMessage.key,
|
||||
displayName: 'Email message',
|
||||
description: '',
|
||||
type: 'plain',
|
||||
},
|
||||
telegram_message_template: {
|
||||
name: BaseTemplateOptions.TelegramMessage.key,
|
||||
name: IntegrationTemplateOptions.TelegramMessage.key,
|
||||
displayName: 'Telegram message',
|
||||
description: '',
|
||||
additionalData: {
|
||||
|
|
@ -100,7 +100,7 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
|||
type: 'plain',
|
||||
},
|
||||
slack_image_url_template: {
|
||||
name: BaseTemplateOptions.SlackImage.key,
|
||||
name: IntegrationTemplateOptions.SlackImage.key,
|
||||
displayName: 'Slack image url',
|
||||
description: '',
|
||||
additionalData: {
|
||||
|
|
@ -111,13 +111,13 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
|||
type: 'plain',
|
||||
},
|
||||
web_image_url_template: {
|
||||
name: BaseTemplateOptions.WebImage.key,
|
||||
name: IntegrationTemplateOptions.WebImage.key,
|
||||
displayName: 'Web image url',
|
||||
description: '',
|
||||
type: 'image',
|
||||
},
|
||||
telegram_image_url_template: {
|
||||
name: BaseTemplateOptions.TelegramImage.key,
|
||||
name: IntegrationTemplateOptions.TelegramImage.key,
|
||||
displayName: 'Telegram image url',
|
||||
description: '',
|
||||
additionalData: {
|
||||
|
|
@ -127,33 +127,33 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = {
|
|||
type: 'image',
|
||||
},
|
||||
grouping_id_template: {
|
||||
name: BaseTemplateOptions.Grouping.key,
|
||||
name: IntegrationTemplateOptions.Grouping.key,
|
||||
displayName: 'Grouping',
|
||||
description:
|
||||
'Reduce noise, minimize duplication with Alert Grouping, based on time, alert content, and even multiple features at the same time. Check the cheasheet to customize your template.',
|
||||
type: 'plain',
|
||||
},
|
||||
acknowledge_condition_template: {
|
||||
name: BaseTemplateOptions.Autoacknowledge.key,
|
||||
name: IntegrationTemplateOptions.Autoacknowledge.key,
|
||||
displayName: 'Acknowledge condition',
|
||||
description: '',
|
||||
type: 'boolean',
|
||||
},
|
||||
resolve_condition_template: {
|
||||
name: BaseTemplateOptions.Resolve.key,
|
||||
name: IntegrationTemplateOptions.Resolve.key,
|
||||
displayName: 'Resolve condition',
|
||||
description:
|
||||
'When monitoring systems return to normal, they can send "resolve" alerts. OnCall can use these signals to resolve alert groups accordingly.',
|
||||
type: 'boolean',
|
||||
},
|
||||
source_link_template: {
|
||||
name: BaseTemplateOptions.SourceLink.key,
|
||||
name: IntegrationTemplateOptions.SourceLink.key,
|
||||
displayName: 'Source link',
|
||||
description: '',
|
||||
type: 'plain',
|
||||
},
|
||||
route_template: {
|
||||
name: BaseTemplateOptions.Routing.key,
|
||||
name: IntegrationTemplateOptions.Routing.key,
|
||||
displayName: 'Routing',
|
||||
description:
|
||||
'Routes direct alerts to different escalation chains based on the content, such as severity or region.',
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@ export const genericTemplateCheatSheet: CheatSheetInterface = {
|
|||
{
|
||||
name: 'Jinja2 refresher ',
|
||||
listItems: [
|
||||
{ listItemName: ' {{ payload.labels.foo }} - extract field value' },
|
||||
{
|
||||
listItemName: 'Extract field value',
|
||||
codeExample: '{{ payload.labels.foo }}',
|
||||
},
|
||||
{
|
||||
listItemName: 'Conditions',
|
||||
codeExample: `{%- if "status" in payload %}
|
||||
|
|
@ -120,7 +123,10 @@ export const slackMessageTemplateCheatSheet: CheatSheetInterface = {
|
|||
{
|
||||
name: 'Jinja2 refresher ',
|
||||
listItems: [
|
||||
{ listItemName: ' {{ payload.labels.foo }} - extract field value' },
|
||||
{
|
||||
listItemName: 'Extract field value',
|
||||
codeExample: '{{ payload.labels.foo }}',
|
||||
},
|
||||
{
|
||||
listItemName: 'Conditions',
|
||||
codeExample: '{%- if "status" in payload %} \n {{ payload.status }} \n {% endif -%}',
|
||||
|
|
@ -172,3 +178,138 @@ export const slackMessageTemplateCheatSheet: CheatSheetInterface = {
|
|||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const alertGroupDynamicLabelCheatSheet: CheatSheetInterface = {
|
||||
name: 'Dynamic Label cheatsheet',
|
||||
description: 'Dynamic Label template is used to extract value for a specified key from the alert payload.',
|
||||
fields: [
|
||||
{
|
||||
name: 'Examples',
|
||||
listItems: [
|
||||
{
|
||||
listItemName:
|
||||
'Extracting the value associated with the "severity" key. If the key is not present, it defaults to "unknown."',
|
||||
codeExample: `{{ payload.get("severity", "unknown") }}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Jinja2 refresher ',
|
||||
listItems: [
|
||||
{
|
||||
listItemName: 'Extract field value',
|
||||
codeExample: '{{ payload.labels.foo }}',
|
||||
},
|
||||
{
|
||||
listItemName: 'Conditions',
|
||||
codeExample: '{%- if "status" in payload %} \n {{ payload.status }} \n {% endif -%}',
|
||||
},
|
||||
{ listItemName: 'Booleans', codeExample: '{{ payload.status == “resolved” }}' },
|
||||
{ listItemName: 'Loops', codeExample: '{% for label in labels %} \n {{ label.title }} \n {% endfor %}' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Additional jinja2 variables',
|
||||
listItems: [{ listItemName: 'payload - payload of the first alert in the group' }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const alertGroupMultiLabelExtractionCheatSheet: CheatSheetInterface = {
|
||||
name: 'Multi-label extraction cheatsheet',
|
||||
description:
|
||||
'Multi-label extraction template allows extracting and modifying multiple labels from an alert payload. The Jinja template must result in string, representing valid JSON dictionary. See Examples for getting familiar with the idea',
|
||||
fields: [
|
||||
{
|
||||
name: 'Examples',
|
||||
listItems: [
|
||||
{
|
||||
listItemName: 'Extracting all the labels from the specific payload field',
|
||||
codeExample: `{{ payload.labels | tojson }}`,
|
||||
},
|
||||
{
|
||||
listItemName: 'Extract labels from different payload fields',
|
||||
codeExample: `{%- set labels = {} -%}
|
||||
{# add several labels #}
|
||||
{%- set labels = dict(labels, **payload.commonLabels) -%}
|
||||
{# add one label #}
|
||||
{%- set labels = dict(labels, **{"status": payload.status}) -%}
|
||||
{# add label not from payload #}
|
||||
{%- set labels = dict(labels, **{"service": "oncall"}) -%}
|
||||
{# dump labels dict to json string, so OnCall can parse it #}
|
||||
{{ labels | tojson }}
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Jinja2 refresher ',
|
||||
listItems: [
|
||||
{ listItemName: 'Dump a structure to JSON string', codeExample: '{{ payload.labels | tojson }}' },
|
||||
{
|
||||
listItemName: 'Extract field value',
|
||||
codeExample: '{{ payload.labels.foo }}',
|
||||
},
|
||||
{
|
||||
listItemName: 'Conditions',
|
||||
codeExample: '{%- if "status" in payload %} \n {{ payload.status }} \n {% endif -%}',
|
||||
},
|
||||
{ listItemName: 'Booleans', codeExample: '{{ payload.status == “resolved” }}' },
|
||||
{ listItemName: 'Loops', codeExample: '{% for label in labels %} \n {{ label.title }} \n {% endfor %}' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Additional jinja2 variables',
|
||||
listItems: [{ listItemName: 'payload - payload of the first alert in the group' }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const webhookPayloadCheatSheet: CheatSheetInterface = {
|
||||
name: 'Webhook Payload cheatsheet ',
|
||||
description:
|
||||
"Webhook payload template is powered by Jinja2. It's used to process webhook data and customize the output",
|
||||
fields: [
|
||||
{
|
||||
name: 'Examples',
|
||||
listItems: [
|
||||
{
|
||||
listItemName:
|
||||
'Construct a custom webhook payload from various webhook data fields and output it as a JSON string',
|
||||
codeExample: `{%- set payload = {} -%}
|
||||
{# add alert group labels #}
|
||||
{%- set payload = dict(payload, **{"labels": alert_group.labels}) -%}
|
||||
{# add some other fields from webhook data just for example #}
|
||||
{%- set payload = dict(payload, **{"event": event.type, "integration": integration.name}) -%}
|
||||
{# encode payload dict to json #}
|
||||
{{ payload | tojson }}
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Jinja2 refresher ',
|
||||
listItems: [
|
||||
{
|
||||
listItemName: 'Extract field value',
|
||||
codeExample: '{{ payload.labels.foo }}',
|
||||
},
|
||||
{
|
||||
listItemName: 'Show field value or “N/A” is not exist',
|
||||
codeExample: '{{ payload.labels.foo | default(“N/A”) }}',
|
||||
},
|
||||
{
|
||||
listItemName: 'Conditions',
|
||||
codeExample: '{%- if "status" in payload %} \n {{ payload.status }} \n {% endif -%}',
|
||||
},
|
||||
{ listItemName: 'Booleans', codeExample: '{{ payload.status == “resolved” }}' },
|
||||
{ listItemName: 'Loops', codeExample: '{% for label in labels %} \n {{ label.title }} \n {% endfor %}' },
|
||||
{ listItemName: 'Dump a structure to JSON string', codeExample: '{{ payload.labels | tojson }}' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Additional jinja2 variables',
|
||||
listItems: [{ listItemName: 'payload - payload of the first alert in the group' }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import IntegrationTemplate from 'containers/IntegrationTemplate/IntegrationTempl
|
|||
import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types';
|
||||
import { LabelsErrors } from 'models/label/label.types';
|
||||
import { ApiSchemas } from 'network/oncall-api/api.types';
|
||||
import { LabelTemplateOptions } from 'pages/integration/IntegrationCommon.config';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { openErrorNotification } from 'utils';
|
||||
import { DOCS_ROOT } from 'utils/consts';
|
||||
|
|
@ -199,8 +200,8 @@ const IntegrationLabelsForm = observer((props: IntegrationLabelsFormProps) => {
|
|||
<IntegrationTemplate
|
||||
id={id}
|
||||
template={{
|
||||
name: 'alert_group_labels',
|
||||
displayName: ``,
|
||||
name: LabelTemplateOptions.AlertGroupDynamicLabel.key,
|
||||
displayName: LabelTemplateOptions.AlertGroupDynamicLabel.value,
|
||||
}}
|
||||
templates={templates}
|
||||
templateBody={alertGroupLabels.custom[customLabelIndexToShowTemplateEditor].value.name}
|
||||
|
|
@ -222,8 +223,8 @@ const IntegrationLabelsForm = observer((props: IntegrationLabelsFormProps) => {
|
|||
<IntegrationTemplate
|
||||
id={id}
|
||||
template={{
|
||||
name: 'alert_group_labels',
|
||||
displayName: ``,
|
||||
name: LabelTemplateOptions.AlertGroupMultiLabel.key,
|
||||
displayName: LabelTemplateOptions.AlertGroupMultiLabel.value,
|
||||
}}
|
||||
templates={templates}
|
||||
templateBody={alertGroupLabels.template}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import {
|
|||
groupingTemplateCheatSheet,
|
||||
slackMessageTemplateCheatSheet,
|
||||
genericTemplateCheatSheet,
|
||||
alertGroupDynamicLabelCheatSheet,
|
||||
alertGroupMultiLabelExtractionCheatSheet,
|
||||
} from 'components/CheatSheet/CheatSheet.config';
|
||||
import MonacoEditor from 'components/MonacoEditor/MonacoEditor';
|
||||
import Text from 'components/Text/Text';
|
||||
|
|
@ -22,7 +24,7 @@ import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_
|
|||
import { AlertTemplatesDTO } from 'models/alert_templates/alert_templates';
|
||||
import { Alert } from 'models/alertgroup/alertgroup.types';
|
||||
import { ChannelFilter } from 'models/channel_filter/channel_filter.types';
|
||||
import { BaseTemplateOptions } from 'pages/integration/IntegrationCommon.config';
|
||||
import { IntegrationTemplateOptions, LabelTemplateOptions } from 'pages/integration/IntegrationCommon.config';
|
||||
import { useStore } from 'state/useStore';
|
||||
import LocationHelper from 'utils/LocationHelper';
|
||||
import { UserActions } from 'utils/authorization';
|
||||
|
|
@ -129,26 +131,30 @@ const IntegrationTemplate = observer((props: IntegrationTemplateProps) => {
|
|||
|
||||
const getCheatSheet = (templateKey: string) => {
|
||||
switch (templateKey) {
|
||||
case BaseTemplateOptions.Grouping.key:
|
||||
case BaseTemplateOptions.Resolve.key:
|
||||
case IntegrationTemplateOptions.Grouping.key:
|
||||
case IntegrationTemplateOptions.Resolve.key:
|
||||
return groupingTemplateCheatSheet;
|
||||
case BaseTemplateOptions.WebTitle.key:
|
||||
case BaseTemplateOptions.WebMessage.key:
|
||||
case BaseTemplateOptions.WebImage.key:
|
||||
case IntegrationTemplateOptions.WebTitle.key:
|
||||
case IntegrationTemplateOptions.WebMessage.key:
|
||||
case IntegrationTemplateOptions.WebImage.key:
|
||||
return genericTemplateCheatSheet;
|
||||
case BaseTemplateOptions.Autoacknowledge.key:
|
||||
case BaseTemplateOptions.SourceLink.key:
|
||||
case BaseTemplateOptions.Phone.key:
|
||||
case BaseTemplateOptions.SMS.key:
|
||||
case BaseTemplateOptions.SlackTitle.key:
|
||||
case BaseTemplateOptions.SlackMessage.key:
|
||||
case BaseTemplateOptions.SlackImage.key:
|
||||
case BaseTemplateOptions.TelegramTitle.key:
|
||||
case BaseTemplateOptions.TelegramMessage.key:
|
||||
case BaseTemplateOptions.TelegramImage.key:
|
||||
case BaseTemplateOptions.EmailTitle.key:
|
||||
case BaseTemplateOptions.EmailMessage.key:
|
||||
case IntegrationTemplateOptions.Autoacknowledge.key:
|
||||
case IntegrationTemplateOptions.SourceLink.key:
|
||||
case IntegrationTemplateOptions.Phone.key:
|
||||
case IntegrationTemplateOptions.SMS.key:
|
||||
case IntegrationTemplateOptions.SlackTitle.key:
|
||||
case IntegrationTemplateOptions.SlackMessage.key:
|
||||
case IntegrationTemplateOptions.SlackImage.key:
|
||||
case IntegrationTemplateOptions.TelegramTitle.key:
|
||||
case IntegrationTemplateOptions.TelegramMessage.key:
|
||||
case IntegrationTemplateOptions.TelegramImage.key:
|
||||
case IntegrationTemplateOptions.EmailTitle.key:
|
||||
case IntegrationTemplateOptions.EmailMessage.key:
|
||||
return slackMessageTemplateCheatSheet;
|
||||
case LabelTemplateOptions.AlertGroupDynamicLabel.key:
|
||||
return alertGroupDynamicLabelCheatSheet;
|
||||
case LabelTemplateOptions.AlertGroupMultiLabel.key:
|
||||
return alertGroupMultiLabelExtractionCheatSheet;
|
||||
default:
|
||||
return genericTemplateCheatSheet;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
TabsBar,
|
||||
VerticalGroup,
|
||||
} from '@grafana/ui';
|
||||
import { capitalCase } from 'change-case';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
|
@ -120,7 +121,12 @@ const OutgoingWebhookForm = observer((props: OutgoingWebhookFormProps) => {
|
|||
const getTemplateEditClickHandler = (formItem: FormItem, values, setFormFieldValue) => {
|
||||
return () => {
|
||||
const formValue = values[formItem.name];
|
||||
setTemplateToEdit({ value: formValue, displayName: undefined, description: undefined, name: formItem.name });
|
||||
setTemplateToEdit({
|
||||
value: formValue,
|
||||
displayName: `Webhook ${capitalCase(formItem.name)}`,
|
||||
description: undefined,
|
||||
name: formItem.name,
|
||||
});
|
||||
setOnFormChangeFn({ fn: (value) => setFormFieldValue(value) });
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,3 +26,7 @@
|
|||
.display-linebreak {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.extra-check {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { HorizontalGroup, Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui';
|
||||
import { Badge, HorizontalGroup, Icon, LoadingPlaceholder, VerticalGroup } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ import Text from 'components/Text/Text';
|
|||
import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types';
|
||||
import { Alert } from 'models/alertgroup/alertgroup.types';
|
||||
import { OutgoingWebhook } from 'models/outgoing_webhook/outgoing_webhook.types';
|
||||
import { LabelTemplateOptions } from 'pages/integration/IntegrationCommon.config';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { openErrorNotification } from 'utils';
|
||||
import { useDebouncedCallback } from 'utils/hooks';
|
||||
|
|
@ -51,7 +52,9 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => {
|
|||
templatePage,
|
||||
} = props;
|
||||
|
||||
const [result, setResult] = useState<{ preview: string | null } | undefined>(undefined);
|
||||
const [result, setResult] = useState<{ preview: string | null; is_valid_json_object?: boolean } | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [conditionalResult, setConditionalResult] = useState<ConditionalResult>({});
|
||||
|
||||
const store = useStore();
|
||||
|
|
@ -102,6 +105,29 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => {
|
|||
}
|
||||
};
|
||||
|
||||
function renderExtraChecks() {
|
||||
function getExtraCheckResult() {
|
||||
switch (templateName) {
|
||||
case LabelTemplateOptions.AlertGroupMultiLabel.key:
|
||||
return result.is_valid_json_object ? (
|
||||
<Badge color="green" icon="check" text="Output is a valid labels dictionary" />
|
||||
) : (
|
||||
<Badge
|
||||
color="red"
|
||||
icon="times"
|
||||
text="Output is not a labels dictionary. Template should produce valid JSON object. Consider using tojson filter."
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const checkResult = getExtraCheckResult();
|
||||
|
||||
return checkResult ? <div className={cx('extra-check')}>{checkResult}</div> : null;
|
||||
}
|
||||
|
||||
function renderResult() {
|
||||
switch (templateType) {
|
||||
case 'html': {
|
||||
|
|
@ -182,7 +208,14 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => {
|
|||
);
|
||||
}
|
||||
|
||||
return result ? <>{renderResult()}</> : <LoadingPlaceholder text="Loading..." />;
|
||||
return result ? (
|
||||
<>
|
||||
{renderExtraChecks()}
|
||||
{renderResult()}
|
||||
</>
|
||||
) : (
|
||||
<LoadingPlaceholder text="Loading..." />
|
||||
);
|
||||
});
|
||||
|
||||
export default TemplatePreview;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import cn from 'classnames/bind';
|
|||
import { debounce } from 'lodash-es';
|
||||
|
||||
import CheatSheet from 'components/CheatSheet/CheatSheet';
|
||||
import { genericTemplateCheatSheet } from 'components/CheatSheet/CheatSheet.config';
|
||||
import { genericTemplateCheatSheet, webhookPayloadCheatSheet } from 'components/CheatSheet/CheatSheet.config';
|
||||
import MonacoEditor from 'components/MonacoEditor/MonacoEditor';
|
||||
import Text from 'components/Text/Text';
|
||||
import styles from 'containers/IntegrationTemplate/IntegrationTemplate.module.scss';
|
||||
|
|
@ -70,6 +70,15 @@ const WebhooksTemplateEditor: React.FC<WebhooksTemplateEditorProps> = ({ templat
|
|||
setIsCheatSheetVisible(false);
|
||||
}, []);
|
||||
|
||||
const getCheatSheet = (templateKey: string) => {
|
||||
switch (templateKey) {
|
||||
case 'data':
|
||||
return webhookPayloadCheatSheet;
|
||||
default:
|
||||
return genericTemplateCheatSheet;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
|
|
@ -118,8 +127,8 @@ const WebhooksTemplateEditor: React.FC<WebhooksTemplateEditorProps> = ({ templat
|
|||
|
||||
{isCheatSheetVisible ? (
|
||||
<CheatSheet
|
||||
cheatSheetName="Generic"
|
||||
cheatSheetData={genericTemplateCheatSheet}
|
||||
cheatSheetName={template.displayName}
|
||||
cheatSheetData={getCheatSheet(template.name)}
|
||||
onClose={onCloseCheatSheet}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
import { AppFeature } from 'state/features';
|
||||
import { KeyValuePair } from 'utils';
|
||||
|
||||
import { BASE_INTEGRATION_TEMPLATES_LIST, BaseTemplateOptions } from './IntegrationCommon.config';
|
||||
|
||||
export const MsTeamsTemplateOptions = {
|
||||
MSTeams: new KeyValuePair('Microsoft Teams', 'Microsoft Teams'),
|
||||
MSTeamsTitle: new KeyValuePair('MSTeams Title', 'Title'),
|
||||
MSTeamsMessage: new KeyValuePair('MSTeams Message', 'Message'),
|
||||
MSTeamsImage: new KeyValuePair('MSTeams Image', 'Image'),
|
||||
};
|
||||
|
||||
export const getTemplateOptions = (features: Record<string, boolean>) => {
|
||||
if (features[AppFeature.MsTeams]) {
|
||||
return {
|
||||
...BaseTemplateOptions,
|
||||
...MsTeamsTemplateOptions,
|
||||
};
|
||||
}
|
||||
return BaseTemplateOptions;
|
||||
};
|
||||
|
||||
export const getIntegrationTemplatesList = (features: Record<string, boolean>) => {
|
||||
if (features[AppFeature.MsTeams]) {
|
||||
return [
|
||||
...BASE_INTEGRATION_TEMPLATES_LIST,
|
||||
|
||||
{
|
||||
label: MsTeamsTemplateOptions.MSTeams.value,
|
||||
value: MsTeamsTemplateOptions.MSTeams.key,
|
||||
children: [
|
||||
{
|
||||
label: MsTeamsTemplateOptions.MSTeamsTitle.value,
|
||||
value: MsTeamsTemplateOptions.MSTeamsTitle.key,
|
||||
},
|
||||
{
|
||||
label: MsTeamsTemplateOptions.MSTeamsMessage.value,
|
||||
value: MsTeamsTemplateOptions.MSTeamsMessage.key,
|
||||
},
|
||||
{
|
||||
label: MsTeamsTemplateOptions.MSTeamsImage.value,
|
||||
value: MsTeamsTemplateOptions.MSTeamsImage.key,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return BASE_INTEGRATION_TEMPLATES_LIST;
|
||||
};
|
||||
|
|
@ -6,7 +6,7 @@ export const MAX_CHARACTERS_COUNT = 50;
|
|||
export const MONACO_INPUT_HEIGHT_SMALL = '32px';
|
||||
export const MONACO_INPUT_HEIGHT_TALL = '120px';
|
||||
|
||||
export const BaseTemplateOptions = {
|
||||
export const IntegrationTemplateOptions = {
|
||||
WebTitle: new KeyValuePair('web_title_template', 'Web Title'),
|
||||
WebMessage: new KeyValuePair('web_message_template', 'Web Message'),
|
||||
WebImage: new KeyValuePair('web_image_url_template', 'Web Image'),
|
||||
|
|
@ -33,71 +33,7 @@ export const BaseTemplateOptions = {
|
|||
Telegram: new KeyValuePair('Telegram', 'Telegram'),
|
||||
};
|
||||
|
||||
export const BASE_INTEGRATION_TEMPLATES_LIST = [
|
||||
{
|
||||
label: BaseTemplateOptions.SourceLink.value,
|
||||
value: BaseTemplateOptions.SourceLink.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.Autoacknowledge.value,
|
||||
value: BaseTemplateOptions.Autoacknowledge.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.Phone.value,
|
||||
value: BaseTemplateOptions.Phone.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.SMS.value,
|
||||
value: BaseTemplateOptions.SMS.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.Email.value,
|
||||
value: BaseTemplateOptions.Email.key,
|
||||
children: [
|
||||
{
|
||||
label: BaseTemplateOptions.EmailTitle.value,
|
||||
value: BaseTemplateOptions.EmailTitle.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.EmailMessage.value,
|
||||
value: BaseTemplateOptions.EmailMessage.key,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.Slack.value,
|
||||
value: BaseTemplateOptions.Slack.key,
|
||||
children: [
|
||||
{
|
||||
label: BaseTemplateOptions.SlackTitle.value,
|
||||
value: BaseTemplateOptions.SlackTitle.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.SlackMessage.value,
|
||||
value: BaseTemplateOptions.SlackMessage.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.SlackImage.value,
|
||||
value: BaseTemplateOptions.SlackImage.key,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.Telegram.value,
|
||||
value: BaseTemplateOptions.Telegram.key,
|
||||
children: [
|
||||
{
|
||||
label: BaseTemplateOptions.TelegramTitle.value,
|
||||
value: BaseTemplateOptions.TelegramTitle.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.TelegramMessage.value,
|
||||
value: BaseTemplateOptions.TelegramMessage.key,
|
||||
},
|
||||
{
|
||||
label: BaseTemplateOptions.TelegramImage.value,
|
||||
value: BaseTemplateOptions.TelegramImage.key,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
export const LabelTemplateOptions = {
|
||||
AlertGroupDynamicLabel: new KeyValuePair('alert_group_dynamic_label', 'Alert Group Dynamic Label'),
|
||||
AlertGroupMultiLabel: new KeyValuePair('alert_group_multi_label', 'Alert Group Multi Label'),
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue