SSE/NG: Add expression metadata for EvalMatch (#32213)

- Add the ability to stick metadata attached to a value in expressions. Currently uses Frame.Meta.Custom.
- None of this is consumed by anything yet, so an incremental step.
This commit is contained in:
Kyle Brandt 2021-03-23 12:23:54 -04:00 committed by GitHub
parent 7bb79158ed
commit 1cd8981be4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 9 deletions

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/expr/mathexp"
)
@ -59,6 +60,13 @@ func (ccc *ConditionsCmd) NeedsVars() []string {
return vars
}
// EvalMatch represents the series violating the threshold.
type EvalMatch struct {
Value *float64 `json:"value"`
Metric string `json:"metric"`
Labels data.Labels `json:"labels"`
}
// Execute runs the command and returns the results or an error if the command
// failed to execute.
func (ccc *ConditionsCmd) Execute(ctx context.Context, vars mathexp.Vars) (mathexp.Results, error) {
@ -66,8 +74,11 @@ func (ccc *ConditionsCmd) Execute(ctx context.Context, vars mathexp.Vars) (mathe
newRes := mathexp.Results{}
noDataFound := true
matches := []EvalMatch{}
for i, c := range ccc.Conditions {
querySeriesSet := vars[c.QueryRefID]
nilReducedCount := 0
for _, val := range querySeriesSet.Values {
series, ok := val.(mathexp.Series)
if !ok {
@ -78,8 +89,23 @@ func (ccc *ConditionsCmd) Execute(ctx context.Context, vars mathexp.Vars) (mathe
// TODO handle error / no data signals
thisCondNoDataFound := reducedNum.GetFloat64Value() == nil
if thisCondNoDataFound {
nilReducedCount++
}
evalRes := c.Evaluator.Eval(reducedNum)
if evalRes {
match := EvalMatch{
Value: reducedNum.GetFloat64Value(),
Metric: series.GetName(),
}
if reducedNum.GetLabels() != nil {
match.Labels = reducedNum.GetLabels().Copy()
}
matches = append(matches, match)
}
if i == 0 {
firing = evalRes
noDataFound = thisCondNoDataFound
@ -93,10 +119,17 @@ func (ccc *ConditionsCmd) Execute(ctx context.Context, vars mathexp.Vars) (mathe
noDataFound = noDataFound && thisCondNoDataFound
}
}
if len(querySeriesSet.Values) == nilReducedCount {
matches = append(matches, EvalMatch{
Metric: "NoData",
})
}
}
num := mathexp.NewNumber("", nil)
num.SetMeta(matches)
var v float64
switch {
case noDataFound:

View File

@ -114,15 +114,11 @@ func TestUnmarshalConditionCMD(t *testing.T) {
}
func TestConditionsCmdExecute(t *testing.T) {
trueNumber := valBasedNumber(ptr.Float64(1))
falseNumber := valBasedNumber(ptr.Float64(0))
noDataNumber := valBasedNumber(nil)
tests := []struct {
name string
vars mathexp.Vars
conditionsCmd *ConditionsCmd
resultNumber mathexp.Number
resultNumber func() mathexp.Number
}{
{
name: "single query and single condition",
@ -142,7 +138,11 @@ func TestConditionsCmdExecute(t *testing.T) {
Evaluator: &thresholdEvaluator{Type: "gt", Threshold: 34},
},
}},
resultNumber: trueNumber,
resultNumber: func() mathexp.Number {
v := valBasedNumber(ptr.Float64(1))
v.SetMeta([]EvalMatch{{Value: ptr.Float64(35)}})
return v
},
},
{
name: "single query and single ranged condition",
@ -163,7 +163,11 @@ func TestConditionsCmdExecute(t *testing.T) {
},
},
},
resultNumber: falseNumber,
resultNumber: func() mathexp.Number {
v := valBasedNumber(ptr.Float64(0))
v.SetMeta([]EvalMatch{})
return v
},
},
{
name: "single query with no data",
@ -182,7 +186,11 @@ func TestConditionsCmdExecute(t *testing.T) {
},
},
},
resultNumber: noDataNumber,
resultNumber: func() mathexp.Number {
v := valBasedNumber(nil)
v.SetMeta([]EvalMatch{{Metric: "NoData"}})
return v
},
},
}
@ -193,7 +201,7 @@ func TestConditionsCmdExecute(t *testing.T) {
require.Equal(t, 1, len(res.Values))
require.Equal(t, tt.resultNumber, res.Values[0])
require.Equal(t, tt.resultNumber(), res.Values[0])
})
}
}

View File

@ -97,6 +97,14 @@ func (s Series) SetLabels(ls data.Labels) { s.Frame.Fields[s.ValueIdx].Labels =
func (s Series) GetName() string { return s.Frame.Name }
func (s Series) GetMeta() interface{} {
return s.Frame.Meta.Custom
}
func (s Series) SetMeta(v interface{}) {
s.Frame.SetMeta(&data.FrameMeta{Custom: v})
}
// AsDataFrame returns the underlying *data.Frame.
func (s Series) AsDataFrame() *data.Frame { return s.Frame }

View File

@ -30,6 +30,8 @@ type Value interface {
Value() interface{}
GetLabels() data.Labels
SetLabels(data.Labels)
GetMeta() interface{}
SetMeta(interface{})
AsDataFrame() *data.Frame
}
@ -48,6 +50,14 @@ func (s Scalar) GetLabels() data.Labels { return nil }
func (s Scalar) SetLabels(ls data.Labels) {}
func (s Scalar) GetMeta() interface{} {
return s.Frame.Meta.Custom
}
func (s Scalar) SetMeta(v interface{}) {
s.Frame.SetMeta(&data.FrameMeta{Custom: v})
}
// AsDataFrame returns the underlying *data.Frame.
func (s Scalar) AsDataFrame() *data.Frame { return s.Frame }
@ -105,3 +115,11 @@ func NewNumber(name string, labels data.Labels) Number {
),
}
}
func (n Number) GetMeta() interface{} {
return n.Frame.Meta.Custom
}
func (n Number) SetMeta(v interface{}) {
n.Frame.SetMeta(&data.FrameMeta{Custom: v})
}