diff --git a/pkg/tsdb/graphite/query.go b/pkg/tsdb/graphite/query.go index 6f4d2bca083..2ab60b21592 100644 --- a/pkg/tsdb/graphite/query.go +++ b/pkg/tsdb/graphite/query.go @@ -256,8 +256,14 @@ func (s *Service) parseResponse(res *http.Response) ([]TargetResponseDTO, error) var data []TargetResponseDTO err = json.Unmarshal(body, &data) if err != nil { - s.logger.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body)) - return nil, backend.DownstreamError(err) + s.logger.Warn("Failed to unamrshal to newer graphite response, attempting legacy") + var legacyData LegacyTargetResponseDTO + err = json.Unmarshal(body, &legacyData) + if err != nil { + s.logger.Info("Failed to unmarshal legacy graphite response", "error", err, "status", res.Status, "body", string(body)) + return nil, backend.PluginError(err) + } + return legacyData.Series, nil } return data, nil diff --git a/pkg/tsdb/graphite/query_test.go b/pkg/tsdb/graphite/query_test.go index 8af55a156a5..036f5401194 100644 --- a/pkg/tsdb/graphite/query_test.go +++ b/pkg/tsdb/graphite/query_test.go @@ -225,6 +225,70 @@ func TestConvertResponses(t *testing.T) { t.Errorf("Data frames should have been equal but was, expected:\n%s\nactual:\n%s", expectedFramesJSON, dataFramesJSON) } }) + + t.Run("Converts legacy response with no series", func(*testing.T) { + body := ` + { + "version": "v0.1", + "meta": { + "stats": {} + }, + "series": [] + }` + refId := "A" + expectedFrames := data.Frames{} + + httpResponse := &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(body))} + dataFrames, err := service.toDataFrames(httpResponse, refId) + + require.NoError(t, err) + if !reflect.DeepEqual(expectedFrames, dataFrames) { + expectedFramesJSON, _ := json.Marshal(expectedFrames) + dataFramesJSON, _ := json.Marshal(dataFrames) + t.Errorf("Data frames should have been equal but was, expected:\n%s\nactual:\n%s", expectedFramesJSON, dataFramesJSON) + } + }) + + t.Run("Converts legacy response with series", func(*testing.T) { + body := ` + { + "version": "v0.1", + "meta": { + "stats": { + } + }, + "series": [ + { + "target": "target", + "tags": { "fooTag": "fooValue", "barTag": "barValue", "int": 100, "float": 3.14 }, + "datapoints": [[50, 1], [null, 2], [100, 3]] + } + ] + }` + refId := "A" + a := 50.0 + b := 100.0 + expectedFrame := data.NewFrame("A", + data.NewField("time", nil, []time.Time{time.Unix(1, 0).UTC(), time.Unix(2, 0).UTC(), time.Unix(3, 0).UTC()}), + data.NewField("value", data.Labels{ + "fooTag": "fooValue", + "barTag": "barValue", + "int": "100", + "float": "3.14", + }, []*float64{&a, nil, &b}).SetConfig(&data.FieldConfig{DisplayNameFromDS: "target"}), + ).SetMeta(&data.FrameMeta{Type: data.FrameTypeTimeSeriesMulti}) + expectedFrames := data.Frames{expectedFrame} + + httpResponse := &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(body))} + dataFrames, err := service.toDataFrames(httpResponse, refId) + + require.NoError(t, err) + if !reflect.DeepEqual(expectedFrames, dataFrames) { + expectedFramesJSON, _ := json.Marshal(expectedFrames) + dataFramesJSON, _ := json.Marshal(dataFrames) + t.Errorf("Data frames should have been equal but was, expected:\n%s\nactual:\n%s", expectedFramesJSON, dataFramesJSON) + } + }) } func TestFixIntervalFormat(t *testing.T) { diff --git a/pkg/tsdb/graphite/types.go b/pkg/tsdb/graphite/types.go index 11c184b13de..7f836961b54 100644 --- a/pkg/tsdb/graphite/types.go +++ b/pkg/tsdb/graphite/types.go @@ -9,6 +9,12 @@ type TargetResponseDTO struct { Tags map[string]any `json:"tags"` } +type LegacyTargetResponseDTO struct { + Version string `json:"version"` + Meta map[string]any `json:"meta"` + Series []TargetResponseDTO `json:"series"` +} + type DataTimePoint [2]Float type DataTimeSeriesPoints []DataTimePoint