diff --git a/CHANGELOG.md b/CHANGELOG.md index e05aa7ae..493c4518 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## tip +* BUGFIX: correctly handle custom query parameters in annotation queries. See [this issue](https://github.com/VictoriaMetrics/grafana-datasource/issues/95) + ## [v0.3.0](https://github.com/VictoriaMetrics/grafana-datasource/releases/tag/v0.3.0) * FEATURE: Improvements to WITH Templates (see [this comment](https://github.com/VictoriaMetrics/grafana-datasource/issues/35#issuecomment-1578649762)): diff --git a/pkg/plugin/datasource.go b/pkg/plugin/datasource.go index 337126ad..2043bce6 100644 --- a/pkg/plugin/datasource.go +++ b/pkg/plugin/datasource.go @@ -88,19 +88,21 @@ func (d *Datasource) query(ctx context.Context, query backend.DataQuery) backend return newResponseError(err, backend.StatusBadRequest) } - reqURL, err := q.getQueryURL(minInterval, d.settings.URL) - if err != nil { - err = fmt.Errorf("failed to create request url: %w", err) - return newResponseError(err, backend.StatusBadRequest) - } - httpMethod := http.MethodPost var settingsData struct { - HTTPMethod string `json:"httpMethod"` + HTTPMethod string `json:"httpMethod"` + CustomQueryParameters string `json:"customQueryParameters"` } if err := json.Unmarshal(d.settings.JSONData, &settingsData); err == nil && settingsData.HTTPMethod != "" { httpMethod = settingsData.HTTPMethod } + customQueryParameters := settingsData.CustomQueryParameters + + reqURL, err := q.getQueryURL(minInterval, d.settings.URL, customQueryParameters) + if err != nil { + err = fmt.Errorf("failed to create request url: %w", err) + return newResponseError(err, backend.StatusBadRequest) + } req, err := http.NewRequestWithContext(ctx, httpMethod, reqURL, nil) if err != nil { diff --git a/pkg/plugin/query.go b/pkg/plugin/query.go index 1a3bdda0..a5c8502b 100644 --- a/pkg/plugin/query.go +++ b/pkg/plugin/query.go @@ -7,6 +7,8 @@ import ( "strconv" "strings" "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend/log" ) const ( @@ -37,7 +39,7 @@ type TimeRange struct { // GetQueryURL calculates step and clear expression from template variables, // and after builds query url depends on query type -func (q *Query) getQueryURL(minInterval time.Duration, rawURL string) (string, error) { +func (q *Query) getQueryURL(minInterval time.Duration, rawURL string, customQueryParams string) (string, error) { if rawURL == "" { return "", fmt.Errorf("url can't be blank") } @@ -59,9 +61,9 @@ func (q *Query) getQueryURL(minInterval time.Duration, rawURL string) (string, e } if q.Instant { - return q.queryInstantURL(expr, step), nil + return q.queryInstantURL(expr, step, customQueryParams), nil } - return q.queryRangeURL(expr, step), nil + return q.queryRangeURL(expr, step, customQueryParams), nil } // withIntervalVariable checks does query has interval variable @@ -79,20 +81,21 @@ func (q *Query) calculateMinInterval() (time.Duration, error) { } // queryInstantURL prepare query url for instant query -func (q *Query) queryInstantURL(expr string, step time.Duration) string { +func (q *Query) queryInstantURL(expr string, step time.Duration, customQueryParams string) string { q.url.Path = path.Join(q.url.Path, instantQueryPath) values := q.url.Query() values.Add("query", expr) values.Add("time", strconv.FormatInt(q.TimeRange.To.Unix(), 10)) values.Add("step", step.String()) + addCustomParams(values, customQueryParams) q.url.RawQuery = values.Encode() return q.url.String() } // queryRangeURL prepare query url for range query -func (q *Query) queryRangeURL(expr string, step time.Duration) string { +func (q *Query) queryRangeURL(expr string, step time.Duration, customQueryParams string) string { q.url.Path = path.Join(q.url.Path, rangeQueryPath) values := q.url.Query() @@ -100,6 +103,7 @@ func (q *Query) queryRangeURL(expr string, step time.Duration) string { values.Add("start", strconv.FormatInt(q.TimeRange.From.Unix(), 10)) values.Add("end", strconv.FormatInt(q.TimeRange.To.Unix(), 10)) values.Add("step", step.String()) + addCustomParams(values, customQueryParams) q.url.RawQuery = values.Encode() return q.url.String() @@ -115,3 +119,16 @@ func (q *Query) parseLegend() string { } return legend } + +func addCustomParams(values url.Values, customQueryParams string) url.Values { + params, err := url.ParseQuery(customQueryParams) + if err != nil { + log.DefaultLogger.Error("failed to parse custom query params", "err", err.Error()) + } + for key, valueList := range params { + for _, value := range valueList { + values.Add(key, value) + } + } + return values +} diff --git a/pkg/plugin/query_test.go b/pkg/plugin/query_test.go index ae80104f..1b8ae3ec 100644 --- a/pkg/plugin/query_test.go +++ b/pkg/plugin/query_test.go @@ -150,7 +150,7 @@ func TestQuery_getQueryURL(t *testing.T) { TimeRange: tt.fields.getTimeRange(), url: tt.fields.url, } - got, err := q.getQueryURL(tt.args.minInterval, tt.args.rawURL) + got, err := q.getQueryURL(tt.args.minInterval, tt.args.rawURL, "") if (err != nil) != tt.wantErr { t.Errorf("getQueryURL() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/src/configuration/PromSettings.tsx b/src/configuration/PromSettings.tsx index 18aecf7a..d5e839a2 100755 --- a/src/configuration/PromSettings.tsx +++ b/src/configuration/PromSettings.tsx @@ -128,7 +128,7 @@ export const PromSettings = (props: Props) => {