Alerting: Fix rule state history with annotations backend (#101174)
* add alertUID to annotations API query parameter * update state history UI to fetch rule by UID --------- Signed-off-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
This commit is contained in:
@@ -91,20 +91,22 @@ func (r *LokiHistorianStore) Get(ctx context.Context, query annotations.ItemQuer
|
||||
return make([]*annotations.ItemDTO, 0), nil
|
||||
}
|
||||
|
||||
rule := &ngmodels.AlertRule{}
|
||||
if query.AlertID != 0 {
|
||||
var err error
|
||||
rule, err = r.ruleStore.GetRuleByID(ctx, ngmodels.GetAlertRuleByIDQuery{OrgID: query.OrgID, ID: query.AlertID})
|
||||
var ruleUID string
|
||||
if query.AlertUID != "" {
|
||||
ruleUID = query.AlertUID
|
||||
} else if query.AlertID != 0 {
|
||||
rule, err := r.ruleStore.GetRuleByID(ctx, ngmodels.GetAlertRuleByIDQuery{OrgID: query.OrgID, ID: query.AlertID})
|
||||
if err != nil {
|
||||
if errors.Is(err, ngmodels.ErrAlertRuleNotFound) {
|
||||
return make([]*annotations.ItemDTO, 0), ErrLokiStoreNotFound.Errorf("rule with ID %d does not exist", query.AlertID)
|
||||
}
|
||||
return make([]*annotations.ItemDTO, 0), ErrLokiStoreInternal.Errorf("failed to query rule: %w", err)
|
||||
}
|
||||
ruleUID = rule.UID
|
||||
}
|
||||
|
||||
// No folders in the filter because it filter by Dashboard UID, and the request is already authorized.
|
||||
logQL, err := historian.BuildLogQuery(buildHistoryQuery(&query, accessResources.Dashboards, rule.UID), nil, r.client.MaxQuerySize())
|
||||
logQL, err := historian.BuildLogQuery(buildHistoryQuery(&query, accessResources.Dashboards, ruleUID), nil, r.client.MaxQuerySize())
|
||||
if err != nil {
|
||||
grafanaErr := errutil.Error{}
|
||||
if errors.As(err, &grafanaErr) {
|
||||
|
||||
@@ -108,7 +108,34 @@ func TestIntegrationAlertStateHistoryStore(t *testing.T) {
|
||||
require.Len(t, res, numTransitions)
|
||||
})
|
||||
|
||||
t.Run("should return ErrLokiStoreNotFound if rule is not found", func(t *testing.T) {
|
||||
t.Run("can query history by alert uid", func(t *testing.T) {
|
||||
rule := dashboardRules[dashboard1.UID][0]
|
||||
|
||||
fakeLokiClient.rangeQueryRes = []historian.Stream{
|
||||
historian.StatesToStream(ruleMetaFromRule(t, rule), transitions, map[string]string{}, log.NewNopLogger()),
|
||||
}
|
||||
|
||||
query := annotations.ItemQuery{
|
||||
OrgID: 1,
|
||||
AlertUID: rule.UID,
|
||||
From: start.UnixMilli(),
|
||||
To: start.Add(time.Second * time.Duration(numTransitions+1)).UnixMilli(),
|
||||
}
|
||||
res, err := store.Get(
|
||||
context.Background(),
|
||||
query,
|
||||
&annotation_ac.AccessResources{
|
||||
Dashboards: map[string]int64{
|
||||
dashboard1.UID: dashboard1.ID,
|
||||
},
|
||||
CanAccessDashAnnotations: true,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, numTransitions)
|
||||
})
|
||||
|
||||
t.Run("should return ErrLokiStoreNotFound if rule is not found by ID", func(t *testing.T) {
|
||||
var rules = slices.Concat(maps.Values(dashboardRules)...)
|
||||
id := rand.Int63n(1000) // in Postgres ID is integer, so limit range
|
||||
// make sure id is not known
|
||||
@@ -137,6 +164,27 @@ func TestIntegrationAlertStateHistoryStore(t *testing.T) {
|
||||
require.ErrorIs(t, err, ErrLokiStoreNotFound)
|
||||
})
|
||||
|
||||
t.Run("should return empty response if rule is not found by UID", func(t *testing.T) {
|
||||
query := annotations.ItemQuery{
|
||||
OrgID: 1,
|
||||
AlertUID: "not-found-uid",
|
||||
From: start.UnixMilli(),
|
||||
To: start.Add(time.Second * time.Duration(numTransitions+1)).UnixMilli(),
|
||||
}
|
||||
res, err := store.Get(
|
||||
context.Background(),
|
||||
query,
|
||||
&annotation_ac.AccessResources{
|
||||
Dashboards: map[string]int64{
|
||||
dashboard1.UID: dashboard1.ID,
|
||||
},
|
||||
CanAccessDashAnnotations: true,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res)
|
||||
})
|
||||
|
||||
t.Run("can query history by dashboard id", func(t *testing.T) {
|
||||
fakeLokiClient.rangeQueryRes = []historian.Stream{
|
||||
historian.StatesToStream(ruleMetaFromRule(t, dashboardRules[dashboard1.UID][0]), transitions, map[string]string{}, log.NewNopLogger()),
|
||||
|
||||
@@ -267,10 +267,10 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
|
||||
annotation.updated,
|
||||
usr.email,
|
||||
usr.login,
|
||||
alert.name as alert_name
|
||||
r.title as alert_name
|
||||
FROM annotation
|
||||
LEFT OUTER JOIN ` + r.db.GetDialect().Quote("user") + ` as usr on usr.id = annotation.user_id
|
||||
LEFT OUTER JOIN alert on alert.id = annotation.alert_id
|
||||
LEFT OUTER JOIN alert_rule as r on r.id = annotation.alert_id
|
||||
INNER JOIN (
|
||||
SELECT a.id from annotation a
|
||||
`)
|
||||
@@ -287,6 +287,9 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
|
||||
if query.AlertID != 0 {
|
||||
sql.WriteString(` AND a.alert_id = ?`)
|
||||
params = append(params, query.AlertID)
|
||||
} else if query.AlertUID != "" {
|
||||
sql.WriteString(` AND a.alert_id = (SELECT id FROM alert_rule WHERE uid = ? and org_id = ?)`)
|
||||
params = append(params, query.AlertUID, query.OrgID)
|
||||
}
|
||||
|
||||
if query.DashboardID != 0 {
|
||||
|
||||
Reference in New Issue
Block a user