Alerting: Add MissingSeriesEvalsToResolve to the APIs (#102150)

What is this feature?

A follow-up for #101184, adds AlertRule.MissingSeriesEvalsToResolve to the APIs.

missing_series_evals_to_resolve must be specified too and it must be > 0.

POST /api/ruler/grafana/api/v1/rules/{folderUID} works in the following way:

    If missing_series_evals_to_resolve is not sent or null, the rule keeps its existing value
    If missing_series_evals_to_resolve > 0: updates to that value
    If missing_series_evals_to_resolve = 0: resets to default (nil).
    AlertRule.MissingSeriesEvalsToResolve can't be 0, so I used it to reset

In the Provisioning API, the value is just set if present and > 0. Otherwise it's reset:

PUT to /api/v1/provisioning/alert-rules/{UID}:

    If missing_series_evals_to_resolve is nil, it's reset to the default value
    If missing_series_evals_to_resolve > 0, it's updated
This commit is contained in:
Alexander Akhmetov
2025-03-26 13:34:53 +01:00
committed by GitHub
parent f2fc1d8575
commit f49a88ab72
17 changed files with 462 additions and 116 deletions
@@ -408,6 +408,42 @@ func TestProvisioningApi(t *testing.T) {
updated := deserializeRule(t, response.Body())
require.Equal(t, rule.UID, updated.UID)
})
t.Run("PUT without MissingSeriesEvalsToResolve clears the field", func(t *testing.T) {
oldValue := util.Pointer(5)
sut := createProvisioningSrvSut(t)
rc := createTestRequestCtx()
rule := createTestAlertRule("rule", 1)
rule.MissingSeriesEvalsToResolve = oldValue
insertRule(t, sut, rule)
rule.MissingSeriesEvalsToResolve = nil
response := sut.RoutePutAlertRule(&rc, rule, rule.UID)
require.Equal(t, 200, response.Status(), string(response.Body()))
require.NotEmpty(t, response.Body())
updated := deserializeRule(t, response.Body())
require.Equal(t, rule.UID, updated.UID)
require.Nil(t, updated.MissingSeriesEvalsToResolve)
})
t.Run("PUT with MissingSeriesEvalsToResolve updates the value", func(t *testing.T) {
oldValue := util.Pointer(5)
newValue := util.Pointer(10)
sut := createProvisioningSrvSut(t)
rc := createTestRequestCtx()
rule := createTestAlertRule("rule", 1)
rule.MissingSeriesEvalsToResolve = oldValue
insertRule(t, sut, rule)
rule.MissingSeriesEvalsToResolve = newValue
response := sut.RoutePutAlertRule(&rc, rule, rule.UID)
require.Equal(t, 200, response.Status(), string(response.Body()))
require.NotEmpty(t, response.Body())
updated := deserializeRule(t, response.Body())
require.Equal(t, rule.UID, updated.UID)
require.NotNil(t, updated.MissingSeriesEvalsToResolve)
require.Equal(t, *newValue, *updated.MissingSeriesEvalsToResolve)
})
})
t.Run("exist in non-default orgs", func(t *testing.T) {
@@ -657,6 +693,47 @@ func TestProvisioningApi(t *testing.T) {
require.Contains(t, string(response.Body()), "invalid alert rule")
})
})
t.Run("are valid", func(t *testing.T) {
t.Run("PUT returns 200", func(t *testing.T) {
sut := createProvisioningSrvSut(t)
rc := createTestRequestCtx()
group := createTestAlertRuleGroup(1)
response := sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
require.Equal(t, 200, response.Status())
require.NotEmpty(t, response.Body())
updated := deserializeRuleGroup(t, response.Body())
require.Equal(t, group.Title, updated.Title)
})
t.Run("PUT with MissingSeriesEvalsToResolve updates the value", func(t *testing.T) {
sut := createProvisioningSrvSut(t)
rc := createTestRequestCtx()
group := createTestAlertRuleGroup(1)
response := sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
updated := deserializeRuleGroup(t, response.Body())
require.Nil(t, updated.Rules[0].MissingSeriesEvalsToResolve)
// Put the same group with a new value
group.Rules[0].MissingSeriesEvalsToResolve = util.Pointer(5)
response = sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
require.Equal(t, 200, response.Status())
updated = deserializeRuleGroup(t, response.Body())
require.NotNil(t, updated.Rules[0].MissingSeriesEvalsToResolve)
require.Equal(t, 5, *updated.Rules[0].MissingSeriesEvalsToResolve)
// Reset the value again
group.Rules[0].MissingSeriesEvalsToResolve = nil
response = sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
require.Equal(t, 200, response.Status())
updated = deserializeRuleGroup(t, response.Body())
require.Nil(t, updated.Rules[0].MissingSeriesEvalsToResolve)
})
})
})
t.Run("exports", func(t *testing.T) {
@@ -2178,6 +2255,18 @@ func createInvalidAlertRuleGroup() definitions.AlertRuleGroup {
}
}
func createTestAlertRuleGroup(orgID int64) definitions.AlertRuleGroup {
return definitions.AlertRuleGroup{
Title: "test rule group",
Interval: 60,
Rules: []definitions.ProvisionedAlertRule{
createTestAlertRule("test-alert-rule", orgID),
createTestAlertRule("test-alert-rule-2", orgID),
createTestRecordingRule("test-recording-rule", orgID),
},
}
}
func createTestAlertRuleWithFolderAndGroup(title string, orgID int64, folderUid string, group string) definitions.ProvisionedAlertRule {
rule := createTestAlertRule(title, orgID)
rule.FolderUID = folderUid
@@ -2256,6 +2345,15 @@ func insertRuleInOrg(t *testing.T, srv ProvisioningSrv, rule definitions.Provisi
require.Equal(t, 201, resp.Status())
}
func deserializeRuleGroup(t *testing.T, data []byte) definitions.AlertRuleGroup {
t.Helper()
var rule definitions.AlertRuleGroup
err := json.Unmarshal(data, &rule)
require.NoError(t, err)
return rule
}
func deserializeRule(t *testing.T, data []byte) definitions.ProvisionedAlertRule {
t.Helper()