diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index 4cc0d4ab611..b70801e38d6 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -575,6 +575,7 @@ func toGettableExtendedRuleNode(r ngmodels.AlertRule, provenanceRecords map[stri IsPaused: r.IsPaused, NotificationSettings: AlertRuleNotificationSettingsFromNotificationSettings(r.NotificationSettings), Record: ApiRecordFromModelRecord(r.Record), + Metadata: AlertRuleMetadataFromModelMetadata(r.Metadata), }, } forDuration := model.Duration(r.For) diff --git a/pkg/services/ngalert/api/api_ruler_test.go b/pkg/services/ngalert/api/api_ruler_test.go index be5c677c0ff..7994712bbb7 100644 --- a/pkg/services/ngalert/api/api_ruler_test.go +++ b/pkg/services/ngalert/api/api_ruler_test.go @@ -334,7 +334,10 @@ func TestRouteGetRuleByUID(t *testing.T) { groupKey.NamespaceUID = folder.UID gen := models.RuleGen.With(models.RuleGen.WithGroupKey(groupKey)) - createdRules := gen.With(gen.WithUniqueGroupIndex(), gen.WithUniqueID()).GenerateManyRef(3) + createdRules := gen.With( + gen.WithUniqueGroupIndex(), gen.WithUniqueID(), + gen.WithEditorSettingsSimplifiedQueryAndExpressionsSection(true), + ).GenerateManyRef(3) require.Len(t, createdRules, 3) ruleStore.PutRule(context.Background(), createdRules...) @@ -352,6 +355,7 @@ func TestRouteGetRuleByUID(t *testing.T) { require.Equal(t, expectedRule.UID, result.GrafanaManagedAlert.UID) require.Equal(t, expectedRule.RuleGroup, result.GrafanaManagedAlert.RuleGroup) require.Equal(t, expectedRule.Title, result.GrafanaManagedAlert.Title) + require.True(t, result.GrafanaManagedAlert.Metadata.EditorSettings.SimplifiedQueryAndExpressionsSection) }) t.Run("error when fetching rule with non-existent UID", func(t *testing.T) { diff --git a/pkg/services/ngalert/api/api_ruler_validation.go b/pkg/services/ngalert/api/api_ruler_validation.go index bd5f64caff9..bd35d0e6375 100644 --- a/pkg/services/ngalert/api/api_ruler_validation.go +++ b/pkg/services/ngalert/api/api_ruler_validation.go @@ -144,6 +144,12 @@ func validateAlertingRuleFields(in *apimodels.PostableExtendedRuleNode, newRule } } + if in.GrafanaManagedAlert.Metadata != nil { + newRule.Metadata.EditorSettings = ngmodels.EditorSettings{ + SimplifiedQueryAndExpressionsSection: in.GrafanaManagedAlert.Metadata.EditorSettings.SimplifiedQueryAndExpressionsSection, + } + } + newRule.For, err = validateForInterval(in) if err != nil { return ngmodels.AlertRule{}, err diff --git a/pkg/services/ngalert/api/compat.go b/pkg/services/ngalert/api/compat.go index a0515c02b08..6cb91e7ad48 100644 --- a/pkg/services/ngalert/api/compat.go +++ b/pkg/services/ngalert/api/compat.go @@ -415,6 +415,20 @@ func MuteTimingIntervalToMuteTimeIntervalHclExport(m definitions.MuteTimeInterva return result, err } +// AlertRuleEditorSettingsFromEditorSettings converts models.EditorSettings to definitions.AlertRuleEditorSettings +func AlertRuleEditorSettingsFromModelEditorSettings(es models.EditorSettings) *definitions.AlertRuleEditorSettings { + return &definitions.AlertRuleEditorSettings{ + SimplifiedQueryAndExpressionsSection: es.SimplifiedQueryAndExpressionsSection, + } +} + +// AlertRuleMetadataFromMetadata converts models.AlertRuleMetadata to definitions.AlertRuleMetadata +func AlertRuleMetadataFromModelMetadata(es models.AlertRuleMetadata) *definitions.AlertRuleMetadata { + return &definitions.AlertRuleMetadata{ + EditorSettings: *AlertRuleEditorSettingsFromModelEditorSettings(es.EditorSettings), + } +} + // AlertRuleNotificationSettingsFromNotificationSettings converts []models.NotificationSettings to definitions.AlertRuleNotificationSettings func AlertRuleNotificationSettingsFromNotificationSettings(ns []models.NotificationSettings) *definitions.AlertRuleNotificationSettings { if len(ns) == 0 { diff --git a/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go b/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go index 433b86f8ee8..cf4c693ad69 100644 --- a/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go +++ b/pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go @@ -433,6 +433,16 @@ const ( ErrorErrState ExecutionErrorState = "Error" ) +// swagger:model +type AlertRuleMetadata struct { + EditorSettings AlertRuleEditorSettings `json:"editor_settings" yaml:"editor_settings"` +} + +// swagger:model +type AlertRuleEditorSettings struct { + SimplifiedQueryAndExpressionsSection bool `json:"simplified_query_and_expressions_section" yaml:"simplified_query_and_expressions_section"` +} + // swagger:model type AlertRuleNotificationSettings struct { // Name of the receiver to send notifications to. @@ -500,6 +510,7 @@ type PostableGrafanaRule struct { IsPaused *bool `json:"is_paused" yaml:"is_paused"` NotificationSettings *AlertRuleNotificationSettings `json:"notification_settings" yaml:"notification_settings"` Record *Record `json:"record" yaml:"record"` + Metadata *AlertRuleMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` } // swagger:model @@ -521,6 +532,7 @@ type GettableGrafanaRule struct { IsPaused bool `json:"is_paused" yaml:"is_paused"` NotificationSettings *AlertRuleNotificationSettings `json:"notification_settings,omitempty" yaml:"notification_settings,omitempty"` Record *Record `json:"record,omitempty" yaml:"record,omitempty"` + Metadata *AlertRuleMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` } // AlertQuery represents a single query associated with an alert definition. diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index 2cf020289ee..38be0c2dcd6 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -269,6 +269,15 @@ type AlertRule struct { Labels map[string]string IsPaused bool NotificationSettings []NotificationSettings + Metadata AlertRuleMetadata +} + +type AlertRuleMetadata struct { + EditorSettings EditorSettings `json:"editor_settings"` +} + +type EditorSettings struct { + SimplifiedQueryAndExpressionsSection bool `json:"simplified_query_and_expressions_section"` } // Namespaced describes a class of resources that are stored in a specific namespace. diff --git a/pkg/services/ngalert/models/testing.go b/pkg/services/ngalert/models/testing.go index 32763c06891..af53eca7e82 100644 --- a/pkg/services/ngalert/models/testing.go +++ b/pkg/services/ngalert/models/testing.go @@ -197,6 +197,12 @@ func (a *AlertRuleMutators) WithUniqueID() AlertRuleMutator { } } +func (a *AlertRuleMutators) WithEditorSettingsSimplifiedQueryAndExpressionsSection(mode bool) AlertRuleMutator { + return func(rule *AlertRule) { + rule.Metadata.EditorSettings.SimplifiedQueryAndExpressionsSection = mode + } +} + func (a *AlertRuleMutators) WithGroupIndex(groupIndex int) AlertRuleMutator { return func(rule *AlertRule) { rule.RuleGroupIndex = groupIndex diff --git a/pkg/services/ngalert/schedule/registry_test.go b/pkg/services/ngalert/schedule/registry_test.go index b485d9cc024..8c770dfb640 100644 --- a/pkg/services/ngalert/schedule/registry_test.go +++ b/pkg/services/ngalert/schedule/registry_test.go @@ -204,6 +204,11 @@ func TestRuleWithFolderFingerprint(t *testing.T) { NotificationSettings: []models.NotificationSettings{ models.NotificationSettingsGen()(), }, + Metadata: models.AlertRuleMetadata{ + EditorSettings: models.EditorSettings{ + SimplifiedQueryAndExpressionsSection: false, + }, + }, } r2 := &models.AlertRule{ ID: 2, @@ -243,6 +248,11 @@ func TestRuleWithFolderFingerprint(t *testing.T) { NotificationSettings: []models.NotificationSettings{ models.NotificationSettingsGen()(), }, + Metadata: models.AlertRuleMetadata{ + EditorSettings: models.EditorSettings{ + SimplifiedQueryAndExpressionsSection: true, + }, + }, } excludedFields := map[string]struct{}{ diff --git a/pkg/services/ngalert/store/compat.go b/pkg/services/ngalert/store/compat.go index a499309245c..7f255b8c7b0 100644 --- a/pkg/services/ngalert/store/compat.go +++ b/pkg/services/ngalert/store/compat.go @@ -80,6 +80,14 @@ func alertRuleToModelsAlertRule(ar alertRule, l log.Logger) (models.AlertRule, e } result.NotificationSettings = ns } + + if ar.Metadata != "" { + err = json.Unmarshal([]byte(ar.Metadata), &result.Metadata) + if err != nil { + return models.AlertRule{}, fmt.Errorf("failed to metadata: %w", err) + } + } + return result, nil } @@ -151,6 +159,12 @@ func alertRuleFromModelsAlertRule(ar models.AlertRule) (alertRule, error) { result.NotificationSettings = string(notificationSettingsData) } + metadata, err := json.Marshal(ar.Metadata) + if err != nil { + return alertRule{}, fmt.Errorf("failed to metadata: %w", err) + } + result.Metadata = string(metadata) + return result, nil } @@ -177,5 +191,6 @@ func alertRuleToAlertRuleVersion(rule alertRule) alertRuleVersion { Labels: rule.Labels, IsPaused: rule.IsPaused, NotificationSettings: rule.NotificationSettings, + Metadata: rule.Metadata, } } diff --git a/pkg/services/ngalert/store/models.go b/pkg/services/ngalert/store/models.go index 779bdba4edd..39ba210995c 100644 --- a/pkg/services/ngalert/store/models.go +++ b/pkg/services/ngalert/store/models.go @@ -26,6 +26,7 @@ type alertRule struct { Labels string IsPaused bool NotificationSettings string `xorm:"notification_settings"` + Metadata string `xorm:"metadata"` } func (a alertRule) TableName() string { @@ -59,6 +60,7 @@ type alertRuleVersion struct { Labels string IsPaused bool NotificationSettings string `xorm:"notification_settings"` + Metadata string `xorm:"metadata"` } func (a alertRuleVersion) TableName() string { diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index fb77908f9b0..216f2ad6f89 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -129,6 +129,8 @@ func (oss *OSSMigrations) AddMigration(mg *Migrator) { enableTraceQLStreaming(mg, oss.features != nil && oss.features.IsEnabledGlobally(featuremgmt.FlagTraceQLStreaming)) ualert.AddReceiverActionScopesMigration(mg) + + ualert.AddRuleMetadata(mg) } func addStarMigrations(mg *Migrator) { diff --git a/pkg/services/sqlstore/migrations/ualert/alert_rule_editor_settings.go b/pkg/services/sqlstore/migrations/ualert/alert_rule_editor_settings.go new file mode 100644 index 00000000000..6887a8a7c07 --- /dev/null +++ b/pkg/services/sqlstore/migrations/ualert/alert_rule_editor_settings.go @@ -0,0 +1,21 @@ +package ualert + +import "github.com/grafana/grafana/pkg/services/sqlstore/migrator" + +// AddRuleMetadata adds column to store alerting rule metadata, such as editor settings for the UI. +func AddRuleMetadata(mg *migrator.Migrator) { + column := &migrator.Column{ + Name: "metadata", + Type: migrator.DB_Text, + Nullable: true, + } + + mg.AddMigration( + "add metadata column to alert_rule table", + migrator.NewAddColumnMigration(migrator.Table{Name: "alert_rule"}, column), + ) + mg.AddMigration( + "add metadata column to alert_rule_version table", + migrator.NewAddColumnMigration(migrator.Table{Name: "alert_rule_version"}, column), + ) +} diff --git a/pkg/tests/api/alerting/api_alertmanager_test.go b/pkg/tests/api/alerting/api_alertmanager_test.go index 7d9c2b59c8c..9a943e1b1f8 100644 --- a/pkg/tests/api/alerting/api_alertmanager_test.go +++ b/pkg/tests/api/alerting/api_alertmanager_test.go @@ -790,7 +790,12 @@ func TestIntegrationDeleteFolderWithRules(t *testing.T) { "namespace_uid": %q, "rule_group": "arulegroup", "no_data_state": "NoData", - "exec_err_state": "Alerting" + "exec_err_state": "Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } } ] @@ -1268,7 +1273,12 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) { "namespace_uid":"nsuid", "rule_group":"arulegroup", "no_data_state":"NoData", - "exec_err_state":"Alerting" + "exec_err_state":"Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } }, { @@ -1304,7 +1314,12 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) { "namespace_uid":"nsuid", "rule_group":"arulegroup", "no_data_state":"Alerting", - "exec_err_state":"Alerting" + "exec_err_state":"Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } } ] @@ -1612,7 +1627,12 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) { "namespace_uid":"nsuid", "rule_group":"arulegroup", "no_data_state":"Alerting", - "exec_err_state":"Alerting" + "exec_err_state":"Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } } ] @@ -1721,8 +1741,13 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) { "namespace_uid":"nsuid", "rule_group":"arulegroup", "no_data_state":"Alerting", - "exec_err_state":"Alerting" - } + "exec_err_state":"Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } + } } ] } @@ -1809,8 +1834,13 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) { "namespace_uid":"nsuid", "rule_group":"arulegroup", "no_data_state":"Alerting", - "exec_err_state":"Alerting" - } + "exec_err_state":"Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } + } } ] } @@ -2336,8 +2366,13 @@ func TestIntegrationQuota(t *testing.T) { "namespace_uid":"nsuid", "rule_group":"arulegroup", "no_data_state":"NoData", - "exec_err_state":"Alerting" - } + "exec_err_state":"Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } + } } ] } diff --git a/pkg/tests/api/alerting/api_ruler_test.go b/pkg/tests/api/alerting/api_ruler_test.go index 01ec065002b..9301edeb1c4 100644 --- a/pkg/tests/api/alerting/api_ruler_test.go +++ b/pkg/tests/api/alerting/api_ruler_test.go @@ -798,6 +798,115 @@ func TestAlertRulePostExport(t *testing.T) { }) } +func TestIntegrationAlertRuleEditorSettings(t *testing.T) { + testinfra.SQLiteIntegrationTest(t) + + const folderName = "folder1" + const groupName = "test-group" + + // Setup Grafana and its Database + dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ + DisableLegacyAlerting: true, + EnableUnifiedAlerting: true, + EnableQuota: true, + DisableAnonymous: true, + ViewersCanEdit: true, + AppModeProduction: true, + }) + + grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path) + apiClient := newAlertingApiClient(grafanaListedAddr, "admin", "admin") + createUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{ + DefaultOrgRole: string(org.RoleAdmin), + Password: "admin", + Login: "admin", + }) + apiClient.CreateFolder(t, folderName, folderName) + + createAlertInGrafana := func(metadata *apimodels.AlertRuleMetadata) apimodels.GettableRuleGroupConfig { + interval, err := model.ParseDuration("1m") + require.NoError(t, err) + alertRule := apimodels.PostableExtendedRuleNode{ + ApiRuleNode: &apimodels.ApiRuleNode{ + For: &interval, + Labels: map[string]string{"label1": "val1"}, + Annotations: map[string]string{"annotation1": "val1"}, + }, + GrafanaManagedAlert: &apimodels.PostableGrafanaRule{ + Title: "AlwaysFiring", + Condition: "A", + Data: []apimodels.AlertQuery{ + { + RefID: "A", + RelativeTimeRange: apimodels.RelativeTimeRange{ + From: apimodels.Duration(time.Duration(5) * time.Hour), + To: apimodels.Duration(time.Duration(3) * time.Hour), + }, + DatasourceUID: expr.DatasourceUID, + Model: json.RawMessage(`{ + "type": "math", + "expression": "2 + 3 > 1" + }`), + }, + }, + Metadata: metadata, + }, + } + rules := apimodels.PostableRuleGroupConfig{ + Name: groupName, + Rules: []apimodels.PostableExtendedRuleNode{ + alertRule, + }, + } + + respModel, status, _ := apiClient.PostRulesGroupWithStatus(t, folderName, &rules) + assert.Equal(t, http.StatusAccepted, status) + require.Len(t, respModel.Created, 1) + + createdRuleGroup := apiClient.GetRulesGroup(t, folderName, rules.Name).GettableRuleGroupConfig + require.Len(t, createdRuleGroup.Rules, 1) + + expectedMetadata := alertRule.GrafanaManagedAlert.Metadata + if metadata == nil { + expectedMetadata = &apimodels.AlertRuleMetadata{ + EditorSettings: apimodels.AlertRuleEditorSettings{ + SimplifiedQueryAndExpressionsSection: false, + }, + } + } + require.Equal(t, expectedMetadata, createdRuleGroup.Rules[0].GrafanaManagedAlert.Metadata) + + return createdRuleGroup + } + + t.Run("set simplified query editor in editor settings", func(t *testing.T) { + metadata := &apimodels.AlertRuleMetadata{ + EditorSettings: apimodels.AlertRuleEditorSettings{ + SimplifiedQueryAndExpressionsSection: false, + }, + } + createdRuleGroup := createAlertInGrafana(metadata) + + rulesWithUID := convertGettableRuleGroupToPostable(createdRuleGroup) + rulesWithUID.Rules[0].GrafanaManagedAlert.Metadata.EditorSettings.SimplifiedQueryAndExpressionsSection = true + + _, status, _ := apiClient.PostRulesGroupWithStatus(t, folderName, &rulesWithUID) + assert.Equal(t, http.StatusAccepted, status) + + updatedRuleGroup := apiClient.GetRulesGroup(t, folderName, groupName).GettableRuleGroupConfig + require.Len(t, updatedRuleGroup.Rules, 1) + require.False(t, false, updatedRuleGroup.Rules[0].GrafanaManagedAlert.Metadata.EditorSettings.SimplifiedQueryAndExpressionsSection) + }) + + t.Run("post alert without metadata", func(t *testing.T) { + createAlertInGrafana(nil) + + createdRuleGroup := apiClient.GetRulesGroup(t, folderName, groupName).GettableRuleGroupConfig + require.Len(t, createdRuleGroup.Rules, 1) + require.False(t, false, createdRuleGroup.Rules[0].GrafanaManagedAlert.Metadata.EditorSettings.SimplifiedQueryAndExpressionsSection) + }) +} + func TestIntegrationAlertRuleConflictingTitle(t *testing.T) { testinfra.SQLiteIntegrationTest(t) @@ -1021,7 +1130,12 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) { "namespace_uid": "nsuid", "rule_group": "anotherrulegroup", "no_data_state": "NoData", - "exec_err_state": "Alerting" + "exec_err_state": "Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } }, { "expr": "", @@ -1054,7 +1168,12 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) { "namespace_uid": "nsuid", "rule_group": "anotherrulegroup", "no_data_state": "Alerting", - "exec_err_state": "Alerting" + "exec_err_state": "Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } }] }] @@ -1099,7 +1218,12 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) { "namespace_uid": "nsuid", "rule_group": "anotherrulegroup", "no_data_state": "NoData", - "exec_err_state": "Alerting" + "exec_err_state": "Alerting", + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } }] }] diff --git a/pkg/tests/api/alerting/test-data/rulegroup-1-get.json b/pkg/tests/api/alerting/test-data/rulegroup-1-get.json index 27474baf8e3..ab1231f3bd8 100644 --- a/pkg/tests/api/alerting/test-data/rulegroup-1-get.json +++ b/pkg/tests/api/alerting/test-data/rulegroup-1-get.json @@ -41,7 +41,12 @@ "rule_group": "Group1", "no_data_state": "NoData", "exec_err_state": "Alerting", - "is_paused": false + "is_paused": false, + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } }, { @@ -83,8 +88,13 @@ "rule_group": "Group1", "no_data_state": "NoData", "exec_err_state": "Alerting", - "is_paused": false + "is_paused": false, + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } } ] -} \ No newline at end of file +} diff --git a/pkg/tests/api/alerting/test-data/rulegroup-2-get.json b/pkg/tests/api/alerting/test-data/rulegroup-2-get.json index dc29a8b2bd2..397066bf202 100644 --- a/pkg/tests/api/alerting/test-data/rulegroup-2-get.json +++ b/pkg/tests/api/alerting/test-data/rulegroup-2-get.json @@ -41,8 +41,13 @@ "rule_group": "Group2", "no_data_state": "NoData", "exec_err_state": "Error", - "is_paused": false + "is_paused": false, + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } } ] -} \ No newline at end of file +} diff --git a/pkg/tests/api/alerting/test-data/rulegroup-3-get.json b/pkg/tests/api/alerting/test-data/rulegroup-3-get.json index 252dc99f13b..2956c506930 100644 --- a/pkg/tests/api/alerting/test-data/rulegroup-3-get.json +++ b/pkg/tests/api/alerting/test-data/rulegroup-3-get.json @@ -41,7 +41,12 @@ "rule_group": "Group3", "no_data_state": "NoData", "exec_err_state": "Alerting", - "is_paused": false + "is_paused": false, + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } }, { @@ -83,8 +88,13 @@ "rule_group": "Group3", "no_data_state": "NoData", "exec_err_state": "Alerting", - "is_paused": false + "is_paused": false, + "metadata": { + "editor_settings": { + "simplified_query_and_expressions_section": false + } + } } } ] -} \ No newline at end of file +} diff --git a/pkg/tests/api/alerting/testing.go b/pkg/tests/api/alerting/testing.go index bdad6d8d43f..ebbacc56005 100644 --- a/pkg/tests/api/alerting/testing.go +++ b/pkg/tests/api/alerting/testing.go @@ -233,6 +233,7 @@ func convertGettableGrafanaRuleToPostable(gettable *apimodels.GettableGrafanaRul ExecErrState: gettable.ExecErrState, IsPaused: &gettable.IsPaused, NotificationSettings: gettable.NotificationSettings, + Metadata: gettable.Metadata, } }