diff --git a/engine/apps/api/tests/test_alert_receive_channel_template.py b/engine/apps/api/tests/test_alert_receive_channel_template.py index f494d776..25c706d6 100644 --- a/engine/apps/api/tests/test_alert_receive_channel_template.py +++ b/engine/apps/api/tests/test_alert_receive_channel_template.py @@ -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 diff --git a/engine/common/api_helpers/mixins.py b/engine/common/api_helpers/mixins.py index 7bce174e..9b06f537 100644 --- a/engine/common/api_helpers/mixins.py +++ b/engine/common/api_helpers/mixins.py @@ -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 diff --git a/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts b/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts index 9b87e9af..41999de7 100644 --- a/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts +++ b/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts @@ -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.', diff --git a/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts b/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts index 478f70aa..b28380be 100644 --- a/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts +++ b/grafana-plugin/src/components/CheatSheet/CheatSheet.config.ts @@ -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' }], + }, + ], +}; diff --git a/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx b/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx index ec9024cd..73f54d1e 100644 --- a/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx +++ b/grafana-plugin/src/containers/IntegrationLabelsForm/IntegrationLabelsForm.tsx @@ -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) => { { { 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; } diff --git a/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx b/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx index d5fae6bd..a4ca5403 100644 --- a/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx @@ -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) }); }; }; diff --git a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css index e88192b9..3968c8d0 100644 --- a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css +++ b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css @@ -26,3 +26,7 @@ .display-linebreak { white-space: pre-line; } + +.extra-check { + margin-bottom: 10px; +} diff --git a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx index 2f0d4d78..a7508229 100644 --- a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx +++ b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx @@ -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({}); 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 ? ( + + ) : ( + + ); + default: + return null; + } + } + + const checkResult = getExtraCheckResult(); + + return checkResult ?
{checkResult}
: null; + } + function renderResult() { switch (templateType) { case 'html': { @@ -182,7 +208,14 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => { ); } - return result ? <>{renderResult()} : ; + return result ? ( + <> + {renderExtraChecks()} + {renderResult()} + + ) : ( + + ); }); export default TemplatePreview; diff --git a/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx b/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx index 093de6db..3ae6e637 100644 --- a/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx +++ b/grafana-plugin/src/containers/WebhooksTemplateEditor/WebhooksTemplateEditor.tsx @@ -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 = ({ templat setIsCheatSheetVisible(false); }, []); + const getCheatSheet = (templateKey: string) => { + switch (templateKey) { + case 'data': + return webhookPayloadCheatSheet; + default: + return genericTemplateCheatSheet; + } + }; + return ( = ({ templat {isCheatSheetVisible ? ( ) : ( diff --git a/grafana-plugin/src/pages/integration/Integration.config.ts b/grafana-plugin/src/pages/integration/Integration.config.ts deleted file mode 100644 index 62201a0f..00000000 --- a/grafana-plugin/src/pages/integration/Integration.config.ts +++ /dev/null @@ -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) => { - if (features[AppFeature.MsTeams]) { - return { - ...BaseTemplateOptions, - ...MsTeamsTemplateOptions, - }; - } - return BaseTemplateOptions; -}; - -export const getIntegrationTemplatesList = (features: Record) => { - 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; -}; diff --git a/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts b/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts index c4590765..cf7b1554 100644 --- a/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts +++ b/grafana-plugin/src/pages/integration/IntegrationCommon.config.ts @@ -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'), +};