mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
InfluxDB: Fix sending retention policy with the backend request (#72763)
* Add retention policy to the request * refactor * refactor influxql query flow * fix healthcheck * organize imports * handle queries separately * fix tests * update bench test
This commit is contained in:
parent
dc26cdf6c9
commit
29ea0886e3
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/flux"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/fsql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/influxql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
)
|
||||
|
||||
@ -34,7 +35,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
|
||||
case influxVersionFlux:
|
||||
return CheckFluxHealth(ctx, dsInfo, req)
|
||||
case influxVersionInfluxQL:
|
||||
return CheckInfluxQLHealth(ctx, dsInfo, s)
|
||||
return CheckInfluxQLHealth(ctx, dsInfo)
|
||||
case influxVersionSQL:
|
||||
return CheckSQLHealth(ctx, dsInfo, req)
|
||||
default:
|
||||
@ -77,30 +78,22 @@ func CheckFluxHealth(ctx context.Context, dsInfo *models.DatasourceInfo,
|
||||
return getHealthCheckMessage(logger, "", errors.New("error getting flux query buckets"))
|
||||
}
|
||||
|
||||
func CheckInfluxQLHealth(ctx context.Context, dsInfo *models.DatasourceInfo, s *Service) (*backend.CheckHealthResult, error) {
|
||||
func CheckInfluxQLHealth(ctx context.Context, dsInfo *models.DatasourceInfo) (*backend.CheckHealthResult, error) {
|
||||
logger := logger.FromContext(ctx)
|
||||
queryString := "SHOW measurements"
|
||||
hcRequest, err := s.createRequest(ctx, logger, dsInfo, queryString)
|
||||
if err != nil {
|
||||
return getHealthCheckMessage(logger, "error creating influxDB healthcheck request", err)
|
||||
}
|
||||
|
||||
res, err := dsInfo.HTTPClient.Do(hcRequest)
|
||||
resp, err := influxql.Query(ctx, dsInfo, &backend.QueryDataRequest{
|
||||
Queries: []backend.DataQuery{
|
||||
{
|
||||
RefID: refID,
|
||||
QueryType: "health",
|
||||
JSON: []byte(`{"query": "SHOW measurements", "rawQuery": true}`),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return getHealthCheckMessage(logger, "error performing influxQL query", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := res.Body.Close(); err != nil {
|
||||
logger.Warn("failed to close response body", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
resp := s.responseParser.Parse(res.Body, res.StatusCode, []Query{{
|
||||
RefID: refID,
|
||||
UseRawQuery: true,
|
||||
RawQuery: queryString,
|
||||
}})
|
||||
if res, ok := resp.Responses[refID]; ok {
|
||||
if res.Error != nil {
|
||||
return getHealthCheckMessage(logger, "error reading influxDB", res.Error)
|
||||
|
@ -3,12 +3,7 @@ package influxdb
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
||||
@ -19,26 +14,19 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/influxql"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
)
|
||||
|
||||
var logger log.Logger = log.New("tsdb.influxdb")
|
||||
|
||||
type Service struct {
|
||||
queryParser *InfluxdbQueryParser
|
||||
responseParser *ResponseParser
|
||||
|
||||
im instancemgmt.InstanceManager
|
||||
}
|
||||
|
||||
var ErrInvalidHttpMode = errors.New("'httpMode' should be either 'GET' or 'POST'")
|
||||
|
||||
func ProvideService(httpClient httpclient.Provider) *Service {
|
||||
return &Service{
|
||||
queryParser: &InfluxdbQueryParser{},
|
||||
responseParser: &ResponseParser{},
|
||||
im: datasource.NewInstanceManager(newInstanceSettings(httpClient)),
|
||||
im: datasource.NewInstanceManager(newInstanceSettings(httpClient)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,102 +94,19 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version := dsInfo.Version
|
||||
if version == influxVersionFlux {
|
||||
|
||||
logger.Debug(fmt.Sprintf("Making a %s type query", dsInfo.Version))
|
||||
|
||||
switch dsInfo.Version {
|
||||
case influxVersionFlux:
|
||||
return flux.Query(ctx, dsInfo, *req)
|
||||
}
|
||||
if version == influxVersionSQL {
|
||||
case influxVersionInfluxQL:
|
||||
return influxql.Query(ctx, dsInfo, req)
|
||||
case influxVersionSQL:
|
||||
return fsql.Query(ctx, dsInfo, *req)
|
||||
}
|
||||
|
||||
logger.Debug("Making a non-Flux type query")
|
||||
|
||||
var allRawQueries string
|
||||
queries := make([]Query, 0, len(req.Queries))
|
||||
|
||||
for _, reqQuery := range req.Queries {
|
||||
query, err := s.queryParser.Parse(reqQuery)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
|
||||
rawQuery, err := query.Build(req)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
|
||||
allRawQueries = allRawQueries + rawQuery + ";"
|
||||
query.RefID = reqQuery.RefID
|
||||
query.RawQuery = rawQuery
|
||||
queries = append(queries, *query)
|
||||
}
|
||||
|
||||
if setting.Env == setting.Dev {
|
||||
logger.Debug("Influxdb query", "raw query", allRawQueries)
|
||||
}
|
||||
|
||||
request, err := s.createRequest(ctx, logger, dsInfo, allRawQueries)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
|
||||
res, err := dsInfo.HTTPClient.Do(request)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
defer func() {
|
||||
if err := res.Body.Close(); err != nil {
|
||||
logger.Warn("Failed to close response body", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
resp := s.responseParser.Parse(res.Body, res.StatusCode, queries)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *Service) createRequest(ctx context.Context, logger log.Logger, dsInfo *models.DatasourceInfo, query string) (*http.Request, error) {
|
||||
u, err := url.Parse(dsInfo.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Path = path.Join(u.Path, "query")
|
||||
httpMode := dsInfo.HTTPMode
|
||||
|
||||
var req *http.Request
|
||||
switch httpMode {
|
||||
case "GET":
|
||||
req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "POST":
|
||||
bodyValues := url.Values{}
|
||||
bodyValues.Add("q", query)
|
||||
body := bodyValues.Encode()
|
||||
req, err = http.NewRequestWithContext(ctx, http.MethodPost, u.String(), strings.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidHttpMode
|
||||
return nil, fmt.Errorf("unknown influxdb version")
|
||||
}
|
||||
|
||||
params := req.URL.Query()
|
||||
params.Set("db", dsInfo.DbName)
|
||||
params.Set("epoch", "ms")
|
||||
|
||||
if httpMode == "GET" {
|
||||
params.Set("q", query)
|
||||
} else if httpMode == "POST" {
|
||||
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
req.URL.RawQuery = params.Encode()
|
||||
|
||||
logger.Debug("Influxdb request", "url", req.URL.String())
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *Service) getDSInfo(ctx context.Context, pluginCtx backend.PluginContext) (*models.DatasourceInfo, error) {
|
||||
|
125
pkg/tsdb/influxdb/influxql/influxql.go
Normal file
125
pkg/tsdb/influxdb/influxql/influxql.go
Normal file
@ -0,0 +1,125 @@
|
||||
package influxql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
)
|
||||
|
||||
const defaultRetentionPolicy = "default"
|
||||
|
||||
var (
|
||||
ErrInvalidHttpMode = errors.New("'httpMode' should be either 'GET' or 'POST'")
|
||||
glog = log.New("tsdb.influx_influxql")
|
||||
)
|
||||
|
||||
func Query(ctx context.Context, dsInfo *models.DatasourceInfo, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
logger := glog.FromContext(ctx)
|
||||
response := backend.NewQueryDataResponse()
|
||||
|
||||
for _, reqQuery := range req.Queries {
|
||||
query, err := models.QueryParse(reqQuery)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
|
||||
rawQuery, err := query.Build(req)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
|
||||
query.RefID = reqQuery.RefID
|
||||
query.RawQuery = rawQuery
|
||||
|
||||
if setting.Env == setting.Dev {
|
||||
logger.Debug("Influxdb query", "raw query", rawQuery)
|
||||
}
|
||||
|
||||
request, err := createRequest(ctx, logger, dsInfo, rawQuery, query.Policy)
|
||||
if err != nil {
|
||||
return &backend.QueryDataResponse{}, err
|
||||
}
|
||||
|
||||
resp, err := execute(dsInfo, logger, query, request)
|
||||
|
||||
if err != nil {
|
||||
response.Responses[query.RefID] = backend.DataResponse{Error: err}
|
||||
} else {
|
||||
response.Responses[query.RefID] = resp
|
||||
}
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func createRequest(ctx context.Context, logger log.Logger, dsInfo *models.DatasourceInfo, queryStr string, retentionPolicy string) (*http.Request, error) {
|
||||
u, err := url.Parse(dsInfo.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Path = path.Join(u.Path, "query")
|
||||
httpMode := dsInfo.HTTPMode
|
||||
|
||||
var req *http.Request
|
||||
switch httpMode {
|
||||
case "GET":
|
||||
req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "POST":
|
||||
bodyValues := url.Values{}
|
||||
bodyValues.Add("q", queryStr)
|
||||
body := bodyValues.Encode()
|
||||
req, err = http.NewRequestWithContext(ctx, http.MethodPost, u.String(), strings.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidHttpMode
|
||||
}
|
||||
|
||||
params := req.URL.Query()
|
||||
params.Set("db", dsInfo.DbName)
|
||||
params.Set("epoch", "ms")
|
||||
// default is hardcoded default retention policy
|
||||
// InfluxDB will use the default policy when it is not added to the request
|
||||
if retentionPolicy != "" && retentionPolicy != "default" {
|
||||
params.Set("rp", retentionPolicy)
|
||||
}
|
||||
|
||||
if httpMode == "GET" {
|
||||
params.Set("q", queryStr)
|
||||
} else if httpMode == "POST" {
|
||||
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
req.URL.RawQuery = params.Encode()
|
||||
|
||||
logger.Debug("Influxdb request", "url", req.URL.String())
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func execute(dsInfo *models.DatasourceInfo, logger log.Logger, query *models.Query, request *http.Request) (backend.DataResponse, error) {
|
||||
res, err := dsInfo.HTTPClient.Do(request)
|
||||
if err != nil {
|
||||
return backend.DataResponse{}, err
|
||||
}
|
||||
defer func() {
|
||||
if err := res.Body.Close(); err != nil {
|
||||
logger.Warn("Failed to close response body", "err", err)
|
||||
}
|
||||
}()
|
||||
resp := ResponseParse(res.Body, res.StatusCode, query)
|
||||
return *resp, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package influxql
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -9,23 +9,21 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
)
|
||||
|
||||
func TestExecutor_createRequest(t *testing.T) {
|
||||
logger := log.New("tsdb.influx_influxql_test")
|
||||
datasource := &models.DatasourceInfo{
|
||||
URL: "http://awesome-influxdb:1337",
|
||||
DbName: "awesome-db",
|
||||
HTTPMode: "GET",
|
||||
}
|
||||
query := "SELECT awesomeness FROM somewhere"
|
||||
s := &Service{
|
||||
queryParser: &InfluxdbQueryParser{},
|
||||
responseParser: &ResponseParser{},
|
||||
}
|
||||
|
||||
t.Run("createRequest with GET httpMode", func(t *testing.T) {
|
||||
req, err := s.createRequest(context.Background(), logger, datasource, query)
|
||||
req, err := createRequest(context.Background(), logger, datasource, query, defaultRetentionPolicy)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -39,7 +37,7 @@ func TestExecutor_createRequest(t *testing.T) {
|
||||
|
||||
t.Run("createRequest with POST httpMode", func(t *testing.T) {
|
||||
datasource.HTTPMode = "POST"
|
||||
req, err := s.createRequest(context.Background(), logger, datasource, query)
|
||||
req, err := createRequest(context.Background(), logger, datasource, query, defaultRetentionPolicy)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "POST", req.Method)
|
||||
@ -58,7 +56,7 @@ func TestExecutor_createRequest(t *testing.T) {
|
||||
|
||||
t.Run("createRequest with PUT httpMode", func(t *testing.T) {
|
||||
datasource.HTTPMode = "PUT"
|
||||
_, err := s.createRequest(context.Background(), logger, datasource, query)
|
||||
_, err := createRequest(context.Background(), logger, datasource, query, defaultRetentionPolicy)
|
||||
require.EqualError(t, err, ErrInvalidHttpMode.Error())
|
||||
})
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package influxql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -11,51 +11,45 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
)
|
||||
|
||||
type ResponseParser struct{}
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
)
|
||||
|
||||
var (
|
||||
legendFormat = regexp.MustCompile(`\[\[([\@\/\w-]+)(\.[\@\/\w-]+)*\]\]*|\$([\@\w-]+?)*`)
|
||||
)
|
||||
|
||||
func (rp *ResponseParser) Parse(buf io.ReadCloser, statusCode int, queries []Query) *backend.QueryDataResponse {
|
||||
return rp.parse(buf, statusCode, queries)
|
||||
func ResponseParse(buf io.ReadCloser, statusCode int, query *models.Query) *backend.DataResponse {
|
||||
return parse(buf, statusCode, query)
|
||||
}
|
||||
|
||||
// parse is the same as Parse, but without the io.ReadCloser (we don't need to
|
||||
// close the buffer)
|
||||
func (*ResponseParser) parse(buf io.Reader, statusCode int, queries []Query) *backend.QueryDataResponse {
|
||||
resp := backend.NewQueryDataResponse()
|
||||
func parse(buf io.Reader, statusCode int, query *models.Query) *backend.DataResponse {
|
||||
response, jsonErr := parseJSON(buf)
|
||||
|
||||
if statusCode/100 != 2 {
|
||||
resp.Responses["A"] = backend.DataResponse{Error: fmt.Errorf("InfluxDB returned error: %s", response.Error)}
|
||||
return &backend.DataResponse{Error: fmt.Errorf("InfluxDB returned error: %s", response.Error)}
|
||||
}
|
||||
|
||||
if jsonErr != nil {
|
||||
resp.Responses["A"] = backend.DataResponse{Error: jsonErr}
|
||||
return resp
|
||||
return &backend.DataResponse{Error: jsonErr}
|
||||
}
|
||||
|
||||
if response.Error != "" {
|
||||
resp.Responses["A"] = backend.DataResponse{Error: fmt.Errorf(response.Error)}
|
||||
return resp
|
||||
return &backend.DataResponse{Error: fmt.Errorf(response.Error)}
|
||||
}
|
||||
|
||||
for i, result := range response.Results {
|
||||
if result.Error != "" {
|
||||
resp.Responses[queries[i].RefID] = backend.DataResponse{Error: fmt.Errorf(result.Error)}
|
||||
} else {
|
||||
resp.Responses[queries[i].RefID] = backend.DataResponse{Frames: transformRows(result.Series, queries[i])}
|
||||
}
|
||||
result := response.Results[0]
|
||||
if result.Error != "" {
|
||||
return &backend.DataResponse{Error: fmt.Errorf(result.Error)}
|
||||
} else {
|
||||
return &backend.DataResponse{Frames: transformRows(result.Series, *query)}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func parseJSON(buf io.Reader) (Response, error) {
|
||||
var response Response
|
||||
func parseJSON(buf io.Reader) (models.Response, error) {
|
||||
var response models.Response
|
||||
|
||||
dec := json.NewDecoder(buf)
|
||||
dec.UseNumber()
|
||||
@ -65,7 +59,7 @@ func parseJSON(buf io.Reader) (Response, error) {
|
||||
return response, err
|
||||
}
|
||||
|
||||
func transformRows(rows []Row, query Query) data.Frames {
|
||||
func transformRows(rows []models.Row, query models.Query) data.Frames {
|
||||
// pre-allocate frames - this can save many allocations
|
||||
cols := 0
|
||||
for _, row := range rows {
|
||||
@ -106,7 +100,7 @@ func transformRows(rows []Row, query Query) data.Frames {
|
||||
return frames
|
||||
}
|
||||
|
||||
func newFrameWithTimeField(row Row, column string, colIndex int, query Query, frameName []byte) *data.Frame {
|
||||
func newFrameWithTimeField(row models.Row, column string, colIndex int, query models.Query, frameName []byte) *data.Frame {
|
||||
var timeArray []time.Time
|
||||
var floatArray []*float64
|
||||
var stringArray []*string
|
||||
@ -164,7 +158,7 @@ func newFrameWithTimeField(row Row, column string, colIndex int, query Query, fr
|
||||
return newDataFrame(name, query.RawQuery, timeField, valueField)
|
||||
}
|
||||
|
||||
func newFrameWithoutTimeField(row Row, retentionPolicyQuery bool, tagValuesQuery bool) *data.Frame {
|
||||
func newFrameWithoutTimeField(row models.Row, retentionPolicyQuery bool, tagValuesQuery bool) *data.Frame {
|
||||
var values []string
|
||||
|
||||
if retentionPolicyQuery {
|
||||
@ -213,7 +207,7 @@ func newDataFrame(name string, queryString string, timeField *data.Field, valueF
|
||||
return frame
|
||||
}
|
||||
|
||||
func formatFrameName(row Row, column string, query Query, frameName []byte) []byte {
|
||||
func formatFrameName(row models.Row, column string, query models.Query, frameName []byte) []byte {
|
||||
if query.Alias == "" {
|
||||
return buildFrameNameFromQuery(row, column, frameName)
|
||||
}
|
||||
@ -253,7 +247,7 @@ func formatFrameName(row Row, column string, query Query, frameName []byte) []by
|
||||
return result
|
||||
}
|
||||
|
||||
func buildFrameNameFromQuery(row Row, column string, frameName []byte) []byte {
|
||||
func buildFrameNameFromQuery(row models.Row, column string, frameName []byte) []byte {
|
||||
frameName = append(frameName, row.Name...)
|
||||
frameName = append(frameName, '.')
|
||||
frameName = append(frameName, column...)
|
||||
@ -329,10 +323,10 @@ func parseNumber(value interface{}) *float64 {
|
||||
return &fvalue
|
||||
}
|
||||
|
||||
func isTagValuesQuery(query Query) bool {
|
||||
func isTagValuesQuery(query models.Query) bool {
|
||||
return strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW TAG VALUES"))
|
||||
}
|
||||
|
||||
func isRetentionPolicyQuery(query Query) bool {
|
||||
func isRetentionPolicyQuery(query models.Query) bool {
|
||||
return strings.Contains(strings.ToLower(query.RawQuery), strings.ToLower("SHOW RETENTION POLICIES"))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package influxql
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
@ -6,24 +6,24 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
)
|
||||
|
||||
//go:embed testdata/response.json
|
||||
var testResponse string
|
||||
|
||||
// go test -benchmem -run=^$ -memprofile memprofile.out -count=10 -bench ^BenchmarkParseJson$ github.com/grafana/grafana/pkg/tsdb/influxdb
|
||||
// go test -benchmem -run=^$ -memprofile memprofile.out -count=10 -bench ^BenchmarkParseJson$ github.com/grafana/grafana/pkg/tsdb/influxdb/influxql
|
||||
// go tool pprof -http=localhost:9999 memprofile.out
|
||||
func BenchmarkParseJson(b *testing.B) {
|
||||
parser := &ResponseParser{}
|
||||
query := &Query{}
|
||||
queries := addQueryToQueries(*query)
|
||||
query := generateQuery(models.Query{})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
buf := strings.NewReader(testResponse)
|
||||
result := parser.parse(buf, 200, queries)
|
||||
require.NotNil(b, result.Responses["A"].Frames)
|
||||
require.NoError(b, result.Responses["A"].Error)
|
||||
result := parse(buf, 200, query)
|
||||
require.NotNil(b, result.Frames)
|
||||
require.NoError(b, result.Error)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package influxql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -20,31 +21,29 @@ func prepare(text string) io.ReadCloser {
|
||||
return io.NopCloser(strings.NewReader(text))
|
||||
}
|
||||
|
||||
func addQueryToQueries(query Query) []Query {
|
||||
var queries []Query
|
||||
query.RefID = "A"
|
||||
query.RawQuery = "Test raw query"
|
||||
queries = append(queries, query)
|
||||
return queries
|
||||
func generateQuery(query models.Query) *models.Query {
|
||||
if query.RefID == "" {
|
||||
query.RefID = "A"
|
||||
}
|
||||
if query.RawQuery == "" {
|
||||
query.RawQuery = "Test raw query"
|
||||
}
|
||||
return &query
|
||||
}
|
||||
|
||||
func TestInfluxdbResponseParser(t *testing.T) {
|
||||
t.Run("Influxdb response parser should handle invalid JSON", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `{ invalid }`
|
||||
|
||||
query := &Query{}
|
||||
query := models.Query{}
|
||||
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
require.Nil(t, result.Responses["A"].Frames)
|
||||
require.Error(t, result.Responses["A"].Error)
|
||||
require.Nil(t, result.Frames)
|
||||
require.Error(t, result.Error)
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser should parse everything normally including nil bools and nil strings", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -66,7 +65,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
query := models.Query{}
|
||||
labels, err := data.LabelsFromString("datacenter=America")
|
||||
require.Nil(t, err)
|
||||
|
||||
@ -118,23 +117,20 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
)
|
||||
boolFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["A"]
|
||||
if diff := cmp.Diff(floatFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(floatFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(stringFrame, frame.Frames[1], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(stringFrame, result.Frames[1], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(boolFrame, frame.Frames[2], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(boolFrame, result.Frames[2], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser should parse metricFindQueries normally", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -155,8 +151,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
var queries []Query
|
||||
queries = append(queries, Query{RefID: "metricFindQuery"})
|
||||
query := models.Query{RefID: "metricFindQuery"}
|
||||
newField := data.NewField("Value", nil, []string{
|
||||
"cpu", "disk", "logs",
|
||||
})
|
||||
@ -164,17 +159,14 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
newField,
|
||||
)
|
||||
|
||||
result := parser.Parse(prepare(response), 200, queries)
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["metricFindQuery"]
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser should parse metricFindQueries->SHOW TAG VALUES normally", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -194,8 +186,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
var queries []Query
|
||||
queries = append(queries, Query{RawQuery: "SHOW TAG VALUES", RefID: "metricFindQuery"})
|
||||
query := models.Query{RawQuery: "SHOW TAG VALUES", RefID: "metricFindQuery"}
|
||||
newField := data.NewField("Value", nil, []string{
|
||||
"cpu-total", "cpu0", "cpu1",
|
||||
})
|
||||
@ -203,46 +194,14 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
newField,
|
||||
)
|
||||
|
||||
result := parser.Parse(prepare(response), 200, queries)
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["metricFindQuery"]
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser should parse two responses with different refIDs", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"series": [{}]
|
||||
},
|
||||
{
|
||||
"series": [{}]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
var queries = addQueryToQueries(*query)
|
||||
queryB := &Query{}
|
||||
queryB.RefID = "B"
|
||||
queries = append(queries, *queryB)
|
||||
result := parser.Parse(prepare(response), 200, queries)
|
||||
|
||||
assert.Len(t, result.Responses, 2)
|
||||
assert.Contains(t, result.Responses, "A")
|
||||
assert.Contains(t, result.Responses, "B")
|
||||
assert.NotContains(t, result.Responses, "C")
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser populates the RawQuery in the response meta ExecutedQueryString", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -263,17 +222,14 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
query := models.Query{}
|
||||
query.RawQuery = "Test raw query"
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["A"]
|
||||
assert.Equal(t, frame.Frames[0].Meta.ExecutedQueryString, "Test raw query")
|
||||
assert.Equal(t, result.Frames[0].Meta.ExecutedQueryString, "Test raw query")
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser with invalid value-format", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -294,7 +250,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
query := models.Query{}
|
||||
|
||||
newField := data.NewField("Value", nil, []*float64{
|
||||
util.Pointer(50.0), nil, util.Pointer(52.0),
|
||||
@ -311,17 +267,14 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
)
|
||||
testFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["A"]
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser with invalid timestamp-format", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -343,7 +296,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
query := models.Query{}
|
||||
|
||||
newField := data.NewField("Value", nil, []*float64{
|
||||
util.Pointer(50.0), util.Pointer(52.0),
|
||||
@ -359,17 +312,14 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
)
|
||||
testFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["A"]
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser with alias", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
@ -395,7 +345,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{Alias: "series alias"}
|
||||
query := models.Query{Alias: "series alias"}
|
||||
labels, err := data.LabelsFromString("/cluster/name/=Cluster/, @cluster@name@=Cluster@, cluster-name=Cluster, datacenter=America, dc.region.name=Northeast")
|
||||
require.Nil(t, err)
|
||||
newField := data.NewField("Value", labels, []*float64{
|
||||
@ -410,31 +360,28 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
newField,
|
||||
)
|
||||
testFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
t.Run("should parse aliases", func(t *testing.T) {
|
||||
frame := result.Responses["A"]
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $m $measurement", Measurement: "10m"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
query = models.Query{Alias: "alias $m $measurement", Measurement: "10m"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame = result.Responses["A"]
|
||||
name := "alias 10m 10m"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $col", Measurement: "10m"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $col", Measurement: "10m"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias mean"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
name = "alias sum"
|
||||
@ -444,13 +391,12 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
})
|
||||
testFrame.Fields[1] = newField
|
||||
testFrame.Fields[1].Config = &data.FieldConfig{DisplayNameFromDS: name}
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[1], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[1], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $tag_datacenter"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $tag_datacenter"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias America"
|
||||
testFrame.Name = name
|
||||
newField = data.NewField("Value", labels, []*float64{
|
||||
@ -458,13 +404,12 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
})
|
||||
testFrame.Fields[1] = newField
|
||||
testFrame.Fields[1].Config = &data.FieldConfig{DisplayNameFromDS: name}
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $tag_datacenter/$tag_datacenter"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $tag_datacenter/$tag_datacenter"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias America/America"
|
||||
testFrame.Name = name
|
||||
newField = data.NewField("Value", labels, []*float64{
|
||||
@ -472,183 +417,152 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
})
|
||||
testFrame.Fields[1] = newField
|
||||
testFrame.Fields[1].Config = &data.FieldConfig{DisplayNameFromDS: name}
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[col]]", Measurement: "10m"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[col]]", Measurement: "10m"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias mean"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $0 $1 $2 $3 $4"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $0 $1 $2 $3 $4"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias cpu upc $2 $3 $4"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $0, $1 - $2 - $3, $4: something"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $0, $1 - $2 - $3, $4: something"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias cpu, upc - $2 - $3, $4: something"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $1"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $1"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias upc"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias $5"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias $5"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias $5"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "series alias"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "series alias"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "series alias"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[m]] [[measurement]]", Measurement: "10m"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[m]] [[measurement]]", Measurement: "10m"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias 10m 10m"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[tag_datacenter]]"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[tag_datacenter]]"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias America"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[tag_dc.region.name]]"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[tag_dc.region.name]]"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias Northeast"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[tag_cluster-name]]"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[tag_cluster-name]]"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias Cluster"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[tag_/cluster/name/]]"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[tag_/cluster/name/]]"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias Cluster/"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias [[tag_@cluster@name@]]"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias [[tag_@cluster@name@]]"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias Cluster@"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
t.Run("shouldn't parse aliases", func(t *testing.T) {
|
||||
query = &Query{Alias: "alias words with no brackets"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame := result.Responses["A"]
|
||||
query = models.Query{Alias: "alias words with no brackets"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name := "alias words with no brackets"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias Test 1.5"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias Test 1.5"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias Test 1.5"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
query = &Query{Alias: "alias Test -1"}
|
||||
result = parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
frame = result.Responses["A"]
|
||||
query = models.Query{Alias: "alias Test -1"}
|
||||
result = ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
name = "alias Test -1"
|
||||
testFrame.Name = name
|
||||
testFrame.Fields[1].Config.DisplayNameFromDS = name
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(testFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser with errors", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"name": "cpu",
|
||||
"columns": ["time","mean","sum"],
|
||||
"tags": {"datacenter": "America"},
|
||||
"values": [
|
||||
[111,222,333],
|
||||
[111,222,333],
|
||||
[111,null,333]
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"error": "query-timeout limit exceeded"
|
||||
}
|
||||
@ -656,11 +570,7 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
var queries = addQueryToQueries(*query)
|
||||
queryB := &Query{}
|
||||
queryB.RefID = "B"
|
||||
queries = append(queries, *queryB)
|
||||
query := models.Query{}
|
||||
labels, err := data.LabelsFromString("datacenter=America")
|
||||
require.Nil(t, err)
|
||||
newField := data.NewField("Value", labels, []*float64{
|
||||
@ -677,32 +587,25 @@ func TestInfluxdbResponseParser(t *testing.T) {
|
||||
newField,
|
||||
)
|
||||
testFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
result := parser.Parse(prepare(response), 200, queries)
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["A"]
|
||||
if diff := cmp.Diff(testFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
require.EqualError(t, result.Responses["B"].Error, "query-timeout limit exceeded")
|
||||
require.EqualError(t, result.Error, "query-timeout limit exceeded")
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser with top-level error", func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
|
||||
response := `
|
||||
{
|
||||
"error": "error parsing query: found THING"
|
||||
}
|
||||
`
|
||||
|
||||
query := &Query{}
|
||||
query := models.Query{}
|
||||
|
||||
result := parser.Parse(prepare(response), 200, addQueryToQueries(*query))
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
require.Nil(t, result.Responses["A"].Frames)
|
||||
require.Nil(t, result.Frames)
|
||||
|
||||
require.EqualError(t, result.Responses["A"].Error, "error parsing query: found THING")
|
||||
require.EqualError(t, result.Error, "error parsing query: found THING")
|
||||
})
|
||||
|
||||
t.Run("Influxdb response parser parseNumber nil", func(t *testing.T) {
|
||||
@ -736,8 +639,6 @@ 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": [
|
||||
@ -789,18 +690,16 @@ func TestResponseParser_Parse_RetentionPolicy(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
var queries []Query
|
||||
queries = append(queries, Query{RefID: "metricFindQuery", RawQuery: "SHOW RETENTION POLICIES"})
|
||||
query := models.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), 200, queries)
|
||||
result := ResponseParse(prepare(response), 200, generateQuery(query))
|
||||
|
||||
frame := result.Responses["metricFindQuery"]
|
||||
if diff := cmp.Diff(policyFrame, frame.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
if diff := cmp.Diff(policyFrame, result.Frames[0], data.FrameTestCompareOptions()...); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
@ -810,7 +709,7 @@ func TestResponseParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
f func(t *testing.T, got *backend.QueryDataResponse)
|
||||
f func(t *testing.T, got backend.DataResponse)
|
||||
}{
|
||||
{
|
||||
name: "Influxdb response parser with valid value when null values returned",
|
||||
@ -823,7 +722,7 @@ func TestResponseParser_Parse(t *testing.T) {
|
||||
[102,52]
|
||||
]
|
||||
}]}]}`,
|
||||
f: func(t *testing.T, got *backend.QueryDataResponse) {
|
||||
f: func(t *testing.T, got backend.DataResponse) {
|
||||
newField := data.NewField("Value", nil, []*float64{nil, nil, util.Pointer(52.0)})
|
||||
newField.Config = &data.FieldConfig{DisplayNameFromDS: "cpu.mean"}
|
||||
testFrame := data.NewFrame("cpu.mean",
|
||||
@ -836,7 +735,7 @@ func TestResponseParser_Parse(t *testing.T) {
|
||||
newField,
|
||||
)
|
||||
testFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
assert.Equal(t, testFrame, got.Responses["A"].Frames[0])
|
||||
assert.Equal(t, testFrame, got.Frames[0])
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -850,7 +749,7 @@ func TestResponseParser_Parse(t *testing.T) {
|
||||
[102,null]
|
||||
]
|
||||
}]}]}`,
|
||||
f: func(t *testing.T, got *backend.QueryDataResponse) {
|
||||
f: func(t *testing.T, got backend.DataResponse) {
|
||||
newField := data.NewField("Value", nil, []*float64{nil, nil, nil})
|
||||
newField.Config = &data.FieldConfig{DisplayNameFromDS: "cpu.mean"}
|
||||
testFrame := data.NewFrame("cpu.mean",
|
||||
@ -863,17 +762,16 @@ func TestResponseParser_Parse(t *testing.T) {
|
||||
newField,
|
||||
)
|
||||
testFrame.Meta = &data.FrameMeta{ExecutedQueryString: "Test raw query"}
|
||||
assert.Equal(t, testFrame, got.Responses["A"].Frames[0])
|
||||
assert.Equal(t, testFrame, got.Frames[0])
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
parser := &ResponseParser{}
|
||||
got := parser.Parse(prepare(tt.input), 200, addQueryToQueries(Query{}))
|
||||
got := ResponseParse(prepare(tt.input), 200, generateQuery(models.Query{}))
|
||||
require.NotNil(t, got)
|
||||
if tt.f != nil {
|
||||
tt.f(t, got)
|
||||
tt.f(t, *got)
|
||||
}
|
||||
})
|
||||
}
|
@ -113,8 +113,6 @@ func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
|
||||
func GetMockService(version string, rt RoundTripper) *Service {
|
||||
return &Service{
|
||||
queryParser: &InfluxdbQueryParser{},
|
||||
responseParser: &ResponseParser{},
|
||||
im: &fakeInstance{
|
||||
version: version,
|
||||
fakeRoundTripper: rt,
|
||||
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -12,7 +12,7 @@ import (
|
||||
|
||||
type InfluxdbQueryParser struct{}
|
||||
|
||||
func (qp *InfluxdbQueryParser) Parse(query backend.DataQuery) (*Query, error) {
|
||||
func QueryParse(query backend.DataQuery) (*Query, error) {
|
||||
model, err := simplejson.NewJson(query.JSON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't unmarshal query")
|
||||
@ -29,17 +29,17 @@ func (qp *InfluxdbQueryParser) Parse(query backend.DataQuery) (*Query, error) {
|
||||
|
||||
measurement := model.Get("measurement").MustString("")
|
||||
|
||||
tags, err := qp.parseTags(model)
|
||||
tags, err := parseTags(model)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupBys, err := qp.parseGroupBy(model)
|
||||
groupBys, err := parseGroupBy(model)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selects, err := qp.parseSelects(model)
|
||||
selects, err := parseSelects(model)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -70,7 +70,7 @@ func (qp *InfluxdbQueryParser) Parse(query backend.DataQuery) (*Query, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (qp *InfluxdbQueryParser) parseSelects(model *simplejson.Json) ([]*Select, error) {
|
||||
func parseSelects(model *simplejson.Json) ([]*Select, error) {
|
||||
selectObjs := model.Get("select").MustArray()
|
||||
result := make([]*Select, 0, len(selectObjs))
|
||||
|
||||
@ -80,7 +80,7 @@ func (qp *InfluxdbQueryParser) parseSelects(model *simplejson.Json) ([]*Select,
|
||||
|
||||
for _, partObj := range selectJson.MustArray() {
|
||||
part := simplejson.NewFromAny(partObj)
|
||||
queryPart, err := qp.parseQueryPart(part)
|
||||
queryPart, err := parseQueryPart(part)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -94,7 +94,7 @@ func (qp *InfluxdbQueryParser) parseSelects(model *simplejson.Json) ([]*Select,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (*InfluxdbQueryParser) parseTags(model *simplejson.Json) ([]*Tag, error) {
|
||||
func parseTags(model *simplejson.Json) ([]*Tag, error) {
|
||||
tags := model.Get("tags").MustArray()
|
||||
result := make([]*Tag, 0, len(tags))
|
||||
for _, t := range tags {
|
||||
@ -128,7 +128,7 @@ func (*InfluxdbQueryParser) parseTags(model *simplejson.Json) ([]*Tag, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (*InfluxdbQueryParser) parseQueryPart(model *simplejson.Json) (*QueryPart, error) {
|
||||
func parseQueryPart(model *simplejson.Json) (*QueryPart, error) {
|
||||
typ, err := model.Get("type").String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -161,12 +161,12 @@ func (*InfluxdbQueryParser) parseQueryPart(model *simplejson.Json) (*QueryPart,
|
||||
return qp, nil
|
||||
}
|
||||
|
||||
func (qp *InfluxdbQueryParser) parseGroupBy(model *simplejson.Json) ([]*QueryPart, error) {
|
||||
func parseGroupBy(model *simplejson.Json) ([]*QueryPart, error) {
|
||||
groupBy := model.Get("groupBy").MustArray()
|
||||
result := make([]*QueryPart, 0, len(groupBy))
|
||||
for _, groupObj := range groupBy {
|
||||
groupJson := simplejson.NewFromAny(groupObj)
|
||||
queryPart, err := qp.parseQueryPart(groupJson)
|
||||
queryPart, err := parseQueryPart(groupJson)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@ -9,8 +9,6 @@ import (
|
||||
)
|
||||
|
||||
func TestInfluxdbQueryParser_Parse(t *testing.T) {
|
||||
parser := &InfluxdbQueryParser{}
|
||||
|
||||
t.Run("can parse influxdb json model", func(t *testing.T) {
|
||||
json := `
|
||||
{
|
||||
@ -110,7 +108,7 @@ func TestInfluxdbQueryParser_Parse(t *testing.T) {
|
||||
Interval: time.Second * 20,
|
||||
}
|
||||
|
||||
res, err := parser.Parse(query)
|
||||
res, err := QueryParse(query)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.GroupBy, 3)
|
||||
require.Len(t, res.Selects, 3)
|
||||
@ -173,7 +171,7 @@ func TestInfluxdbQueryParser_Parse(t *testing.T) {
|
||||
Interval: time.Second * 10,
|
||||
}
|
||||
|
||||
res, err := parser.Parse(query)
|
||||
res, err := QueryParse(query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "RawDummyQuery", res.RawQuery)
|
||||
require.Len(t, res.GroupBy, 2)
|
||||
@ -196,7 +194,7 @@ func TestInfluxdbQueryParser_Parse(t *testing.T) {
|
||||
Interval: time.Millisecond * 0,
|
||||
}
|
||||
|
||||
res, err := parser.Parse(query)
|
||||
res, err := QueryParse(query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, time.Millisecond*1, res.Interval)
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import (
|
||||
"testing"
|
@ -1,4 +1,4 @@
|
||||
package influxdb
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
Loading…
Reference in New Issue
Block a user