Alerting: notification template group (#96447)

Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>
Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
pull/96826/head
Pepe Cano 1 year ago committed by GitHub
parent 451b226644
commit 706300e9b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      .betterer.results
  2. 2
      docs/sources/alerting/configure-notifications/template-notifications/_index.md
  3. 2
      docs/sources/alerting/configure-notifications/template-notifications/examples.md
  4. 37
      docs/sources/alerting/configure-notifications/template-notifications/manage-notification-templates.md
  5. 16
      docs/sources/alerting/set-up/provision-alerting-resources/export-alerting-resources/index.md
  6. 26
      docs/sources/alerting/set-up/provision-alerting-resources/file-provisioning/index.md
  7. 18
      docs/sources/alerting/set-up/provision-alerting-resources/terraform-provisioning/index.md
  8. 96
      docs/sources/shared/alerts/alerting_provisioning.md
  9. 12
      pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go
  10. 8
      public/app/features/alerting/unified/Templates.test.tsx
  11. 6
      public/app/features/alerting/unified/Templates.tsx
  12. 7
      public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx
  13. 2
      public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx
  14. 2
      public/app/features/alerting/unified/components/contact-points/EditContactPoint.test.tsx
  15. 6
      public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx
  16. 4
      public/app/features/alerting/unified/components/receivers/TemplateDataExamples.ts
  17. 81
      public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
  18. 14
      public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx
  19. 14
      public/app/features/alerting/unified/components/receivers/form/fields/TemplateSelector.tsx
  20. 9
      public/locales/en-US/grafana.json
  21. 9
      public/locales/pseudo-LOCALE/grafana.json

@ -1227,17 +1227,8 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"], [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"], [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"], [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"] [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"],
], [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "12"]
"public/app/features/alerting/unified/components/receivers/TemplateForm.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
[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 />", "5"],
[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"]

@ -99,7 +99,7 @@ The notification template is assigned to the contact point to determine the noti
{{< figure src="/media/docs/alerting/how-notification-templates-works.png" max-width="1200px" caption="A flow of the alert notification process, from querying the alert rule to sending the alert notification message." >}} {{< figure src="/media/docs/alerting/how-notification-templates-works.png" max-width="1200px" caption="A flow of the alert notification process, from querying the alert rule to sending the alert notification message." >}}
By default, Grafana provides default templates, such as `default.title` and `default.message`, to format notification messages. By default, Grafana provides default templates, such as `{{define "default.title"}}` and `{{define "default.message"}}`, to format notification messages.
## More information ## More information

@ -64,7 +64,7 @@ Notification templates allows you to change the default notification messages.
You can modify the content and format of notification messages. For example, you can customize the content to show only specific information or adjust the format to suit a particular contact point, such as Slack or Email. You can modify the content and format of notification messages. For example, you can customize the content to show only specific information or adjust the format to suit a particular contact point, such as Slack or Email.
{{% admonition type="note" %}} {{% admonition type="note" %}}
Avoid adding extra information about alert instances in notification templates, as this information is only be visible in the notification message. Avoid adding extra information about alert instances in notification templates, as this information is only visible in the notification message.
Instead, you should [use annotations or labels](ref:template-annotations-and-labels) to add information directly to the alert, ensuring it's also visible in the alert state and alert history within Grafana. You can then print the new alert annotation or label in notification templates. Instead, you should [use annotations or labels](ref:template-annotations-and-labels) to add information directly to the alert, ensuring it's also visible in the alert state and alert history within Grafana. You can then print the new alert annotation or label in notification templates.
{{% /admonition %}} {{% /admonition %}}

@ -34,11 +34,11 @@ refs:
In contact points, you can select notification templates to customize the notification messages sent. In contact points, you can select notification templates to customize the notification messages sent.
By default, Grafana provides a template for the notification title (`default.title`) and a template for the notification message (`default.message`). Both default templates display common alert details. By default, Grafana provides a template for the notification title (`{{define "default.title"}}`) and a template for the notification message (`{{define "default.message"}}`). Both default templates display common alert details.
You can also create custom templates to customize the content and format of notification messages, which can then be applied to one or more contact points. You can also create custom templates to customize the content and format of notification messages, which can then be applied to one or more contact points. In Grafana, a custom notification template is created within a notification template group.
This documentation provides step-by-step instructions for selecting templates in contact points, previewing templates, and creating custom templates using the Grafana UI. This documentation provides step-by-step instructions for selecting templates in contact points, previewing templates, and creating notification template groups using the Grafana UI.
## Select a notification template for a contact point ## Select a notification template for a contact point
@ -50,29 +50,28 @@ To add an existing notification template to your contact point, complete the fol
For example, if you are creating an email contact point integration, click **Message** or **Subject**. For example, if you are creating an email contact point integration, click **Message** or **Subject**.
1. Click **Edit**. 1. Click **Edit**.
A dialog box opens where you can select templates. A dialog box opens where you can select notification templates.
1. Click **Select existing template** to select a template and [preview](#preview-a-notification-template) it using the default payload. 1. Click **Select notification template** or **Enter custom message** to customize a template or message
You can also copy the selected template and use it in the custom tab. - You can select an existing notification template and [preview](#preview-a-notification-template) it using the default payload.
- You can also copy the notification template and use it in the **Enter custom message** tab.
1. Click **Enter custom message** to customize and edit the field directly. Note that the title changes depending on the field you are editing.
1. You can switch between the two tabs to access the list of available templates and copy them across to the customized version.
1. Click **Save contact point**. 1. Click **Save contact point**.
## Create a notification template ## Create a notification template and notification template group
Create notification templates to customize notification messages and reuse them in contact points. Create notification templates to customize notification messages and reuse them in contact points.
Your notification template name must be unique. You cannot have two templates with the same name in the same notification template or in different notification templates. Avoid defining templates with the same name as default templates, such as: `__subject`, `__text_values_list`, `__text_alert_list`, `default.title` and `default.message`. In Grafana, custom notification templates (`{{define "<NAME>"}}`) are created within a notification template group, allowing you to test and implement multiple templates together.
Your notification template name (`{{define "<NAME>"}}`) must be unique. You cannot have two templates with the same name in the same notification template group or in different notification template groups. Therefore, avoid using names already defined as default templates, such as: `__subject`, `__text_values_list`, `__text_alert_list`, `default.title` and `default.message`.
To create a notification template in Grafana, complete the following steps. To create a notification template in Grafana, complete the following steps.
1. Click **Alerts & IRM** -> **Contact points**. 1. Click **Alerts & IRM** -> **Contact points**.
1. Click the **Notification Templates** tab and then **+ Add notification template**. 1. Click the **Notification Templates** tab and then **+ Add notification template group**.
1. Enter a name for the notification template. 1. Enter a name for the notification template group.
1. Write the content of the template in the content field. 1. Write the content of the template in the content field.
@ -80,13 +79,13 @@ To create a notification template in Grafana, complete the following steps.
If `{{ define }}` is not included in the content, `{{ define "<NOTIFICATION_TEMPLATE_NAME>" }}` and `{{ end }}` is automatically added to the start and end. If `{{ define }}` is not included in the content, `{{ define "<NOTIFICATION_TEMPLATE_NAME>" }}` and `{{ end }}` is automatically added to the start and end.
To create a notification template that contains more than one template, complete the following steps. To create a notification template group that contains more than one notification template, complete the following steps.
1. Click **+ Add notification template**. 1. Click **+ Add notification template group**.
1. Enter a name for the notification template. 1. Enter a name for the notification template group.
1. Write each template in the Content field, including `{{ define "name-of-template" }}` and `{{ end }}` at the start and end of each template. 1. Write each template in the Content field, including `{{ define "<NOTIFICATION_TEMPLATE_NAME>" }}` and `{{ end }}` at the start and end of each template.
1. Save your changes. 1. Save your changes.
@ -103,7 +102,7 @@ Notification template preview is only for Grafana Alertmanager.
To preview your notification templates: To preview your notification templates:
1. Navigate to **Alerts&IRM** -> **Alerting** -> **Contact points** -> **Notification Templates**. 1. Navigate to **Alerts&IRM** -> **Alerting** -> **Contact points** -> **Notification Templates**.
1. Click **+ Add notification template** or edit an existing template. 1. Click **+ Add notification template group** or edit an existing template group.
1. Add or update your template content. 1. Add or update your template content.
Default data is provided and you can add or edit alert data to it as well as alert instances. You can add alert data directly in the Payload data window itself or click **Select alert instances** or **Add custom alerts**. Default data is provided and you can add or edit alert data to it as well as alert instances. You can add alert data directly in the Payload data window itself or click **Select alert instances** or **Add custom alerts**.

@ -69,9 +69,9 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/http-api-provisioning/#span-idroute-get-alert-rule-exportspan-export-an-alert-rule-in-provisioning-file-format-_routegetalertruleexport_ destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/http-api-provisioning/#span-idroute-get-alert-rule-exportspan-export-an-alert-rule-in-provisioning-file-format-_routegetalertruleexport_
alerting_http_templates: alerting_http_templates:
- pattern: /docs/grafana/ - pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/http-api-provisioning/#templates destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/http-api-provisioning/#notification-template-groups
- pattern: /docs/grafana-cloud/ - pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/http-api-provisioning/#templates destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/http-api-provisioning/#notification-template-groups
alerting_http_contactpoints: alerting_http_contactpoints:
- pattern: /docs/grafana/ - pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/http-api-provisioning/#contact-points destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/http-api-provisioning/#contact-points
@ -165,15 +165,15 @@ To export contact points from the Grafana UI, complete the following steps.
1. Click **Copy Code** or **Download**. 1. Click **Copy Code** or **Download**.
### Export templates ### Export notification template groups
Grafana currently doesn't offer an Export UI or [Export endpoint](#export-api-endpoints) for notification templates, unlike other Alerting resources presented in this documentation. Grafana currently doesn't offer an Export UI or [Export endpoint](#export-api-endpoints) for notification template groups, unlike other Alerting resources presented in this documentation.
However, you can export it by manually copying the content template and title directly from the Grafana UI. However, you can export it by manually copying the content and name of the notification template group from the Grafana UI.
1. Click **Alerts & IRM** -> **Contact points** -> **Notification templates** tab. 1. Click **Alerts & IRM** -> **Contact points** -> **Notification templates** tab.
1. Find the template you want to export. 1. Find the notification template group you want to export.
1. Copy the content and title. 1. Copy the content and name.
1. Adjust it for the [file provisioning format](ref:alerting_file_provisioning_template) or [Terraform resource](ref:alerting_tf_provisioning_template). 1. Adjust it for the [file provisioning format](ref:alerting_file_provisioning_template) or [Terraform resource](ref:alerting_tf_provisioning_template).
### Export the notification policy tree ### Export the notification policy tree
@ -217,8 +217,8 @@ You can use the [Alerting HTTP API](ref:alerting_http_provisioning) to return ex
| [Alert rules](ref:alerting_http_alertrules) | /api/v1/provisioning/alert-rules | | [Alert rules](ref:alerting_http_alertrules) | /api/v1/provisioning/alert-rules |
| [Contact points](ref:alerting_http_contactpoints) | /api/v1/provisioning/contact-points | | [Contact points](ref:alerting_http_contactpoints) | /api/v1/provisioning/contact-points |
| [Notification policy tree](ref:alerting_http_notificationpolicies) | /api/v1/provisioning/policies | | [Notification policy tree](ref:alerting_http_notificationpolicies) | /api/v1/provisioning/policies |
| [Notification template groups](ref:alerting_http_templates) | /api/v1/provisioning/templates |
| [Mute timings](ref:alerting_http_mutetimings) | /api/v1/provisioning/mute-timings | | [Mute timings](ref:alerting_http_mutetimings) | /api/v1/provisioning/mute-timings |
| [Templates](ref:alerting_http_templates) | /api/v1/provisioning/templates |
However, note the standard endpoints return a JSON format that is not compatible for provisioning through configuration files or Terraform, except the `/export` endpoints listed below. However, note the standard endpoints return a JSON format that is not compatible for provisioning through configuration files or Terraform, except the `/export` endpoints listed below.

@ -46,9 +46,9 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/ destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/
export_templates: export_templates:
- pattern: /docs/grafana/ - pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-templates destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-notification-template-groups
- pattern: /docs/grafana-cloud/ - pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-templates destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/export-alerting-resources/#export-notification-template-groups
--- ---
# Use configuration files to provision alerting resources # Use configuration files to provision alerting resources
@ -649,19 +649,19 @@ settings:
{{< /collapse >}} {{< /collapse >}}
## Import templates ## Import notification template groups
Create or delete templates using provisioning files in your Grafana instance(s). Create or delete notification template groups using provisioning files in your Grafana instance(s).
1. Find the notification template in Grafana. 1. Find the notification template group in Grafana.
1. [Export](ref:export_templates) a template by copying the template content and title. 1. [Export](ref:export_templates) a template group by copying the template content and name.
1. Copy the contents into a YAML or JSON configuration file and add it to the `provisioning/alerting` directory of the Grafana instance you want to import the alerting resources to. 1. Copy the contents into a YAML or JSON configuration file and add it to the `provisioning/alerting` directory of the Grafana instance you want to import the alerting resources to.
Example configuration files can be found below. Example configuration files can be found below.
1. Restart your Grafana instance (or reload the provisioned files using the Admin API). 1. Restart your Grafana instance (or reload the provisioned files using the Admin API).
Here is an example of a configuration file for creating templates. Here is an example of a configuration file for creating notification template groups.
```yaml ```yaml
# config file version # config file version
@ -671,16 +671,16 @@ apiVersion: 1
templates: templates:
# <int> organization ID, default = 1 # <int> organization ID, default = 1
- orgId: 1 - orgId: 1
# <string, required> name of the template, must be unique # <string, required> name of the template group, must be unique
name: my_first_template name: my_first_template
# <string, required> content of the template # <string, required> content of the template group
template: | template: |
{{ define "my_first_template" }} {{ define "my_first_template" }}
Custom notification message Custom notification message
{{ end }} {{ end }}
``` ```
Here is an example of a configuration file for deleting templates. Here is an example of a configuration file for deleting notification template groups.
```yaml ```yaml
# config file version # config file version
@ -690,7 +690,7 @@ apiVersion: 1
deleteTemplates: deleteTemplates:
# <int> organization ID, default = 1 # <int> organization ID, default = 1
- orgId: 1 - orgId: 1
# <string, required> name of the template, must be unique # <string, required> name of the template group, must be unique
name: my_first_template name: my_first_template
``` ```
@ -863,8 +863,8 @@ In alerting resources, most properties support template variable interpolation,
- Alert rule query model: `groups[].rules[].data.model` - Alert rule query model: `groups[].rules[].data.model`
- Mute timings name: `muteTimes[].name` - Mute timings name: `muteTimes[].name`
- Mute timings time intervals: `muteTimes[].time_intervals[]` - Mute timings time intervals: `muteTimes[].time_intervals[]`
- Notification template name: `templates[].name` - Notification template group name: `templates[].name`
- Notification template content: `templates[].template` - Notification template group content: `templates[].template`
Note for properties that support interpolation, you may unexpectedly substitute template variables when not intended. To avoid this, you can escape the `$variable` with `$$variable`. Note for properties that support interpolation, you may unexpectedly substitute template variables when not intended. To avoid this, you can escape the `$variable` with `$$variable`.

@ -272,19 +272,19 @@ In this section, we'll create Terraform configurations for each alerting resourc
1. Continue to add more Grafana resources or [use the Terraform CLI for provisioning](#provision-grafana-resources-with-terraform). 1. Continue to add more Grafana resources or [use the Terraform CLI for provisioning](#provision-grafana-resources-with-terraform).
### Add and enable templates ### Add and enable notification templates
[Notification templates](ref:notification-template) allow customization of alert notifications across multiple contact points. [Notification templates](ref:notification-template) allow customization of alert notifications across multiple contact points.
1. Create or find the notification template you want to import in Grafana. Alternatively, consider writing the resource in code as demonstrated in the example below. 1. Create or find the notification template group you want to import in Grafana. Alternatively, consider writing the resource in code as demonstrated in the example below.
1. [Export](ref:alerting_export) the template as [`grafana_message_template` Terraform resource](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/message_template). 1. [Export](ref:alerting_export) the notification template group as [`grafana_message_template` Terraform resource](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/message_template).
This example is a simple demo template defined as `custom_email.message`. This example creates a notification template group named `custom_emails` that defines a `custom_email.message` template.
```terraform ```terraform
resource "grafana_message_template" "<terraform_message_template_name>" { resource "grafana_message_template" "<terraform_message_template_name>" {
name = "custom_email.message" name = "custom_emails"
template = <<EOT template = <<EOT
{{ define "custom_email.message" }} {{ define "custom_email.message" }}
@ -294,6 +294,8 @@ In this section, we'll create Terraform configurations for each alerting resourc
} }
``` ```
This enables contact points to use the notification templates (`{{ define "<NAME>"}}`) within the notification template group.
1. In the previous contact point, enable the template by setting the `email.message` property as follows. 1. In the previous contact point, enable the template by setting the `email.message` property as follows.
```terraform ```terraform
@ -384,9 +386,9 @@ resource "grafana_contact_point" "my_contact_point" {
disable_provenance = true disable_provenance = true
} }
resource "grafana_message_template" "my_template" { resource "grafana_message_template" "custom_notification_template_group" {
name = "My Reusable Template" name = "custom_notification_template_group"
template = "{{define \"My Reusable Template\" }}\n template content\n{{ end }}" template = "{{define \"template1\" }}Say{{ end }}{{define \"template2\" }}Hi!{{ end }}"
disable_provenance = true disable_provenance = true
} }

@ -287,22 +287,21 @@ policies:
- weekends - weekends
``` ```
### Mute timings ### Notification template groups
| Method | URI | Name | Summary | Template groups enable you to define multiple notification templates (`{{ define "" }}`) within a single group. They can be managed from the Grafana Alerting UI.
| ------ | ---------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------- |
| DELETE | /api/v1/provisioning/mute-timings/:name | [route delete mute timing](#route-delete-mute-timing) | Delete a mute timing. |
| GET | /api/v1/provisioning/mute-timings/:name | [route get mute timing](#route-get-mute-timing) | Get a mute timing. |
| GET | /api/v1/provisioning/mute-timings | [route get mute timings](#route-get-mute-timings) | Get all the mute timings. |
| POST | /api/v1/provisioning/mute-timings | [route post mute timing](#route-post-mute-timing) | Create a new mute timing. |
| PUT | /api/v1/provisioning/mute-timings/:name | [route put mute timing](#route-put-mute-timing) | Replace an existing mute timing. |
| GET | /api/v1/provisioning/mute-timings/export | [route get mute timings export](#route-get-mute-timings-export) | Export all mute timings in provisioning file format. |
| GET | /api/v1/provisioning/mute-timings/:name/export | [route get mute timing export](#route-get-mute-timing-export) | Export a mute timing in provisioning file format. |
**Example Request for all mute timings:** | Method | URI | Name | Summary |
| ------ | ------------------------------------ | ----------------------------------------------- | ----------------------------------------------- |
| DELETE | /api/v1/provisioning/templates/:name | [route delete template](#route-delete-template) | Delete a notification template group. |
| GET | /api/v1/provisioning/templates/:name | [route get template](#route-get-template) | Get a notification template group. |
| GET | /api/v1/provisioning/templates | [route get template](#route-get-templates) | Get all notification template groups. |
| PUT | /api/v1/provisioning/templates/:name | [route put template](#route-put-template) | Create or update a notification template group. |
**Example Request for all notification template groups:**
```http ```http
GET /api/v1/provisioning/mute-timings GET /api/v1/provisioning/templates
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
@ -316,34 +315,34 @@ Content-Type: application/json
[ [
{ {
"name": "weekends", "name": "custom_email.message",
"time_intervals": [ "template": "{{ define \"custom_email.message\" }}\n Custom alert!\n{{ end }}",
{ "provenance": "file"
"weekdays": [ },
"saturday", {
"sunday" "name": "custom_email.subject",
] "template": "{{ define \"custom_email.subject\" }}\n{{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s)\n{{ end }}",
}
],
"version": "",
"provenance": "file" "provenance": "file"
} }
] ]
``` ```
### Templates ### Mute timings
| Method | URI | Name | Summary | | Method | URI | Name | Summary |
| ------ | ------------------------------------ | ----------------------------------------------- | ----------------------------------------- | | ------ | ---------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------- |
| DELETE | /api/v1/provisioning/templates/:name | [route delete template](#route-delete-template) | Delete a template. | | DELETE | /api/v1/provisioning/mute-timings/:name | [route delete mute timing](#route-delete-mute-timing) | Delete a mute timing. |
| GET | /api/v1/provisioning/templates/:name | [route get template](#route-get-template) | Get a notification template. | | GET | /api/v1/provisioning/mute-timings/:name | [route get mute timing](#route-get-mute-timing) | Get a mute timing. |
| GET | /api/v1/provisioning/templates | [route get templates](#route-get-templates) | Get all notification templates. | | GET | /api/v1/provisioning/mute-timings | [route get mute timings](#route-get-mute-timings) | Get all the mute timings. |
| PUT | /api/v1/provisioning/templates/:name | [route put template](#route-put-template) | Create or update a notification template. | | POST | /api/v1/provisioning/mute-timings | [route post mute timing](#route-post-mute-timing) | Create a new mute timing. |
| PUT | /api/v1/provisioning/mute-timings/:name | [route put mute timing](#route-put-mute-timing) | Replace an existing mute timing. |
| GET | /api/v1/provisioning/mute-timings/export | [route get mute timings export](#route-get-mute-timings-export) | Export all mute timings in provisioning file format. |
| GET | /api/v1/provisioning/mute-timings/:name/export | [route get mute timing export](#route-get-mute-timing-export) | Export a mute timing in provisioning file format. |
**Example Request for all notification templates:** **Example Request for all mute timings:**
```http ```http
GET /api/v1/provisioning/templates GET /api/v1/provisioning/mute-timings
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
@ -357,13 +356,16 @@ Content-Type: application/json
[ [
{ {
"name": "custom_email.message", "name": "weekends",
"template": "{{ define \"custom_email.message\" }}\n Custom alert!\n{{ end }}", "time_intervals": [
"provenance": "file" {
}, "weekdays": [
{ "saturday",
"name": "custom_email.subject", "sunday"
"template": "{{ define \"custom_email.subject\" }}\n{{ len .Alerts.Firing }} firing alert(s), {{ len .Alerts.Resolved }} resolved alert(s)\n{{ end }}", ]
}
],
"version": "",
"provenance": "file" "provenance": "file"
} }
] ]
@ -489,7 +491,7 @@ Status: Conflict
[GenericPublicError](#generic-public-error) [GenericPublicError](#generic-public-error)
### <span id="route-delete-template"></span> Delete a template. (_RouteDeleteTemplate_) ### <span id="route-delete-template"></span> Delete a notification template group. (_RouteDeleteTemplate_)
``` ```
DELETE /api/v1/provisioning/templates/:name DELETE /api/v1/provisioning/templates/:name
@ -499,7 +501,7 @@ DELETE /api/v1/provisioning/templates/:name
| Name | Source | Type | Go type | Separator | Required | Default | Description | | Name | Source | Type | Go type | Separator | Required | Default | Description |
| ------- | ------- | ------ | -------- | --------- | :------: | ------- | ------------------------------------------------------------------------------------------------------------- | | ------- | ------- | ------ | -------- | --------- | :------: | ------- | ------------------------------------------------------------------------------------------------------------- |
| name | `path` | string | `string` | | ✓ | | Template Name | | name | `path` | string | `string` | | ✓ | | Name of the template group |
| version | `query` | string | `string` | | | | Current version of the resource. Used for optimistic concurrency validation. Keep empty to bypass validation. | | version | `query` | string | `string` | | | | Current version of the resource. Used for optimistic concurrency validation. Keep empty to bypass validation. |
#### All responses #### All responses
@ -1047,7 +1049,7 @@ Status: Not Found
[NotFound](#not-found) [NotFound](#not-found)
### <span id="route-get-template"></span> Get a notification template. (_RouteGetTemplate_) ### <span id="route-get-template"></span> Get a notification template group. (_RouteGetTemplate_)
``` ```
GET /api/v1/provisioning/templates/:name GET /api/v1/provisioning/templates/:name
@ -1055,9 +1057,9 @@ GET /api/v1/provisioning/templates/:name
#### Parameters #### Parameters
| Name | Source | Type | Go type | Separator | Required | Default | Description | | Name | Source | Type | Go type | Separator | Required | Default | Description |
| ---- | ------ | ------ | -------- | --------- | :------: | ------- | ------------- | | ---- | ------ | ------ | -------- | --------- | :------: | ------- | -------------------------- |
| name | `path` | string | `string` | | ✓ | | Template Name | | name | `path` | string | `string` | | ✓ | | Name of the template group |
#### All responses #### All responses
@ -1082,7 +1084,7 @@ Status: OK
###### <span id="route-get-template-404-schema"></span> Schema ###### <span id="route-get-template-404-schema"></span> Schema
### <span id="route-get-templates"></span> Get all notification templates. (_RouteGetTemplates_) ### <span id="route-get-templates"></span> Get all notification template groups. (_RouteGetTemplates_)
``` ```
GET /api/v1/provisioning/templates GET /api/v1/provisioning/templates
@ -1456,7 +1458,7 @@ Status: Bad Request
[ValidationError](#validation-error) [ValidationError](#validation-error)
### <span id="route-put-template"></span> Create or update a notification template. (_RoutePutTemplate_) ### <span id="route-put-template"></span> Create or update a notification template group. (_RoutePutTemplate_)
``` ```
PUT /api/v1/provisioning/templates/:name PUT /api/v1/provisioning/templates/:name
@ -1468,7 +1470,7 @@ PUT /api/v1/provisioning/templates/:name
| Name | Source | Type | Go type | Separator | Required | Default | Description | | Name | Source | Type | Go type | Separator | Required | Default | Description |
| -------------------------- | -------- | ------------------------------------------------------------- | ------------------------------------ | --------- | :------: | ------- | --------------------------------------------------------- | | -------------------------- | -------- | ------------------------------------------------------------- | ------------------------------------ | --------- | :------: | ------- | --------------------------------------------------------- |
| name | `path` | string | `string` | | ✓ | | Template Name | | name | `path` | string | `string` | | ✓ | | Name of the template group |
| X-Disable-Provenance: true | `header` | string | `string` | | | | Allows editing of provisioned resources in the Grafana UI | | X-Disable-Provenance: true | `header` | string | `string` | | | | Allows editing of provisioned resources in the Grafana UI |
| Body | `body` | [NotificationTemplateContent](#notification-template-content) | `models.NotificationTemplateContent` | | | | | | Body | `body` | [NotificationTemplateContent](#notification-template-content) | `models.NotificationTemplateContent` | | | | |

@ -2,14 +2,14 @@ package definitions
// swagger:route GET /v1/provisioning/templates provisioning stable RouteGetTemplates // swagger:route GET /v1/provisioning/templates provisioning stable RouteGetTemplates
// //
// Get all notification templates. // Get all notification template groups.
// //
// Responses: // Responses:
// 200: NotificationTemplates // 200: NotificationTemplates
// swagger:route GET /v1/provisioning/templates/{name} provisioning stable RouteGetTemplate // swagger:route GET /v1/provisioning/templates/{name} provisioning stable RouteGetTemplate
// //
// Get a notification template. // Get a notification template group.
// //
// Responses: // Responses:
// 200: NotificationTemplate // 200: NotificationTemplate
@ -17,7 +17,7 @@ package definitions
// swagger:route PUT /v1/provisioning/templates/{name} provisioning stable RoutePutTemplate // swagger:route PUT /v1/provisioning/templates/{name} provisioning stable RoutePutTemplate
// //
// Updates an existing notification template. // Updates an existing notification template group.
// //
// Consumes: // Consumes:
// - application/json // - application/json
@ -29,7 +29,7 @@ package definitions
// swagger:route DELETE /v1/provisioning/templates/{name} provisioning stable RouteDeleteTemplate // swagger:route DELETE /v1/provisioning/templates/{name} provisioning stable RouteDeleteTemplate
// //
// Delete a template. // Delete a notification template group.
// //
// Responses: // Responses:
// 204: description: The template was deleted successfully. // 204: description: The template was deleted successfully.
@ -37,14 +37,14 @@ package definitions
// swagger:parameters RouteGetTemplate RoutePutTemplate RouteDeleteTemplate // swagger:parameters RouteGetTemplate RoutePutTemplate RouteDeleteTemplate
type RouteGetTemplateParam struct { type RouteGetTemplateParam struct {
// Template Name // Template group name
// in:path // in:path
Name string `json:"name"` Name string `json:"name"`
} }
// swagger:parameters stable RouteDeleteTemplate // swagger:parameters stable RouteDeleteTemplate
type RouteDeleteTemplateParam struct { type RouteDeleteTemplateParam struct {
// Template name // Template group name
// in:path // in:path
Name string `json:"name"` Name string `json:"name"`

@ -43,7 +43,7 @@ jest.mock('@grafana/ui', () => ({
const ui = { const ui = {
templateForm: byRole('form', { name: 'Template form' }), templateForm: byRole('form', { name: 'Template form' }),
form: { form: {
title: byLabelText(/Template name/), title: byLabelText(/Template group name/),
saveButton: byRole('button', { name: 'Save' }), saveButton: byRole('button', { name: 'Save' }),
}, },
}; };
@ -93,7 +93,7 @@ describe('Templates routes', () => {
const form = await ui.templateForm.find(); const form = await ui.templateForm.find();
expect(form).toBeInTheDocument(); expect(form).toBeInTheDocument();
expect(within(form).getByRole('textbox', { name: /Template name/ })).toHaveValue(''); expect(within(form).getByRole('textbox', { name: /Template group name/ })).toHaveValue('');
}); });
it('should pass name validation when editing existing template', async () => { it('should pass name validation when editing existing template', async () => {
@ -134,7 +134,7 @@ describe('Templates K8s API', () => {
const form = await ui.templateForm.find(); const form = await ui.templateForm.find();
expect(form).toBeInTheDocument(); expect(form).toBeInTheDocument();
expect(within(form).getByRole('textbox', { name: /Template name/ })).toHaveValue('custom-email'); expect(within(form).getByRole('textbox', { name: /Template group name/ })).toHaveValue('custom-email');
expect(within(form).getAllByTestId('code-editor')[0]).toHaveValue( expect(within(form).getAllByTestId('code-editor')[0]).toHaveValue(
'{{ define "custom-email" }} Custom email template {{ end }}' '{{ define "custom-email" }} Custom email template {{ end }}'
); );
@ -146,7 +146,7 @@ describe('Templates K8s API', () => {
const form = await ui.templateForm.find(); const form = await ui.templateForm.find();
expect(form).toBeInTheDocument(); expect(form).toBeInTheDocument();
expect(within(form).getByRole('textbox', { name: /Template name/ })).toHaveValue('custom-email (copy)'); expect(within(form).getByRole('textbox', { name: /Template group name/ })).toHaveValue('custom-email (copy)');
expect(within(form).getAllByTestId('code-editor')[0]).toHaveTextContent( expect(within(form).getAllByTestId('code-editor')[0]).toHaveTextContent(
'{{ define "custom-email_NEW" }} Custom email template {{ end }}' '{{ define "custom-email_NEW" }} Custom email template {{ end }}'
); );

@ -11,7 +11,11 @@ const NotificationTemplates = (): JSX.Element => (
<AlertmanagerPageWrapper <AlertmanagerPageWrapper
navId="receivers" navId="receivers"
accessType="notification" accessType="notification"
pageNav={{ id: 'templates', text: 'Notification templates', subTitle: 'Create and edit notification templates' }} pageNav={{
id: 'templates',
text: 'Notification templates',
subTitle: 'Create and edit a group of notification templates',
}}
> >
<Routes> <Routes>
<Route path=":name/edit" element={<EditMessageTemplate />} /> <Route path=":name/edit" element={<EditMessageTemplate />} />

@ -225,7 +225,10 @@ describe('contact points', () => {
// check buttons in Notification Templates // check buttons in Notification Templates
const notificationTemplatesTab = screen.getByRole('tab', { name: 'Notification Templates' }); const notificationTemplatesTab = screen.getByRole('tab', { name: 'Notification Templates' });
await user.click(notificationTemplatesTab); await user.click(notificationTemplatesTab);
expect(screen.getByRole('link', { name: 'Add notification template' })).toHaveAttribute('aria-disabled', 'true'); expect(screen.getByRole('link', { name: 'Add notification template group' })).toHaveAttribute(
'aria-disabled',
'true'
);
}); });
it('allows deleting when not disabled', async () => { it('allows deleting when not disabled', async () => {
@ -423,7 +426,7 @@ describe('contact points', () => {
// check buttons in Notification Templates // check buttons in Notification Templates
const notificationTemplatesTab = screen.getByRole('tab', { name: 'Notification Templates' }); const notificationTemplatesTab = screen.getByRole('tab', { name: 'Notification Templates' });
await user.click(notificationTemplatesTab); await user.click(notificationTemplatesTab);
expect(screen.queryByRole('link', { name: 'Add notification template' })).not.toBeInTheDocument(); expect(screen.queryByRole('link', { name: 'Add notification template group' })).not.toBeInTheDocument();
}); });
}); });

@ -155,7 +155,7 @@ const NotificationTemplatesTab = () => {
href="/alerting/notifications/templates/new" href="/alerting/notifications/templates/new"
disabled={!createTemplateAllowed} disabled={!createTemplateAllowed}
> >
Add notification template Add notification template group
</LinkButton> </LinkButton>
)} )}
</Stack> </Stack>

@ -59,7 +59,7 @@ describe('Edit contact point', () => {
// If this isn't correct, then we haven't set the correct initial state for the radio buttons/tabs // If this isn't correct, then we haven't set the correct initial state for the radio buttons/tabs
expect(await screen.findByLabelText(/custom template value/i)).toHaveValue('some custom value'); expect(await screen.findByLabelText(/custom template value/i)).toHaveValue('some custom value');
await user.click(screen.getByRole('radio', { name: /select existing template/i })); await user.click(screen.getByRole('radio', { name: /select notification template/i }));
await clickSelectOption(screen.getByTestId(templatesSelectorTestId), 'slack-template'); await clickSelectOption(screen.getByTestId(templatesSelectorTestId), 'slack-template');
expect(await getTemplatePreviewContent()).toHaveTextContent(/some example preview for slack-template/i); expect(await getTemplatePreviewContent()).toHaveTextContent(/some example preview for slack-template/i);

@ -31,7 +31,11 @@ export function TemplateDataDocs() {
return ( return (
<Stack gap={2}> <Stack gap={2}>
<TemplateDataTable <TemplateDataTable
caption={<h4 className={styles.header}>Template Data</h4>} caption={
<h4 className={styles.header}>
Notification template data <span>Available in the context of a notification.</span>{' '}
</h4>
}
dataItems={GlobalTemplateData} dataItems={GlobalTemplateData}
typeRenderer={(type) => typeRenderer={(type) =>
type === '[]Alert' ? ( type === '[]Alert' ? (

@ -5,7 +5,7 @@ export interface TemplateExampleItem {
export const GlobalTemplateDataExamples: TemplateExampleItem[] = [ export const GlobalTemplateDataExamples: TemplateExampleItem[] = [
{ {
description: 'Default template for notification titles', description: 'Default templates for notification titles',
example: `{{- /* This is a copy of the "default.title" template. */ -}} example: `{{- /* This is a copy of the "default.title" template. */ -}}
{{- /* Edit the template name and template content as needed. */ -}} {{- /* Edit the template name and template content as needed. */ -}}
{{ define "default.title.copy" }} {{ define "default.title.copy" }}
@ -13,7 +13,7 @@ export const GlobalTemplateDataExamples: TemplateExampleItem[] = [
{{ end }}`, {{ end }}`,
}, },
{ {
description: 'Default template for notification messages', description: 'Default templates for notification messages',
example: `{{- /* This is a copy of the "default.message" template. */ -}} example: `{{- /* This is a copy of the "default.message" template. */ -}}
{{- /* Edit the template name and template content as needed. */ -}} {{- /* Edit the template name and template content as needed. */ -}}
{{ define "default.message.copy" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing** {{ define "default.message.copy" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**

@ -20,11 +20,13 @@ import {
LinkButton, LinkButton,
Menu, Menu,
Stack, Stack,
Text,
useSplitter, useSplitter,
useStyles2, useStyles2,
} from '@grafana/ui'; } from '@grafana/ui';
import { useAppNotification } from 'app/core/copy/appNotification'; import { useAppNotification } from 'app/core/copy/appNotification';
import { useCleanup } from 'app/core/hooks/useCleanup'; import { useCleanup } from 'app/core/hooks/useCleanup';
import { t, Trans } from 'app/core/internationalization';
import { ActiveTab as ContactPointsActiveTabs } from 'app/features/alerting/unified/components/contact-points/ContactPoints'; import { ActiveTab as ContactPointsActiveTabs } from 'app/features/alerting/unified/components/contact-points/ContactPoints';
import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types'; import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types';
@ -169,7 +171,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
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}>
Save <Trans i18nKey="common.save">Save</Trans>
</Button> </Button>
<LinkButton <LinkButton
disabled={isSubmitting} disabled={isSubmitting}
@ -179,7 +181,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
variant="secondary" variant="secondary"
size="sm" size="sm"
> >
Cancel <Trans i18nKey="common.cancel">Cancel</Trans>
</LinkButton> </LinkButton>
</Stack> </Stack>
); );
@ -205,7 +207,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
{/* name field for the template */} {/* name field for the template */}
<FieldSet disabled={isProvisioned} className={styles.fieldset}> <FieldSet disabled={isProvisioned} className={styles.fieldset}>
<InlineField <InlineField
label="Template name" label="Template group name"
error={errors?.title?.message} error={errors?.title?.message}
invalid={!!errors.title?.message} invalid={!!errors.title?.message}
required required
@ -216,7 +218,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
required: { value: true, message: 'Required.' }, required: { value: true, message: 'Required.' },
validate: { titleIsUnique }, validate: { titleIsUnique },
})} })}
placeholder="Give your template a title" placeholder="Give your template group a name"
width={42} width={42}
autoFocus={true} autoFocus={true}
id="new-template-name" id="new-template-name"
@ -234,7 +236,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
<div className={cx(styles.flexColumn, styles.containerWithBorderAndRadius, styles.minEditorSize)}> <div className={cx(styles.flexColumn, styles.containerWithBorderAndRadius, styles.minEditorSize)}>
<div> <div>
<EditorColumnHeader <EditorColumnHeader
label="Template" label="Template group"
actions={ actions={
<> <>
{/* examples dropdown – only available for Grafana Alertmanager */} {/* examples dropdown – only available for Grafana Alertmanager */}
@ -260,7 +262,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
} }
> >
<Button variant="secondary" size="sm" icon="angle-down"> <Button variant="secondary" size="sm" icon="angle-down">
Add example <Trans i18nKey="alerting.templates.editor.add-example">Add example</Trans>
</Button> </Button>
</Dropdown> </Dropdown>
)} )}
@ -271,7 +273,7 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
variant="secondary" variant="secondary"
onClick={toggleCheatsheetOpened} onClick={toggleCheatsheetOpened}
> >
Reference <Trans i18nKey="common.help">Help</Trans>
</Button> </Button>
</> </>
} }
@ -349,34 +351,43 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
function TemplatingBasics() { function TemplatingBasics() {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const intro = t(
'alerting.templates.help.intro',
`Notification templates use Go templating language to create notification messages.
In Grafana, a template group can define multiple notification templates using {{ define "<NAME>" }}.
These templates can then be used in contact points and within other notification templates by calling {{ template "<NAME>" }}.
For detailed information about notification templates, refer to our documentation.`
);
return ( return (
<Alert title="How to" severity="info"> <Alert title="" severity="info">
<Stack direction="row"> <Stack direction="column" gap={2}>
<div> <Stack direction="row">
Grafana uses Go templating language to create notification messages. <div style={{ whiteSpace: 'pre' }}>{intro}</div>
<br /> <div>
To find out more about templating please visit our documentation. <LinkButton
</div> href="https://grafana.com/docs/grafana/latest/alerting/manage-notifications/template-notifications/"
<div> target="_blank"
<LinkButton icon="external-link-alt"
href="https://grafana.com/docs/grafana/latest/alerting/manage-notifications/template-notifications/" variant="secondary"
target="_blank" >
icon="external-link-alt" <Trans i18nKey="alerting.templates.editor.goto-docs">Notification templates documentation</Trans>
variant="secondary" </LinkButton>
> </div>
Templating documentation </Stack>
</LinkButton>
</div> <Text variant="bodySmall">
<Trans i18nKey="alerting.templates.editor.auto-complete">
For auto-completion of common templating code, type the following keywords in the content editor:
</Trans>
<div className={styles.code}>
{Object.values(snippets)
.map((s) => s.label)
.join(', ')}
</div>
</Text>
</Stack> </Stack>
<div className={styles.snippets}>
For auto-completion of common templating code, type the following keywords in the content editor:
<div className={styles.code}>
{Object.values(snippets)
.map((s) => s.label)
.join(', ')}
</div>
</div>
</Alert> </Alert>
); );
} }
@ -460,10 +471,6 @@ export const getStyles = (theme: GrafanaTheme2) => {
display: 'none', display: 'none',
}, },
}), }),
snippets: css({
marginTop: theme.spacing(2),
fontSize: theme.typography.bodySmall.fontSize,
}),
code: css({ code: css({
color: theme.colors.text.secondary, color: theme.colors.text.secondary,
fontWeight: theme.typography.fontWeightBold, fontWeight: theme.typography.fontWeightBold,

@ -62,7 +62,7 @@ export const TemplatesTable = ({ alertManagerName, templates }: Props) => {
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th>Template</th> <th>Template group</th>
<Authorize <Authorize
actions={[ actions={[
AlertmanagerAction.CreateNotificationTemplate, AlertmanagerAction.CreateNotificationTemplate,
@ -95,8 +95,8 @@ export const TemplatesTable = ({ alertManagerName, templates }: Props) => {
{!!templateToDelete && ( {!!templateToDelete && (
<ConfirmModal <ConfirmModal
isOpen={true} isOpen={true}
title="Delete template" title="Delete template group"
body={`Are you sure you want to delete template "${templateToDelete.title}"?`} body={`Are you sure you want to delete template group "${templateToDelete.title}"?`}
confirmText="Yes, delete" confirmText="Yes, delete"
onConfirm={onDeleteTemplate} onConfirm={onDeleteTemplate}
onDismiss={() => setTemplateToDelete(undefined)} onDismiss={() => setTemplateToDelete(undefined)}
@ -161,7 +161,7 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
<Authorize actions={[AlertmanagerAction.UpdateNotificationTemplate]}> <Authorize actions={[AlertmanagerAction.UpdateNotificationTemplate]}>
<ActionIcon <ActionIcon
to={makeAMLink(`/alerting/notifications/templates/${encodeURIComponent(uid)}/edit`, alertManagerName)} to={makeAMLink(`/alerting/notifications/templates/${encodeURIComponent(uid)}/edit`, alertManagerName)}
tooltip="edit template" tooltip="Edit template group"
icon="pen" icon="pen"
/> />
</Authorize> </Authorize>
@ -172,7 +172,7 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
`/alerting/notifications/templates/${encodeURIComponent(uid)}/duplicate`, `/alerting/notifications/templates/${encodeURIComponent(uid)}/duplicate`,
alertManagerName alertManagerName
)} )}
tooltip="Copy template" tooltip="Copy template group"
icon="copy" icon="copy"
/> />
</Authorize> </Authorize>
@ -180,7 +180,7 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
<Authorize actions={[AlertmanagerAction.DeleteNotificationTemplate]}> <Authorize actions={[AlertmanagerAction.DeleteNotificationTemplate]}>
<ActionIcon <ActionIcon
onClick={() => onDeleteClick(notificationTemplate)} onClick={() => onDeleteClick(notificationTemplate)}
tooltip="delete template" tooltip="Delete template group"
icon="trash-alt" icon="trash-alt"
/> />
</Authorize> </Authorize>
@ -191,7 +191,7 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
<tr className={idx % 2 === 0 ? tableStyles.evenRow : undefined}> <tr className={idx % 2 === 0 ? tableStyles.evenRow : undefined}>
<td></td> <td></td>
<td colSpan={2}> <td colSpan={2}>
<DetailsField label="Description" horizontal={true}> <DetailsField label="" horizontal={true}>
<TemplateEditor <TemplateEditor
width={'auto'} width={'auto'}
height={'auto'} height={'auto'}

@ -54,7 +54,7 @@ export function TemplatesPicker({ onSelect, option, valueInForm }: TemplatesPick
<> <>
<Button <Button
icon="edit" icon="edit"
tooltip={`Edit ${option.label.toLowerCase()} using existing templates.`} tooltip={`Edit ${option.label.toLowerCase()} using existing notification templates.`}
onClick={onClick} onClick={onClick}
variant="secondary" variant="secondary"
size="sm" size="sm"
@ -129,10 +129,10 @@ function TemplateSelector({ onSelect, onClose, option, valueInForm }: TemplateSe
const templateOptions: Array<SelectableValue<TemplateFieldOption>> = [ const templateOptions: Array<SelectableValue<TemplateFieldOption>> = [
{ {
label: 'Select existing template', label: 'Select notification template',
ariaLabel: 'Select existing template', ariaLabel: 'Select notification template',
value: 'Existing', value: 'Existing',
description: `Select a single template and preview it, or copy it to paste it in the custom tab. ${templateOption === 'Existing' ? 'Clicking Save will save your changes to the selected template.' : ''}`, description: `Select an existing notification template and preview it, or copy it to paste it in the custom tab. ${templateOption === 'Existing' ? 'Clicking Save saves your changes to the selected template.' : ''}`,
}, },
{ {
label: `Enter custom ${option.label.toLowerCase()}`, label: `Enter custom ${option.label.toLowerCase()}`,
@ -195,8 +195,8 @@ function TemplateSelector({ onSelect, onClose, option, valueInForm }: TemplateSe
<Stack direction="row" gap={1} alignItems="center"> <Stack direction="row" gap={1} alignItems="center">
<Select<Template> <Select<Template>
data-testid="existing-templates-selector" data-testid="existing-templates-selector"
placeholder="Choose template" placeholder="Choose notification template"
aria-label="Choose template" aria-label="Choose notification template"
onChange={(value: SelectableValue<Template>, _) => { onChange={(value: SelectableValue<Template>, _) => {
setTemplate(value); setTemplate(value);
}} }}
@ -205,7 +205,7 @@ function TemplateSelector({ onSelect, onClose, option, valueInForm }: TemplateSe
defaultValue={defaultTemplateValue} defaultValue={defaultTemplateValue}
/> />
<IconButton <IconButton
tooltip="Copy selected template to clipboard. You can use it in the custom tab." tooltip="Copy selected notification template to clipboard. You can use it in the custom tab."
onClick={() => onClick={() =>
copyToClipboard(getUseTemplateText(template?.value?.name ?? defaultTemplateValue?.value?.name ?? '')) copyToClipboard(getUseTemplateText(template?.value?.name ?? defaultTemplateValue?.value?.name ?? ''))
} }

@ -490,6 +490,14 @@
"alertCondition": "Alert condition" "alertCondition": "Alert condition"
}, },
"templates": { "templates": {
"editor": {
"add-example": "Add example",
"auto-complete": "For auto-completion of common templating code, type the following keywords in the content editor:",
"goto-docs": "Notification templates documentation"
},
"help": {
"intro": "Notification templates use Go templating language to create notification messages.\n\nIn Grafana, a template group can define multiple notification templates using {{ define \"<NAME>\" }}.\nThese templates can then be used in contact points and within other notification templates by calling {{ template \"<NAME>\" }}.\nFor detailed information about notification templates, refer to our documentation."
},
"misconfigured-badge-text": "Misconfigured", "misconfigured-badge-text": "Misconfigured",
"misconfigured-warning": "This template is misconfigured.", "misconfigured-warning": "This template is misconfigured.",
"misconfigured-warning-details": "Templates must be defined in both the <1></1> and <4></4> sections of your alertmanager configuration." "misconfigured-warning-details": "Templates must be defined in both the <1></1> and <4></4> sections of your alertmanager configuration."
@ -665,6 +673,7 @@
"close": "Close", "close": "Close",
"collapse": "Collapse", "collapse": "Collapse",
"edit": "Edit", "edit": "Edit",
"help": "Help",
"locale": { "locale": {
"default": "Default" "default": "Default"
}, },

@ -490,6 +490,14 @@
"alertCondition": "Åľęřŧ čőʼnđįŧįőʼn" "alertCondition": "Åľęřŧ čőʼnđįŧįőʼn"
}, },
"templates": { "templates": {
"editor": {
"add-example": "Åđđ ęχämpľę",
"auto-complete": "Főř äūŧő-čőmpľęŧįőʼn őƒ čőmmőʼn ŧęmpľäŧįʼnģ čőđę, ŧypę ŧĥę ƒőľľőŵįʼnģ ĸęyŵőřđş įʼn ŧĥę čőʼnŧęʼnŧ ęđįŧőř:",
"goto-docs": "Ńőŧįƒįčäŧįőʼn ŧęmpľäŧęş đőčūmęʼnŧäŧįőʼn"
},
"help": {
"intro": "Ńőŧįƒįčäŧįőʼn ŧęmpľäŧęş ūşę Ğő ŧęmpľäŧįʼnģ ľäʼnģūäģę ŧő čřęäŧę ʼnőŧįƒįčäŧįőʼn męşşäģęş.\n\nĨʼn Ğřäƒäʼnä, ä ŧęmpľäŧę ģřőūp čäʼn đęƒįʼnę mūľŧįpľę ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş ūşįʼnģ {{ define \"<NAME>\" }}.\nŦĥęşę ŧęmpľäŧęş čäʼn ŧĥęʼn þę ūşęđ įʼn čőʼnŧäčŧ pőįʼnŧş äʼnđ ŵįŧĥįʼn őŧĥęř ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş þy čäľľįʼnģ {{ template \"<NAME>\" }}.\nFőř đęŧäįľęđ įʼnƒőřmäŧįőʼn äþőūŧ ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş, řęƒęř ŧő őūř đőčūmęʼnŧäŧįőʼn."
},
"misconfigured-badge-text": "Mįşčőʼnƒįģūřęđ", "misconfigured-badge-text": "Mįşčőʼnƒįģūřęđ",
"misconfigured-warning": "Ŧĥįş ŧęmpľäŧę įş mįşčőʼnƒįģūřęđ.", "misconfigured-warning": "Ŧĥįş ŧęmpľäŧę įş mįşčőʼnƒįģūřęđ.",
"misconfigured-warning-details": "Ŧęmpľäŧęş mūşŧ þę đęƒįʼnęđ įʼn þőŧĥ ŧĥę <1></1> äʼnđ <4></4> şęčŧįőʼnş őƒ yőūř äľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼn." "misconfigured-warning-details": "Ŧęmpľäŧęş mūşŧ þę đęƒįʼnęđ įʼn þőŧĥ ŧĥę <1></1> äʼnđ <4></4> şęčŧįőʼnş őƒ yőūř äľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼn."
@ -665,6 +673,7 @@
"close": "Cľőşę", "close": "Cľőşę",
"collapse": "Cőľľäpşę", "collapse": "Cőľľäpşę",
"edit": "Ēđįŧ", "edit": "Ēđįŧ",
"help": "Ħęľp",
"locale": { "locale": {
"default": "Đęƒäūľŧ" "default": "Đęƒäūľŧ"
}, },

Loading…
Cancel
Save