Files
grafana/pkg/tsdb/influxdb/models/model_parser.go
T
beejeebus 9de769318c Add more errorsource attribution to InfluxDb datasource (#100969)
This PR adds errorsource attribution to the influxql and flux query paths
when the query model cannot be parsed, which is a user error.

It also catches cases where the datasource configuration does not
contain a scheme or host, and adds downstream attribution to those
errors.

Error handling on the influxql query path is updated to match 'all errors
are per query, and stashed on the response object' pattern.

Fixes https://github.com/grafana/oss-plugin-partnerships/issues/1250
2025-02-20 11:53:28 -05:00

196 lines
4.3 KiB
Go

package models
import (
"errors"
"fmt"
"strconv"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/influxdata/influxql"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
)
var (
ErrInvalidQuery = errors.New("invalid InfluxDB query")
)
type InfluxdbQueryParser struct{}
func QueryParse(query backend.DataQuery, logger log.Logger) (*Query, error) {
model, err := simplejson.NewJson(query.JSON)
if err != nil {
return nil, fmt.Errorf("couldn't unmarshal query")
}
policy := model.Get("policy").MustString("default")
rawQuery := model.Get("query").MustString("")
useRawQuery := model.Get("rawQuery").MustBool(false)
alias := model.Get("alias").MustString("")
tz := model.Get("tz").MustString("")
limit := model.Get("limit").MustString("")
slimit := model.Get("slimit").MustString("")
orderByTime := model.Get("orderByTime").MustString("")
measurement := model.Get("measurement").MustString("")
resultFormat := model.Get("resultFormat").MustString("")
tags, err := parseTags(model)
if err != nil {
return nil, errors.Join(ErrInvalidQuery, err)
}
groupBys, err := parseGroupBy(model)
if err != nil {
return nil, errors.Join(ErrInvalidQuery, err)
}
selects, err := parseSelects(model)
if err != nil {
return nil, errors.Join(ErrInvalidQuery, err)
}
interval := query.Interval
// we make sure it is at least 1 millisecond
minInterval := time.Millisecond
if interval < minInterval {
interval = minInterval
}
var statement influxql.Statement
if useRawQuery {
statement, err = influxql.ParseStatement(rawQuery)
if err != nil {
logger.Debug(fmt.Sprintf("Couldn't parse raw query: %v", err), "rawQuery", rawQuery)
}
}
return &Query{
Measurement: measurement,
Policy: policy,
GroupBy: groupBys,
Tags: tags,
Selects: selects,
RawQuery: rawQuery,
Interval: interval,
Alias: alias,
UseRawQuery: useRawQuery,
Tz: tz,
Limit: limit,
Slimit: slimit,
OrderByTime: orderByTime,
ResultFormat: resultFormat,
Statement: statement,
}, nil
}
func parseSelects(model *simplejson.Json) ([]*Select, error) {
selectObjs := model.Get("select").MustArray()
result := make([]*Select, 0, len(selectObjs))
for _, selectObj := range selectObjs {
selectJson := simplejson.NewFromAny(selectObj)
var parts Select
for _, partObj := range selectJson.MustArray() {
part := simplejson.NewFromAny(partObj)
queryPart, err := parseQueryPart(part)
if err != nil {
return nil, err
}
parts = append(parts, *queryPart)
}
result = append(result, &parts)
}
return result, nil
}
func parseTags(model *simplejson.Json) ([]*Tag, error) {
tags := model.Get("tags").MustArray()
result := make([]*Tag, 0, len(tags))
for _, t := range tags {
tagJson := simplejson.NewFromAny(t)
tag := &Tag{}
var err error
tag.Key, err = tagJson.Get("key").String()
if err != nil {
return nil, err
}
tag.Value, err = tagJson.Get("value").String()
if err != nil {
return nil, err
}
operator, err := tagJson.Get("operator").String()
if err == nil {
tag.Operator = operator
}
condition, err := tagJson.Get("condition").String()
if err == nil {
tag.Condition = condition
}
result = append(result, tag)
}
return result, nil
}
func parseQueryPart(model *simplejson.Json) (*QueryPart, error) {
typ, err := model.Get("type").String()
if err != nil {
return nil, err
}
var params []string
for _, paramObj := range model.Get("params").MustArray() {
param := simplejson.NewFromAny(paramObj)
stringParam, err := param.String()
if err == nil {
params = append(params, stringParam)
continue
}
intParam, err := param.Int()
if err == nil {
params = append(params, strconv.Itoa(intParam))
continue
}
return nil, err
}
qp, err := NewQueryPart(typ, params)
if err != nil {
return nil, err
}
return qp, nil
}
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 := parseQueryPart(groupJson)
if err != nil {
return nil, err
}
result = append(result, queryPart)
}
return result, nil
}