diff --git a/pkg/tsdb/testdatasource/scenarios.go b/pkg/tsdb/testdatasource/scenarios.go index c3a93296e3e..2ee3ab93138 100644 --- a/pkg/tsdb/testdatasource/scenarios.go +++ b/pkg/tsdb/testdatasource/scenarios.go @@ -285,42 +285,40 @@ func (p *testDataPlugin) handleManualEntryScenario(ctx context.Context, req *bac for _, q := range req.Queries { model, err := simplejson.NewJson(q.JSON) if err != nil { - continue + return nil, fmt.Errorf("error reading query") } points := model.Get("points").MustArray() frame := newSeriesForQuery(q, model, 0) - startTime := q.TimeRange.From.UnixNano() / int64(time.Millisecond) - endTime := q.TimeRange.To.UnixNano() / int64(time.Millisecond) - timeVec := make([]*time.Time, 0) - floatVec := make([]*float64, 0) + timeField := data.NewFieldFromFieldType(data.FieldTypeTime, 0) + valueField := data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, 0) + timeField.Name = "time" + valueField.Name = "value" for _, val := range points { pointValues := val.([]interface{}) - var value float64 + var value *float64 - if valueFloat, err := strconv.ParseFloat(string(pointValues[0].(json.Number)), 64); err == nil { - value = valueFloat + if pointValues[0] != nil { + if valueFloat, err := strconv.ParseFloat(string(pointValues[0].(json.Number)), 64); err == nil { + value = &valueFloat + } } timeInt, err := strconv.ParseInt(string(pointValues[1].(json.Number)), 10, 64) if err != nil { continue } + t := time.Unix(timeInt/int64(1e+3), (timeInt%int64(1e+3))*int64(1e+6)) - if timeInt >= startTime && timeInt <= endTime { - timeVec = append(timeVec, &t) - floatVec = append(floatVec, &value) - } + timeField.Append(t) + valueField.Append(value) } - frame.Fields = data.Fields{ - data.NewField("time", nil, timeVec), - data.NewField("value", nil, floatVec), - } + frame.Fields = data.Fields{timeField, valueField} respD := resp.Responses[q.RefID] respD.Frames = append(respD.Frames, frame) diff --git a/pkg/tsdb/testdatasource/scenarios_test.go b/pkg/tsdb/testdatasource/scenarios_test.go index 816f695dc7d..573ee9cc7bb 100644 --- a/pkg/tsdb/testdatasource/scenarios_test.go +++ b/pkg/tsdb/testdatasource/scenarios_test.go @@ -185,6 +185,57 @@ func TestTestdataScenarios(t *testing.T) { require.True(t, maxNil) }) }) + + t.Run("manual entry ", func(t *testing.T) { + t.Run("should support nulls and return all data", func(t *testing.T) { + timeRange := plugins.DataTimeRange{From: "5m", To: "now", Now: time.Now()} + + query := backend.DataQuery{ + RefID: "A", + TimeRange: backend.TimeRange{ + From: timeRange.MustGetFrom(), + To: timeRange.MustGetTo(), + }, + JSON: []byte(`{ "points": [ + [ + 4, 1616557148000 + ], + [ + null, 1616558756000 + ], + [ + 4, 1616561658000 + ]] }`), + } + + req := &backend.QueryDataRequest{ + PluginContext: backend.PluginContext{}, + Queries: []backend.DataQuery{query}, + } + + resp, err := p.handleManualEntryScenario(context.Background(), req) + require.NoError(t, err) + require.NotNil(t, resp) + + dResp, exists := resp.Responses[query.RefID] + require.True(t, exists) + require.NoError(t, dResp.Error) + + require.Len(t, dResp.Frames, 1) + frame := dResp.Frames[0] + require.Len(t, frame.Fields, 2) + require.Equal(t, "time", frame.Fields[0].Name) + require.Equal(t, "value", frame.Fields[1].Name) + require.Equal(t, 3, frame.Rows()) + + vals := frame.Fields[1] + v, _ := vals.ConcreteAt(0) + require.Equal(t, float64(4), v) + require.Nil(t, vals.At(1)) + v, _ = vals.ConcreteAt(2) + require.Equal(t, float64(4), v) + }) + }) } func TestParseLabels(t *testing.T) {