diff --git a/pkg/tsdb/cloudwatch/response_parser.go b/pkg/tsdb/cloudwatch/response_parser.go index 78b7154afa7..05bb8dcd9e6 100644 --- a/pkg/tsdb/cloudwatch/response_parser.go +++ b/pkg/tsdb/cloudwatch/response_parser.go @@ -2,6 +2,7 @@ package cloudwatch import ( "fmt" + "regexp" "sort" "strings" "time" @@ -12,6 +13,9 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) +// matches a dynamic label +var dynamicLabel = regexp.MustCompile(`\$\{.+\}`) + func (e *cloudWatchExecutor) parseResponse(startTime time.Time, endTime time.Time, metricDataOutputs []*cloudwatch.GetMetricDataOutput, queries []*models.CloudWatchQuery) ([]*responseWrapper, error) { aggregatedResponse := aggregateResponse(metricDataOutputs) @@ -110,6 +114,8 @@ func getLabels(cloudwatchLabel string, query *models.CloudWatchQuery) data.Label func buildDataFrames(startTime time.Time, endTime time.Time, aggregatedResponse models.QueryRowResponse, query *models.CloudWatchQuery) (data.Frames, error) { frames := data.Frames{} + hasStaticLabel := query.Label != "" && !dynamicLabel.MatchString(query.Label) + for _, metric := range aggregatedResponse.Metrics { label := *metric.Label @@ -169,10 +175,15 @@ func buildDataFrames(startTime time.Time, endTime time.Time, aggregatedResponse timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, timestamps) valueField := data.NewField(data.TimeSeriesValueFieldName, labels, points) - valueField.SetConfig(&data.FieldConfig{DisplayNameFromDS: label, Links: createDataLinks(deepLink)}) + name := label + // CloudWatch appends the dimensions to the returned label if the query label is not dynamic, so static labels need to be set + if hasStaticLabel { + name = query.Label + } + valueField.SetConfig(&data.FieldConfig{DisplayNameFromDS: name, Links: createDataLinks(deepLink)}) frame := data.Frame{ - Name: label, + Name: name, Fields: []*data.Field{ timeField, valueField, diff --git a/pkg/tsdb/cloudwatch/response_parser_test.go b/pkg/tsdb/cloudwatch/response_parser_test.go index 1701bcce634..5cdedf42967 100644 --- a/pkg/tsdb/cloudwatch/response_parser_test.go +++ b/pkg/tsdb/cloudwatch/response_parser_test.go @@ -376,6 +376,82 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { assert.Equal(t, "some label", frames[0].Name) }) + t.Run("when non-static label set on query", func(t *testing.T) { + timestamp := time.Unix(0, 0) + response := &models.QueryRowResponse{ + Metrics: []*cloudwatch.MetricDataResult{ + { + Id: aws.String("lb3"), + Label: aws.String("some label"), + Timestamps: []*time.Time{ + aws.Time(timestamp), + }, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), + }, + }, + } + + query := &models.CloudWatchQuery{ + RefId: "refId1", + Region: "us-east-1", + Namespace: "AWS/ApplicationELB", + MetricName: "TargetResponseTime", + Dimensions: map[string][]string{ + "LoadBalancer": {"lb1"}, + "InstanceType": {"micro"}, + "Resource": {"res"}, + }, + Statistic: "Average", + Period: 60, + MetricQueryType: models.MetricQueryTypeQuery, + MetricEditorMode: models.MetricEditorModeBuilder, + Label: "set ${AVG} label", + } + frames, err := buildDataFrames(startTime, endTime, *response, query) + require.NoError(t, err) + + assert.Equal(t, "some label", frames[0].Name) + }) + + t.Run("unless static label set on query", func(t *testing.T) { + timestamp := time.Unix(0, 0) + response := &models.QueryRowResponse{ + Metrics: []*cloudwatch.MetricDataResult{ + { + Id: aws.String("lb3"), + Label: aws.String("some label"), + Timestamps: []*time.Time{ + aws.Time(timestamp), + }, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), + }, + }, + } + + query := &models.CloudWatchQuery{ + RefId: "refId1", + Region: "us-east-1", + Namespace: "AWS/ApplicationELB", + MetricName: "TargetResponseTime", + Dimensions: map[string][]string{ + "LoadBalancer": {"lb1"}, + "InstanceType": {"micro"}, + "Resource": {"res"}, + }, + Statistic: "Average", + Period: 60, + MetricQueryType: models.MetricQueryTypeQuery, + MetricEditorMode: models.MetricEditorModeBuilder, + Label: "actual", + } + frames, err := buildDataFrames(startTime, endTime, *response, query) + require.NoError(t, err) + + assert.Equal(t, "actual", frames[0].Name) + }) + t.Run("Parse cloudwatch response", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{