diff --git a/pkg/tsdb/testdatasource/scenarios.go b/pkg/tsdb/testdatasource/scenarios.go index 20d91acace9..b6b1bab0f2f 100644 --- a/pkg/tsdb/testdatasource/scenarios.go +++ b/pkg/tsdb/testdatasource/scenarios.go @@ -652,7 +652,7 @@ func (s *Service) handleLogsScenario(ctx context.Context, req *backend.QueryData } func RandomWalk(query backend.DataQuery, model *simplejson.Json, index int) *data.Frame { - rand := rand.New(rand.NewSource(time.Now().UnixNano())) + rand := rand.New(rand.NewSource(time.Now().UnixNano() + int64(index))) timeWalkerMs := query.TimeRange.From.UnixNano() / int64(time.Millisecond) to := query.TimeRange.To.UnixNano() / int64(time.Millisecond) startValue := model.Get("startValue").MustFloat64(rand.Float64() * 100) @@ -700,7 +700,7 @@ func RandomWalk(query backend.DataQuery, model *simplejson.Json, index int) *dat SetConfig(&data.FieldConfig{ Interval: float64(query.Interval.Milliseconds()), }), - data.NewField(frameNameForQuery(query, model, index), parseLabels(model), floatVec), + data.NewField(frameNameForQuery(query, model, index), parseLabels(model, index), floatVec), ) } @@ -843,7 +843,7 @@ func predictableCSVWave(query backend.DataQuery, model *simplejson.Json) ([]*dat frame := newSeriesForQuery(query, model, 0) frame.Fields = fields - frame.Fields[1].Labels = parseLabelsString(subQ.Labels) + frame.Fields[1].Labels = parseLabelsString(subQ.Labels, 0) if subQ.Name != "" { frame.Name = subQ.Name } @@ -929,7 +929,7 @@ func predictablePulse(query backend.DataQuery, model *simplejson.Json) (*data.Fr frame := newSeriesForQuery(query, model, 0) frame.Fields = fields - frame.Fields[1].Labels = parseLabels(model) + frame.Fields[1].Labels = parseLabels(model, 0) return frame, nil } @@ -998,12 +998,12 @@ func newSeriesForQuery(query backend.DataQuery, model *simplejson.Json, index in * * '{job="foo", instance="bar"} => {job: "foo", instance: "bar"}` */ -func parseLabels(model *simplejson.Json) data.Labels { +func parseLabels(model *simplejson.Json, seriesIndex int) data.Labels { labelText := model.Get("labels").MustString("") - return parseLabelsString(labelText) + return parseLabelsString(labelText, seriesIndex) } -func parseLabelsString(labelText string) data.Labels { +func parseLabelsString(labelText string, seriesIndex int) data.Labels { if labelText == "" { return data.Labels{} } @@ -1020,6 +1020,7 @@ func parseLabelsString(labelText string) data.Labels { key := strings.TrimSpace(keyval[:idx]) val := strings.TrimSpace(keyval[idx+1:]) val = strings.Trim(val, "\"") + val = strings.ReplaceAll(val, "$seriesIndex", strconv.Itoa(seriesIndex)) tags[key] = val } diff --git a/pkg/tsdb/testdatasource/scenarios_test.go b/pkg/tsdb/testdatasource/scenarios_test.go index 64da99d41b1..95f3b8e509c 100644 --- a/pkg/tsdb/testdatasource/scenarios_test.go +++ b/pkg/tsdb/testdatasource/scenarios_test.go @@ -3,15 +3,17 @@ package testdatasource import ( "context" "fmt" + "math/rand" "testing" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana/pkg/components/simplejson" - "github.com/grafana/grafana/pkg/tsdb/legacydata" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/tsdb/legacydata" ) func TestTestdataScenarios(t *testing.T) { @@ -193,23 +195,47 @@ func TestParseLabels(t *testing.T) { "job": "foo", "instance": "bar", } + seriesIndex := rand.Int() - tcs := []struct { - model map[string]interface{} + tests := []struct { + name string + model map[string]interface{} + expected data.Labels }{ - {model: map[string]interface{}{ - "labels": `{job="foo", instance="bar"}`, - }}, - {model: map[string]interface{}{ - "labels": `job=foo, instance=bar`, - }}, - {model: map[string]interface{}{ - "labels": `job = foo,instance = bar`, - }}, + { + name: "wrapped in {} and quoted value ", + model: map[string]interface{}{"labels": `{job="foo", instance="bar"}`}, + expected: expectedTags, + }, + { + name: "comma-separated non-quoted", + model: map[string]interface{}{"labels": `job=foo, instance=bar`}, + expected: expectedTags, + }, + { + name: "comma-separated quoted", + model: map[string]interface{}{"labels": `job="foo"", instance="bar"`}, + expected: expectedTags, + }, + { + name: "comma-separated with spaces, non quoted", + model: map[string]interface{}{"labels": `job = foo,instance = bar`}, + expected: expectedTags, + }, + { + name: "expands $seriesIndex", + model: map[string]interface{}{"labels": `job=series-$seriesIndex,instance=bar`}, + expected: data.Labels{ + "job": fmt.Sprintf("series-%d", seriesIndex), + "instance": "bar", + }, + }, } - for i, tc := range tcs { - model := simplejson.NewFromAny(tc.model) - assert.Equal(t, expectedTags, parseLabels(model), fmt.Sprintf("Actual tags in test case %d doesn't match expected tags", i+1)) + for i, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + model := simplejson.NewFromAny(tc.model) + assert.Equal(t, tc.expected, parseLabels(model, seriesIndex), fmt.Sprintf("Actual tags in test case %d doesn't match expected tags", i+1)) + }) } } diff --git a/public/app/plugins/datasource/testdata/QueryEditor.tsx b/public/app/plugins/datasource/testdata/QueryEditor.tsx index 1fb08b1aad6..666affe9f19 100644 --- a/public/app/plugins/datasource/testdata/QueryEditor.tsx +++ b/public/app/plugins/datasource/testdata/QueryEditor.tsx @@ -231,6 +231,9 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
key=value, key2=value
+ Value can contain templates: +
+ $seriesIndex - replaced with index of the series } >