diff --git a/pkg/tsdb/influxdb/response_parser.go b/pkg/tsdb/influxdb/response_parser.go index 6605e7fe982..ec203d18acc 100644 --- a/pkg/tsdb/influxdb/response_parser.go +++ b/pkg/tsdb/influxdb/response_parser.go @@ -74,6 +74,9 @@ func transformRows(rows []Row, query Query) data.Frames { // It's sized for a reasonably-large name, but will grow if needed. frameName := make([]byte, 0, 128) + retentionPolicyQuery := isRetentionPolicyQuery(query) + tagValuesQuery := isTagValuesQuery(query) + for _, row := range rows { var hasTimeCol = false @@ -86,11 +89,32 @@ func transformRows(rows []Row, query Query) data.Frames { if !hasTimeCol { var values []string + if retentionPolicyQuery { + values = make([]string, 1, len(row.Values)) + } else { + values = make([]string, 0, len(row.Values)) + } + for _, valuePair := range row.Values { - if strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW TAG VALUES")) { + if tagValuesQuery { if len(valuePair) >= 2 { values = append(values, valuePair[1].(string)) } + } else if retentionPolicyQuery { + // We want to know whether the given retention policy is the default one or not. + // If it is default policy then we should add it to the beginning. + // The index 4 gives us if that policy is default or not. + // https://docs.influxdata.com/influxdb/v1.8/query_language/explore-schema/#show-retention-policies + // Only difference is v0.9. In that version we don't receive shardGroupDuration value. + // https://archive.docs.influxdata.com/influxdb/v0.9/query_language/schema_exploration/#show-retention-policies + // Since it is always the last value we will check that last value always. + if len(valuePair) >= 1 { + if valuePair[len(row.Columns)-1].(bool) { + values[0] = valuePair[0].(string) + } else { + values = append(values, valuePair[0].(string)) + } + } } else { if len(valuePair) >= 1 { values = append(values, valuePair[0].(string)) @@ -294,3 +318,11 @@ func parseNumber(value interface{}) *float64 { return &fvalue } + +func isTagValuesQuery(query Query) bool { + return strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW TAG VALUES")) +} + +func isRetentionPolicyQuery(query Query) bool { + return strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW RETENTION POLICIES")) +} diff --git a/pkg/tsdb/influxdb/response_parser_test.go b/pkg/tsdb/influxdb/response_parser_test.go index bd0b3e4878d..b73081e3a38 100644 --- a/pkg/tsdb/influxdb/response_parser_test.go +++ b/pkg/tsdb/influxdb/response_parser_test.go @@ -10,9 +10,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/util" ) func prepare(text string) io.ReadCloser { @@ -733,6 +734,78 @@ func TestInfluxdbResponseParser(t *testing.T) { }) } +func TestResponseParser_Parse_RetentionPolicy(t *testing.T) { + t.Run("Influxdb response parser should parse metricFindQueries->SHOW RETENTION POLICIES normally", func(t *testing.T) { + parser := &ResponseParser{} + + response := ` + { + "results": [ + { + "statement_id": 0, + "series": [ + { + "columns": [ + "name", + "duration", + "shardGroupDuration", + "replicaN", + "default" + ], + "values": [ + [ + "autogen", + "0s", + "168h0m0s", + 1, + false + ], + [ + "bar", + "24h0m0s", + "1h0m0s", + 1, + true + ], + [ + "5m_avg", + "2400h0m0s", + "24h0m0s", + 1, + false + ], + [ + "1m_avg", + "240h0m0s", + "24h0m0s", + 1, + false + ] + ] + } + ] + } + ] + } + ` + + var queries []Query + queries = append(queries, Query{RefID: "metricFindQuery", RawQuery: "SHOW RETENTION POLICIES"}) + policyFrame := data.NewFrame("", + data.NewField("value", nil, []string{ + "bar", "autogen", "5m_avg", "1m_avg", + }), + ) + + result := parser.Parse(prepare(response), queries) + + frame := result.Responses["metricFindQuery"] + if diff := cmp.Diff(policyFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" { + t.Errorf("Result mismatch (-want +got):\n%s", diff) + } + }) +} + func TestResponseParser_Parse(t *testing.T) { tests := []struct { name string