Skip to content

Commit

Permalink
Merge pull request #29 from VictoriaMetrics/issue-23
Browse files Browse the repository at this point in the history
improve response from backend when there are different fields returns
  • Loading branch information
hagen1778 authored Jun 24, 2024
2 parents 2f87098 + c7b2c77 commit 542fee9
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## tip

* BUGFIX: fix bug with displaying responses with a custom set of fields. See [this issue](https://github.com/VictoriaMetrics/victorialogs-datasource/issues/23).

## v0.2.1

* BUGFIX: change the `metrics` flag from `false` to `true` in `plugin.json` to ensure the plugin appears in the Grafana datasource selection list.
Expand Down
14 changes: 14 additions & 0 deletions pkg/plugin/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"time"

"github.com/VictoriaMetrics/metricsql"
"github.com/grafana/grafana-plugin-sdk-go/backend"
Expand Down Expand Up @@ -84,6 +85,19 @@ func parseStreamResponse(reader io.Reader) backend.DataResponse {
return newResponseError(err, backend.StatusInternal)
}
labelsField.Append(d)

// Grafana expects lineField to be always non-empty.
if timeFd.Len() == 0 {
lineField.Append(string(d))
}
}

// Grafana expects time field to be always non-empty.
if timeFd.Len() == 0 {
now := time.Now()
for i := 0; i < lineField.Len(); i++ {
timeFd.Append(now)
}
}

frame := data.NewFrame("", timeFd, lineField, labelsField)
Expand Down
108 changes: 106 additions & 2 deletions pkg/plugin/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,77 @@ func Test_parseStreamResponse(t *testing.T) {
frame.Meta = &data.FrameMeta{}
rsp.Frames = append(rsp.Frames, frame)

return rsp
},
},
{
name: "response with different labels and without standard fields",
response: `{"stream":"stderr","count(*)":"394"}
{"stream":"stdout","count(*)":"21"}`,
want: func() backend.DataResponse {
labelsField := data.NewFieldFromFieldType(data.FieldTypeJSON, 0)
labelsField.Name = gLabelsField

timeFd := data.NewFieldFromFieldType(data.FieldTypeTime, 0)
timeFd.Name = gTimeField

lineField := data.NewFieldFromFieldType(data.FieldTypeString, 0)
lineField.Name = gLineField

lineField.Append(`{"count(*)":"394","stream":"stderr"}`)
lineField.Append(`{"count(*)":"21","stream":"stdout"}`)

labels := data.Labels{
"count(*)": "394",
"stream": "stderr",
}

b, _ := labelsToJSON(labels)
labelsField.Append(b)

labels = data.Labels{
"count(*)": "21",
"stream": "stdout",
}
b, _ = labelsToJSON(labels)
labelsField.Append(b)
frame := data.NewFrame("", timeFd, lineField, labelsField)

rsp := backend.DataResponse{}
frame.Meta = &data.FrameMeta{}
rsp.Frames = append(rsp.Frames, frame)

return rsp
},
},
{
name: "response with different labels only one label",
response: `{"level":""}`,
want: func() backend.DataResponse {
labelsField := data.NewFieldFromFieldType(data.FieldTypeJSON, 0)
labelsField.Name = gLabelsField

timeFd := data.NewFieldFromFieldType(data.FieldTypeTime, 0)
timeFd.Name = gTimeField

lineField := data.NewFieldFromFieldType(data.FieldTypeString, 0)
lineField.Name = gLineField

lineField.Append(`{"level":""}`)

labels := data.Labels{
"level": "",
}

b, _ := labelsToJSON(labels)
labelsField.Append(b)

frame := data.NewFrame("", timeFd, lineField, labelsField)

rsp := backend.DataResponse{}
frame.Meta = &data.FrameMeta{}
rsp.Frames = append(rsp.Frames, frame)

return rsp
},
},
Expand All @@ -135,8 +206,41 @@ func Test_parseStreamResponse(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := io.NopCloser(bytes.NewBuffer([]byte(tt.response)))
w := tt.want()
if got := parseStreamResponse(r); !reflect.DeepEqual(got, w) {
t.Errorf("parseStreamResponse() = %#v, want %#v", got, w)
resp := parseStreamResponse(r)
if w.Error != nil {
if !reflect.DeepEqual(w, resp) {
t.Errorf("parseStreamResponse() = %#v, want %#v", resp, w)
}
return
}

if len(resp.Frames) != 1 {
t.Fatalf("expected for response to always contain 1 Frame; got %d", len(resp.Frames))
}
got := resp.Frames[0]
want := w.Frames[0]
expFieldsLen := got.Fields[0].Len()
for j, field := range want.Fields {
// if time field is empty, fill it with the value from the response
// because time field in the parseStreamResponse generated as time.Now()
if field.Name == gTimeField && field.Len() == 0 {
for _, f := range got.Fields {
if f.Name == gTimeField {
want.Fields[j] = f
}
}
}

// all fields within response should have equal length
gf := got.Fields[j]
if gf.Len() != expFieldsLen {
t.Fatalf("expected all fields to have equal length %d; got %d instead for field %q",
expFieldsLen, gf.Len(), gf.Name)
}
}

if !reflect.DeepEqual(got, want) {
t.Errorf("parseStreamResponse() = %#v, want %#v", got, want)
}
})
}
Expand Down

0 comments on commit 542fee9

Please sign in to comment.