mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add example dropdown in the Edit notification template view (#94711)
Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
This commit is contained in:
@@ -1623,7 +1623,8 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"]
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
|
||||||
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"]
|
||||||
],
|
],
|
||||||
"public/app/features/alerting/unified/components/receivers/TemplatePreview.tsx:5381": [
|
"public/app/features/alerting/unified/components/receivers/TemplatePreview.tsx:5381": [
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export function PayloadEditor({
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Toggletip content={<AlertTemplateDataTable />} placement="top" fitContent>
|
<Toggletip content={<AlertTemplateDataTable />} placement="top" fitContent>
|
||||||
<Button variant="secondary" fill="outline" size="sm" icon="question-circle">
|
<Button variant="secondary" fill="outline" size="sm" icon="question-circle">
|
||||||
Help
|
Reference
|
||||||
</Button>
|
</Button>
|
||||||
</Toggletip>
|
</Toggletip>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function TemplateDataDocs() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTemplateDataDocsStyles = (theme: GrafanaTheme2) => ({
|
export const getTemplateDataDocsStyles = (theme: GrafanaTheme2) => ({
|
||||||
header: css({
|
header: css({
|
||||||
color: theme.colors.text.primary,
|
color: theme.colors.text.primary,
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ function KeyValueTemplateDataTable() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTemplateDataTableStyles = (theme: GrafanaTheme2) => ({
|
export const getTemplateDataTableStyles = (theme: GrafanaTheme2) => ({
|
||||||
table: css({
|
table: css({
|
||||||
borderCollapse: 'collapse',
|
borderCollapse: 'collapse',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
export interface TemplateExampleItem {
|
||||||
|
description: string;
|
||||||
|
example: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GlobalTemplateDataExamples: TemplateExampleItem[] = [
|
||||||
|
{
|
||||||
|
description: 'Default template for notification titles',
|
||||||
|
example: `{{- /* This is a copy of the "default.title" template. */ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "default.title.copy" }}
|
||||||
|
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}
|
||||||
|
{{ end }}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Default template for notification messages',
|
||||||
|
example: `{{- /* This is a copy of the "default.message" template. */ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "default.message.copy" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
|
||||||
|
{{ template "__text_alert_list.copy" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
|
||||||
|
|
||||||
|
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
|
||||||
|
{{ template "__text_alert_list.copy" .Alerts.Resolved }}{{ end }}{{ end }}
|
||||||
|
|
||||||
|
{{ define "__text_alert_list.copy" }}{{ range . }}
|
||||||
|
Value: {{ template "__text_values_list.copy" . }}
|
||||||
|
Labels:
|
||||||
|
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
|
||||||
|
{{ end }}Annotations:
|
||||||
|
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
|
||||||
|
{{ end }}{{ if gt (len .GeneratorURL) 0 }}Source: {{ .GeneratorURL }}
|
||||||
|
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: {{ .SilenceURL }}
|
||||||
|
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }}
|
||||||
|
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }}
|
||||||
|
{{ end }}{{ end }}{{ end }}
|
||||||
|
|
||||||
|
{{ define "__text_values_list.copy" }}{{ if len .Values }}{{ $first := true }}{{ range $refID, $value := .Values -}}
|
||||||
|
{{ if $first }}{{ $first = false }}{{ else }}, {{ end }}{{ $refID }}={{ $value }}{{ end -}}
|
||||||
|
{{ else }}[no value]{{ end }}{{ end }}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Print alerts with summary and description',
|
||||||
|
example: `{{- /* Example displaying the summary and description annotations of each alert in the notification. */ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "custom.alerts" -}}
|
||||||
|
{{ len .Alerts }} alert(s)
|
||||||
|
{{ range .Alerts -}}
|
||||||
|
{{ template "alert.summary_and_description" . -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{ define "alert.summary_and_description" }}
|
||||||
|
Summary: {{.Annotations.summary}}
|
||||||
|
Status: {{ .Status }}
|
||||||
|
Description: {{.Annotations.description}}
|
||||||
|
{{ end -}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Print firing and resolved alerts',
|
||||||
|
example: `{{- /* Example displaying firing and resolved alerts separately in the notification. */ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "custom.firing_and_resolved_alerts" -}}
|
||||||
|
{{ len .Alerts.Resolved }} resolved alert(s)
|
||||||
|
{{ range .Alerts.Resolved -}}
|
||||||
|
{{ template "alert.summary_and_description" . -}}
|
||||||
|
{{ end }}
|
||||||
|
{{ len .Alerts.Firing }} firing alert(s)
|
||||||
|
{{ range .Alerts.Firing -}}
|
||||||
|
{{ template "alert.summary_and_description" . -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{ define "alert.summary_and_description" }}
|
||||||
|
Summary: {{.Annotations.summary}}
|
||||||
|
Status: {{ .Status }}
|
||||||
|
Description: {{.Annotations.description}}
|
||||||
|
{{ end -}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Print common labels and annotations',
|
||||||
|
example: `{{- /* Example displaying labels and annotations that are common to all alerts in the notification.*/ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "custom.common_labels_and_annotations" -}}
|
||||||
|
{{ len .Alerts.Resolved }} resolved alert(s)
|
||||||
|
{{ len .Alerts.Firing }} firing alert(s)
|
||||||
|
|
||||||
|
Common labels: {{ len .CommonLabels.SortedPairs }}
|
||||||
|
{{ range .CommonLabels.SortedPairs -}}
|
||||||
|
- {{ .Name }} = {{ .Value }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
Common annotations: {{ len .CommonAnnotations.SortedPairs }}
|
||||||
|
{{ range .CommonAnnotations.SortedPairs }}
|
||||||
|
- {{ .Name }} = {{ .Value }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ end -}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Print individual labels and annotations',
|
||||||
|
example: `{{- /* Example displaying all labels and annotations for each alert in the notification.*/ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "custom.alert_labels_and_annotations" -}}
|
||||||
|
{{ len .Alerts.Resolved }} resolved alert(s)
|
||||||
|
{{ range .Alerts.Resolved -}}
|
||||||
|
{{ template "alert.labels_and_annotations" . -}}
|
||||||
|
{{ end }}
|
||||||
|
{{ len .Alerts.Firing }} firing alert(s)
|
||||||
|
{{ range .Alerts.Firing -}}
|
||||||
|
{{ template "alert.labels_and_annotations" . -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{ define "alert.labels_and_annotations" }}
|
||||||
|
Alert labels: {{ len .Labels.SortedPairs }}
|
||||||
|
{{ range .Labels.SortedPairs -}}
|
||||||
|
- {{ .Name }} = {{ .Value }}
|
||||||
|
{{ end -}}
|
||||||
|
Alert annotations: {{ len .Annotations.SortedPairs }}
|
||||||
|
{{ range .Annotations.SortedPairs -}}
|
||||||
|
- {{ .Name }} = {{ .Value }}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Print URLs for runbook and alert data in Grafana',
|
||||||
|
example: `{{- /* Example displaying additional information, such as runbook link, DashboardURL and SilenceURL, for each alert in the notification.*/ -}}
|
||||||
|
{{- /* Edit the template name and template content as needed. */ -}}
|
||||||
|
{{ define "custom.alert_additional_details" -}}
|
||||||
|
{{ len .Alerts.Resolved }} resolved alert(s)
|
||||||
|
{{ range .Alerts.Resolved -}}
|
||||||
|
{{ template "alert.additional_details" . -}}
|
||||||
|
{{ end }}
|
||||||
|
{{ len .Alerts.Firing }} firing alert(s)
|
||||||
|
{{ range .Alerts.Firing -}}
|
||||||
|
{{ template "alert.additional_details" . -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{ define "alert.additional_details" }}
|
||||||
|
- Dashboard: {{ .DashboardURL }}
|
||||||
|
- Panel: {{ .PanelURL }}
|
||||||
|
- AlertGenerator: {{ .GeneratorURL }}
|
||||||
|
- Silence: {{ .SilenceURL }}
|
||||||
|
- RunbookURL: {{ .Annotations.runbook_url}}
|
||||||
|
{{ end -}}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -11,9 +11,11 @@ import { isFetchError, locationService } from '@grafana/runtime';
|
|||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
|
Dropdown,
|
||||||
FieldSet,
|
FieldSet,
|
||||||
Input,
|
Input,
|
||||||
LinkButton,
|
LinkButton,
|
||||||
|
Menu,
|
||||||
useStyles2,
|
useStyles2,
|
||||||
Stack,
|
Stack,
|
||||||
useSplitter,
|
useSplitter,
|
||||||
@@ -43,6 +45,7 @@ import {
|
|||||||
|
|
||||||
import { PayloadEditor } from './PayloadEditor';
|
import { PayloadEditor } from './PayloadEditor';
|
||||||
import { TemplateDataDocs } from './TemplateDataDocs';
|
import { TemplateDataDocs } from './TemplateDataDocs';
|
||||||
|
import { GlobalTemplateDataExamples } from './TemplateDataExamples';
|
||||||
import { TemplateEditor } from './TemplateEditor';
|
import { TemplateEditor } from './TemplateEditor';
|
||||||
import { TemplatePreview } from './TemplatePreview';
|
import { TemplatePreview } from './TemplatePreview';
|
||||||
import { snippets } from './editor/templateDataSuggestions';
|
import { snippets } from './editor/templateDataSuggestions';
|
||||||
@@ -157,6 +160,12 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const appendExample = (example: string) => {
|
||||||
|
const content = getValues('content'),
|
||||||
|
newValue = !content ? example : `${content}\n${example}`;
|
||||||
|
setValue('content', newValue);
|
||||||
|
};
|
||||||
|
|
||||||
const actionButtons = (
|
const actionButtons = (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Button onClick={() => formRef.current?.requestSubmit()} variant="primary" size="sm" disabled={isSubmitting}>
|
<Button onClick={() => formRef.current?.requestSubmit()} variant="primary" size="sm" disabled={isSubmitting}>
|
||||||
@@ -223,20 +232,51 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
|
|||||||
<div {...columnSplitter.primaryProps}>
|
<div {...columnSplitter.primaryProps}>
|
||||||
{/* primaryProps will set "minHeight: min-content;" so we have to make sure to apply minHeight to the child */}
|
{/* primaryProps will set "minHeight: min-content;" so we have to make sure to apply minHeight to the child */}
|
||||||
<div className={cx(styles.flexColumn, styles.containerWithBorderAndRadius, styles.minEditorSize)}>
|
<div className={cx(styles.flexColumn, styles.containerWithBorderAndRadius, styles.minEditorSize)}>
|
||||||
<EditorColumnHeader
|
<div>
|
||||||
label="Template"
|
<EditorColumnHeader
|
||||||
actions={
|
label="Template"
|
||||||
<Button
|
actions={
|
||||||
icon="question-circle"
|
<>
|
||||||
size="sm"
|
{/* examples dropdown – only available for Grafana Alertmanager */}
|
||||||
fill="outline"
|
{isGrafanaAlertManager && (
|
||||||
variant="secondary"
|
<Dropdown
|
||||||
onClick={toggleCheatsheetOpened}
|
overlay={
|
||||||
>
|
<Menu>
|
||||||
Help
|
{GlobalTemplateDataExamples.map((item, index) => (
|
||||||
</Button>
|
<Menu.Item
|
||||||
}
|
key={index}
|
||||||
/>
|
label={item.description}
|
||||||
|
onClick={() => appendExample(item.example)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
label={'Examples documentation'}
|
||||||
|
url="https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/examples/"
|
||||||
|
target="_blank"
|
||||||
|
icon="external-link-alt"
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" size="sm" icon="angle-down">
|
||||||
|
Add example
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
icon="question-circle"
|
||||||
|
size="sm"
|
||||||
|
fill="outline"
|
||||||
|
variant="secondary"
|
||||||
|
onClick={toggleCheatsheetOpened}
|
||||||
|
>
|
||||||
|
Reference
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<AutoSizer>
|
<AutoSizer>
|
||||||
{({ width, height }) => (
|
{({ width, height }) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user