From 10314585ecdc726e0c98cf30665ae7a511fa84b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Qu=C3=A9m=C3=A9ner?= Date: Tue, 17 Sep 2024 13:20:40 +0200 Subject: [PATCH] fix(alerting): extend instant vector check for non-nullable types (#93323) --- pkg/services/ngalert/eval/eval.go | 16 +++--- pkg/services/ngalert/eval/eval_test.go | 71 ++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/pkg/services/ngalert/eval/eval.go b/pkg/services/ngalert/eval/eval.go index 0f0bf3074c1..9b1ad620e64 100644 --- a/pkg/services/ngalert/eval/eval.go +++ b/pkg/services/ngalert/eval/eval.go @@ -22,6 +22,7 @@ import ( "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" ) var logger = log.New("ngalert.eval") @@ -789,17 +790,20 @@ func buildResult(f *data.Frame, val *float64, ts time.Time) Result { } func scalarInstantVector(f *data.Frame) (*float64, bool) { - defaultReturnValue := 0.0 if len(f.Fields) != 2 { - return &defaultReturnValue, false + return nil, false } if f.Fields[0].Len() > 1 || (f.Fields[0].Type() != data.FieldTypeNullableTime && f.Fields[0].Type() != data.FieldTypeTime) { - return &defaultReturnValue, false + return nil, false } - if f.Fields[1].Len() > 1 || f.Fields[1].Type() != data.FieldTypeNullableFloat64 { - return &defaultReturnValue, false + switch f.Fields[1].Type() { + case data.FieldTypeFloat64: + return util.Pointer(f.Fields[1].At(0).(float64)), true + case data.FieldTypeNullableFloat64: + return f.Fields[1].At(0).(*float64), true + default: + return nil, true } - return f.Fields[1].At(0).(*float64), true } // AsDataFrame forms the EvalResults in Frame suitable for displaying in the table panel of the front end. diff --git a/pkg/services/ngalert/eval/eval_test.go b/pkg/services/ngalert/eval/eval_test.go index f2cc7273ab9..ed2b0e18367 100644 --- a/pkg/services/ngalert/eval/eval_test.go +++ b/pkg/services/ngalert/eval/eval_test.go @@ -2,6 +2,7 @@ package eval import ( "context" + "encoding/json" "errors" "fmt" "math/rand" @@ -1342,6 +1343,76 @@ func TestCreate(t *testing.T) { }) } +func TestQueryServiceResponse(t *testing.T) { + data := ` +{ + "results": { + "A": { + "status": 200, + "frames": [ + { + "schema": { + "refId": "A", + "meta": { + "type": "numeric-multi", + "typeVersion": [ + 0, + 1 + ], + "custom": { + "resultType": "scalar" + }, + "executedQueryString": "Expr: 1\nStep: 15s" + }, + "fields": [ + { + "name": "Time", + "type": "time", + "typeInfo": { + "frame": "time.Time" + }, + "config": { + "interval": 15000 + } + }, + { + "name": "Value", + "type": "number", + "typeInfo": { + "frame": "float64" + }, + "labels": {}, + "config": { + "displayNameFromDS": "1" + } + } + ] + }, + "data": { + "values": [ + [ + 1719855251019 + ], + [ + 1 + ] + ] + } + } + ] + } + } +} +` + var s backend.QueryDataResponse + err := json.Unmarshal([]byte(data), &s) + require.NoError(t, err) + res := EvaluateAlert(&s, models.Condition{Condition: "A"}, time.Time{}) + + require.Equal(t, 1, len(res)) + require.Equal(t, Alerting, res[0].State) +} + type fakeExpressionService struct { hook func(ctx context.Context, now time.Time, pipeline expr.DataPipeline) (*backend.QueryDataResponse, error) buildHook func(req *expr.Request) (expr.DataPipeline, error)