Alerting: Fix ignored filters when paginating alert rules in the API (#114710)

Alerting: Fix alert rules pagination with filters
This commit is contained in:
Alexander Akhmetov
2025-12-02 15:49:49 +01:00
committed by GitHub
parent 4fc328d57a
commit 7dd9e8f185
2 changed files with 103 additions and 6 deletions
+15 -6
View File
@@ -676,8 +676,10 @@ func (st DBstore) ListAlertRulesByGroup(ctx context.Context, query *ngmodels.Lis
}
func buildGroupCursorCondition(sess *xorm.Session, c ngmodels.GroupCursor) *xorm.Session {
return sess.Where("(namespace_uid > ?)", c.NamespaceUID).
Or("(namespace_uid = ? AND rule_group > ?)", c.NamespaceUID, c.RuleGroup)
return sess.And(
"((namespace_uid > ?) OR (namespace_uid = ? AND rule_group > ?))",
c.NamespaceUID, c.NamespaceUID, c.RuleGroup,
)
}
func shouldIncludeRule(rule *ngmodels.AlertRule, query *ngmodels.ListAlertRulesExtendedQuery, groupsMap map[string]struct{}) bool {
@@ -979,10 +981,17 @@ func decodeCursor(token string) (continueCursor, error) {
}
func buildCursorCondition(sess *xorm.Session, c continueCursor) *xorm.Session {
return sess.Where("(namespace_uid > ?)", c.NamespaceUID).
Or("(namespace_uid = ? AND rule_group > ?)", c.NamespaceUID, c.RuleGroup).
Or("(namespace_uid = ? AND rule_group = ? AND rule_group_idx > ?)", c.NamespaceUID, c.RuleGroup, c.RuleGroupIdx).
Or("(namespace_uid = ? AND rule_group = ? AND rule_group_idx = ? AND id > ?)", c.NamespaceUID, c.RuleGroup, c.RuleGroupIdx, c.ID)
return sess.And(`(
(namespace_uid > ?)
OR (namespace_uid = ? AND rule_group > ?)
OR (namespace_uid = ? AND rule_group = ? AND rule_group_idx > ?)
OR (namespace_uid = ? AND rule_group = ? AND rule_group_idx = ? AND id > ?)
)`,
c.NamespaceUID,
c.NamespaceUID, c.RuleGroup,
c.NamespaceUID, c.RuleGroup, c.RuleGroupIdx,
c.NamespaceUID, c.RuleGroup, c.RuleGroupIdx, c.ID,
)
}
// Count returns either the number of the alert rules under a specific org (if orgID is not zero)
@@ -1963,6 +1963,94 @@ func TestIntegration_ListAlertRulesByGroup(t *testing.T) {
require.Equal(t, expected, actual, "rules should be ordered by group name")
}
})
t.Run("SearchTitle filter should be applied across all pages", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
folderService2 := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures())
store := createTestStore(sqlStore, folderService2, &logtest.Fake{}, cfg.UnifiedAlerting, &fakeBus{})
searchTitle := "str"
// Create rules across multiple groups with some having "str" in title
// Group 1: 2 rules, one with "str", one without
// Group 2: 2 rules, none with "str"
// Group 3: 2 rules, one with "str", one without
ruleGen := models.RuleGen.With(
models.RuleMuts.WithIntervalMatching(cfg.UnifiedAlerting.BaseInterval),
models.RuleMuts.WithOrgID(orgID),
)
ns := "test-ns"
createRule(t, store, ruleGen.With(
ruleGen.WithNamespaceUID(ns),
ruleGen.WithGroupName("group-1"),
models.RuleMuts.WithTitle("rule-1 with str"),
))
createRule(t, store, ruleGen.With(
ruleGen.WithNamespaceUID(ns),
ruleGen.WithGroupName("group-1"),
models.RuleMuts.WithTitle("rule-2"),
))
createRule(t, store, ruleGen.With(
ruleGen.WithNamespaceUID(ns),
ruleGen.WithGroupName("group-2"),
models.RuleMuts.WithTitle("rule-3"),
))
createRule(t, store, ruleGen.With(
ruleGen.WithNamespaceUID(ns),
ruleGen.WithGroupName("group-2"),
models.RuleMuts.WithTitle("rule-4"),
))
createRule(t, store, ruleGen.With(
ruleGen.WithNamespaceUID(ns),
ruleGen.WithGroupName("group-3"),
models.RuleMuts.WithTitle("rule-5 with str"),
))
createRule(t, store, ruleGen.With(
ruleGen.WithNamespaceUID(ns),
ruleGen.WithGroupName("group-3"),
models.RuleMuts.WithTitle("rule-6"),
))
// First page: get 1 group with SearchTitle filter
result, continueToken, err := store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesExtendedQuery{
ListAlertRulesQuery: models.ListAlertRulesQuery{
OrgID: orgID,
SearchTitle: searchTitle,
},
Limit: 1,
})
require.NoError(t, err)
require.NotEmpty(t, continueToken, "should have more pages")
// Verify first page only has rules with "str" in title
require.Len(t, result, 1)
for _, rule := range result {
require.Contains(t, strings.ToLower(rule.Title), searchTitle)
require.Equal(t, "group-1", rule.RuleGroup)
}
// Second page
result2, continueToken2, err := store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesExtendedQuery{
ListAlertRulesQuery: models.ListAlertRulesQuery{
OrgID: orgID,
SearchTitle: searchTitle,
},
Limit: 1,
ContinueToken: continueToken,
})
require.NoError(t, err)
// Verify second page also only has rules with "str" in title
require.Len(t, result2, 1)
for _, rule := range result2 {
require.Contains(t, strings.ToLower(rule.Title), searchTitle)
require.Equal(t, "group-3", rule.RuleGroup)
}
// After all pages, token should be empty
require.Empty(t, continueToken2, "should be no more pages")
})
}
func Benchmark_ListAlertRules(b *testing.B) {