Alerting: Rule version history API (#99041)

* implement store method to read rule versions

* implement request handler

* declare a new endpoint

* fix fake to return correct response

* add tests

* add integration tests

* rename history to versions

* apply diff from swagger CI step

Signed-off-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>

---------

Signed-off-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
This commit is contained in:
Yuri Tseretyan
2025-02-03 13:26:18 -05:00
committed by GitHub
parent 8a259ecafa
commit ac41c19350
20 changed files with 772 additions and 16 deletions
+103
View File
@@ -448,6 +448,109 @@ func TestRouteGetRuleByUID(t *testing.T) {
})
}
func TestRouteGetRuleHistoryByUID(t *testing.T) {
orgID := rand.Int63()
f := randFolder()
groupKey := models.GenerateGroupKey(orgID)
groupKey.NamespaceUID = f.UID
gen := models.RuleGen.With(models.RuleGen.WithGroupKey(groupKey), models.RuleGen.WithUniqueID())
t.Run("rule history is successfully fetched with the correct UID", func(t *testing.T) {
ruleStore := fakes.NewRuleStore(t)
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f)
rule := gen.GenerateRef()
history := gen.With(gen.WithUID(rule.UID)).GenerateManyRef(3)
// simulate order of the history
rule.ID = 100
for i, alertRule := range history {
alertRule.ID = rule.ID - int64(i) - 1
}
ruleStore.PutRule(context.Background(), rule)
ruleStore.History[rule.GetKey()] = append(ruleStore.History[rule.GetKey()], history...)
perms := createPermissionsForRules([]*models.AlertRule{rule}, orgID)
req := createRequestContextWithPerms(orgID, perms, nil)
svc := createService(ruleStore)
response := svc.RouteGetRuleVersionsByUID(req, rule.UID)
require.Equal(t, http.StatusOK, response.Status())
var result apimodels.GettableRuleVersions
require.NoError(t, json.Unmarshal(response.Body(), &result))
require.NotNil(t, result)
require.Len(t, result, len(history)+1) // history + current version
t.Run("should be in correct order", func(t *testing.T) {
expectedHistory := append([]*models.AlertRule{rule}, history...)
for i, rul := range expectedHistory {
assert.Equal(t, rul.UID, result[i].GrafanaManagedAlert.UID)
}
})
})
t.Run("NotFound when rule does not exist", func(t *testing.T) {
ruleStore := fakes.NewRuleStore(t)
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f)
ruleKey := models.AlertRuleKey{
OrgID: orgID,
UID: "test",
}
history := gen.With(gen.WithKey(ruleKey)).GenerateManyRef(3)
ruleStore.History[ruleKey] = append(ruleStore.History[ruleKey], history...) // even if history is full of records
perms := createPermissionsForRules(history, orgID)
req := createRequestContextWithPerms(orgID, perms, nil)
response := createService(ruleStore).RouteGetRuleVersionsByUID(req, ruleKey.UID)
require.Equal(t, http.StatusNotFound, response.Status())
})
t.Run("Empty result when rule history is empty", func(t *testing.T) {
ruleStore := fakes.NewRuleStore(t)
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f)
ruleKey := models.AlertRuleKey{
OrgID: orgID,
UID: "test",
}
rule := gen.With(gen.WithKey(ruleKey)).GenerateRef()
ruleStore.PutRule(context.Background(), rule)
ruleStore.History[ruleKey] = nil
perms := createPermissionsForRules([]*models.AlertRule{rule}, orgID)
req := createRequestContextWithPerms(orgID, perms, nil)
response := createService(ruleStore).RouteGetRuleVersionsByUID(req, ruleKey.UID)
require.Equal(t, http.StatusOK, response.Status())
var result apimodels.GettableRuleVersions
require.NoError(t, json.Unmarshal(response.Body(), &result))
require.Empty(t, result)
})
t.Run("Unauthorized if user does not have access to the current rule", func(t *testing.T) {
ruleStore := fakes.NewRuleStore(t)
anotherFolder := randFolder()
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], f, anotherFolder)
ruleKey := models.AlertRuleKey{
OrgID: orgID,
UID: "test",
}
rule := gen.With(gen.WithKey(ruleKey), gen.WithNamespaceUID(anotherFolder.UID)).GenerateRef()
ruleStore.PutRule(context.Background(), rule)
history := gen.With(gen.WithKey(ruleKey)).GenerateManyRef(3)
ruleStore.History[ruleKey] = history
perms := createPermissionsForRules(history, orgID) // grant permissions to all records in history but not the rule itself
req := createRequestContextWithPerms(orgID, perms, nil)
response := createService(ruleStore).RouteGetRuleVersionsByUID(req, ruleKey.UID)
require.Equal(t, http.StatusForbidden, response.Status())
})
}
func TestRouteGetRulesConfig(t *testing.T) {
gen := models.RuleGen
t.Run("fine-grained access is enabled", func(t *testing.T) {