mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 13:09:22 -06:00
89dcaaf049
This commit changes extractEvalString to sort NumberCaptureValues in ascending order of Var before building the output string. This means that users will see EvaluationString in a consistent order, but also make it possible to assert its output in tests.
109 lines
3.1 KiB
Go
109 lines
3.1 KiB
Go
package eval
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
|
|
"github.com/grafana/grafana/pkg/expr/classic"
|
|
)
|
|
|
|
func extractEvalString(frame *data.Frame) (s string) {
|
|
if frame == nil {
|
|
return "empty frame"
|
|
}
|
|
|
|
if frame.Meta == nil || frame.Meta.Custom == nil {
|
|
return
|
|
}
|
|
|
|
if evalMatches, ok := frame.Meta.Custom.([]classic.EvalMatch); ok {
|
|
sb := strings.Builder{}
|
|
|
|
for i, m := range evalMatches {
|
|
sb.WriteString("[ ")
|
|
sb.WriteString(fmt.Sprintf("var='%s%v' ", frame.RefID, i))
|
|
sb.WriteString(fmt.Sprintf("metric='%s' ", m.Metric))
|
|
sb.WriteString(fmt.Sprintf("labels={%s} ", m.Labels))
|
|
|
|
valString := "null"
|
|
if m.Value != nil {
|
|
valString = fmt.Sprintf("%v", *m.Value)
|
|
}
|
|
|
|
sb.WriteString(fmt.Sprintf("value=%v ", valString))
|
|
|
|
sb.WriteString("]")
|
|
if i < len(evalMatches)-1 {
|
|
sb.WriteString(", ")
|
|
}
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
if captures, ok := frame.Meta.Custom.([]NumberValueCapture); ok {
|
|
// Sort captures in ascending order of "Var" so we can assert in tests
|
|
sort.Slice(captures, func(i, j int) bool {
|
|
return captures[i].Var < captures[j].Var
|
|
})
|
|
sb := strings.Builder{}
|
|
for i, capture := range captures {
|
|
sb.WriteString("[ ")
|
|
sb.WriteString(fmt.Sprintf("var='%s' ", capture.Var))
|
|
sb.WriteString(fmt.Sprintf("labels={%s} ", capture.Labels))
|
|
valString := "null"
|
|
if capture.Value != nil {
|
|
valString = fmt.Sprintf("%v", *capture.Value)
|
|
}
|
|
sb.WriteString(fmt.Sprintf("value=%v ", valString))
|
|
sb.WriteString("]")
|
|
if i < len(captures)-1 {
|
|
sb.WriteString(", ")
|
|
}
|
|
}
|
|
return sb.String()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// extractValues returns the RefID and value for all classic conditions, reduce, and math expressions in the frame.
|
|
// For classic conditions the same refID can have multiple values due to multiple conditions, for them we use the index of
|
|
// the condition in addition to the refID to distinguish between different values.
|
|
// It returns nil if there are no results in the frame.
|
|
func extractValues(frame *data.Frame) map[string]NumberValueCapture {
|
|
if frame == nil {
|
|
return nil
|
|
}
|
|
if frame.Meta == nil || frame.Meta.Custom == nil {
|
|
return nil
|
|
}
|
|
|
|
if matches, ok := frame.Meta.Custom.([]classic.EvalMatch); ok {
|
|
// Classic evaluations only have a single match but it can contain multiple conditions.
|
|
// Conditions have a strict ordering which we can rely on to distinguish between values.
|
|
v := make(map[string]NumberValueCapture, len(matches))
|
|
for i, match := range matches {
|
|
// In classic conditions we use refID and the condition position as a way to distinguish between values.
|
|
// We can guarantee determinism as conditions are ordered and this order is preserved when marshaling.
|
|
refID := fmt.Sprintf("%s%d", frame.RefID, i)
|
|
v[refID] = NumberValueCapture{
|
|
Var: frame.RefID,
|
|
Labels: match.Labels,
|
|
Value: match.Value,
|
|
}
|
|
}
|
|
return v
|
|
}
|
|
|
|
if captures, ok := frame.Meta.Custom.([]NumberValueCapture); ok {
|
|
v := make(map[string]NumberValueCapture, len(captures))
|
|
for _, capture := range captures {
|
|
v[capture.Var] = capture
|
|
}
|
|
return v
|
|
}
|
|
return nil
|
|
}
|