From 3ecd96e68225c3b8ae8cdae10adec1c2f3c3b00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 27 Sep 2016 18:17:39 +0200 Subject: [PATCH] feat(testdata): lots of work on new test data data source and scenarios --- pkg/api/api.go | 3 +- pkg/api/dtos/models.go | 6 +- pkg/api/metrics.go | 50 +++++----- pkg/services/alerting/conditions/query.go | 6 +- pkg/tsdb/models.go | 17 ++-- pkg/tsdb/prometheus/prometheus.go | 6 +- pkg/tsdb/query_context.go | 4 +- pkg/tsdb/testdata/scenarios.go | 98 +++++++++++++++++++ pkg/tsdb/testdata/testdata.go | 39 +++----- pkg/tsdb/time_range.go | 42 ++++++-- pkg/tsdb/time_range_test.go | 20 ++-- pkg/tsdb/tsdb_test.go | 30 +++--- .../app/testdata/datasource/datasource.ts | 32 ++++-- .../app/testdata/datasource/query_ctrl.ts | 17 ++-- .../app/testdata/partials/query.editor.html | 8 +- .../app/plugins/panel/graph/data_processor.ts | 2 +- 16 files changed, 257 insertions(+), 123 deletions(-) create mode 100644 pkg/tsdb/testdata/scenarios.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 38e299d19c8..bac3db429d2 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -244,7 +244,8 @@ func Register(r *macaron.Macaron) { r.Get("/search/", Search) // metrics - r.Get("/metrics/test", wrap(GetTestMetrics)) + r.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics)) + r.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios)) // metrics r.Get("/metrics", wrap(GetInternalMetrics)) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 143ee5b98d5..170a5a868fc 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -96,8 +96,10 @@ func (slice DataSourceList) Swap(i, j int) { slice[i], slice[j] = slice[j], slice[i] } -type MetricQueryResultDto struct { - Data []interface{} `json:"data"` +type MetricRequest struct { + From string `json:"from"` + To string `json:"to"` + Queries []*simplejson.Json `json:"queries"` } type UserStars struct { diff --git a/pkg/api/metrics.go b/pkg/api/metrics.go index c36f2108581..c3bd9062737 100644 --- a/pkg/api/metrics.go +++ b/pkg/api/metrics.go @@ -8,43 +8,47 @@ import ( "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/tsdb" + "github.com/grafana/grafana/pkg/tsdb/testdata" "github.com/grafana/grafana/pkg/util" ) -func GetTestMetrics(c *middleware.Context) Response { +// POST /api/tsdb/query +func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response { + timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To) - timeRange := tsdb.NewTimeRange(c.Query("from"), c.Query("to")) + request := &tsdb.Request{TimeRange: timeRange} - req := &tsdb.Request{ - TimeRange: timeRange, - Queries: []*tsdb.Query{ - { - RefId: "A", - MaxDataPoints: c.QueryInt64("maxDataPoints"), - IntervalMs: c.QueryInt64("intervalMs"), - DataSource: &tsdb.DataSourceInfo{ - Name: "Grafana TestDataDB", - PluginId: "grafana-testdata-datasource", - }, + for _, query := range reqDto.Queries { + request.Queries = append(request.Queries, &tsdb.Query{ + RefId: query.Get("refId").MustString("A"), + MaxDataPoints: query.Get("maxDataPoints").MustInt64(100), + IntervalMs: query.Get("intervalMs").MustInt64(1000), + Model: query, + DataSource: &tsdb.DataSourceInfo{ + Name: "Grafana TestDataDB", + PluginId: "grafana-testdata-datasource", }, - }, + }) } - resp, err := tsdb.HandleRequest(req) + resp, err := tsdb.HandleRequest(request) if err != nil { return ApiError(500, "Metric request error", err) } - result := dtos.MetricQueryResultDto{} + return Json(200, &resp) +} - for _, v := range resp.Results { - if v.Error != nil { - return ApiError(500, "tsdb.HandleRequest() response error", v.Error) - } +// GET /api/tsdb/testdata/scenarios +func GetTestDataScenarios(c *middleware.Context) Response { + result := make([]interface{}, 0) - for _, series := range v.Series { - result.Data = append(result.Data, series) - } + for _, scenario := range testdata.ScenarioRegistry { + result = append(result, map[string]interface{}{ + "id": scenario.Id, + "name": scenario.Name, + "description": scenario.Description, + }) } return Json(200, &result) diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index e808d77a182..d32c42f27b0 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -69,7 +69,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) { context.Firing = len(context.EvalMatches) > 0 } -func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) { +func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) { getDsInfo := &m.GetDataSourceByIdQuery{ Id: c.Query.DatasourceId, OrgId: context.Rule.OrgId, @@ -105,9 +105,9 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange t return result, nil } -func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource, timerange tsdb.TimeRange) *tsdb.Request { +func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource, timeRange *tsdb.TimeRange) *tsdb.Request { req := &tsdb.Request{ - TimeRange: timerange, + TimeRange: timeRange, Queries: []*tsdb.Query{ { RefId: "A", diff --git a/pkg/tsdb/models.go b/pkg/tsdb/models.go index 0060f459d7b..fc66a47f981 100644 --- a/pkg/tsdb/models.go +++ b/pkg/tsdb/models.go @@ -4,7 +4,6 @@ import "github.com/grafana/grafana/pkg/components/simplejson" type Query struct { RefId string - Query string Model *simplejson.Json Depends []string DataSource *DataSourceInfo @@ -17,13 +16,13 @@ type Query struct { type QuerySlice []*Query type Request struct { - TimeRange TimeRange + TimeRange *TimeRange Queries QuerySlice } type Response struct { - BatchTimings []*BatchTiming - Results map[string]*QueryResult + BatchTimings []*BatchTiming `json:"timings"` + Results map[string]*QueryResult `json:"results"` } type DataSourceInfo struct { @@ -50,14 +49,14 @@ type BatchResult struct { } type QueryResult struct { - Error error - RefId string - Series TimeSeriesSlice + Error error `json:"error"` + RefId string `json:"refId"` + Series TimeSeriesSlice `json:"series"` } type TimeSeries struct { - Name string `json:"target"` - Points [][2]*float64 `json:"datapoints"` + Name string `json:"name"` + Points [][2]*float64 `json:"points"` } type TimeSeriesSlice []*TimeSeries diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index 1df51bd1ffe..4d6ab03cede 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -10,8 +10,8 @@ import ( "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/tsdb" "github.com/prometheus/client_golang/api/prometheus" - "golang.org/x/net/context" pmodel "github.com/prometheus/common/model" + "golang.org/x/net/context" ) type PrometheusExecutor struct { @@ -111,12 +111,12 @@ func parseQuery(queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) (*Prom return nil, err } - start, err := queryContext.TimeRange.FromTime() + start, err := queryContext.TimeRange.ParseFrom() if err != nil { return nil, err } - end, err := queryContext.TimeRange.ToTime() + end, err := queryContext.TimeRange.ParseTo() if err != nil { return nil, err } diff --git a/pkg/tsdb/query_context.go b/pkg/tsdb/query_context.go index a1fc4c9bcb5..db40ba6253c 100644 --- a/pkg/tsdb/query_context.go +++ b/pkg/tsdb/query_context.go @@ -3,7 +3,7 @@ package tsdb import "sync" type QueryContext struct { - TimeRange TimeRange + TimeRange *TimeRange Queries QuerySlice Results map[string]*QueryResult ResultsChan chan *BatchResult @@ -11,7 +11,7 @@ type QueryContext struct { BatchWaits sync.WaitGroup } -func NewQueryContext(queries QuerySlice, timeRange TimeRange) *QueryContext { +func NewQueryContext(queries QuerySlice, timeRange *TimeRange) *QueryContext { return &QueryContext{ TimeRange: timeRange, Queries: queries, diff --git a/pkg/tsdb/testdata/scenarios.go b/pkg/tsdb/testdata/scenarios.go new file mode 100644 index 00000000000..d54cbb05638 --- /dev/null +++ b/pkg/tsdb/testdata/scenarios.go @@ -0,0 +1,98 @@ +package testdata + +import ( + "math/rand" + "time" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/tsdb" +) + +type ScenarioHandler func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult + +type Scenario struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Handler ScenarioHandler `json:"-"` +} + +var ScenarioRegistry map[string]*Scenario + +func init() { + ScenarioRegistry = make(map[string]*Scenario) + logger := log.New("tsdb.testdata") + + registerScenario(&Scenario{ + Id: "random_walk", + Name: "Random Walk", + + Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { + timeWalkerMs := context.TimeRange.MustGetFrom().Unix() * 1000 + to := context.TimeRange.MustGetTo().Unix() * 1000 + + series := newSeriesForQuery(query) + + points := make([][2]*float64, 0) + walker := rand.Float64() * 100 + + for i := int64(0); i < 10000 && timeWalkerMs < to; i++ { + timestamp := float64(timeWalkerMs) + val := float64(walker) + points = append(points, [2]*float64{&val, ×tamp}) + + walker += rand.Float64() - 0.5 + timeWalkerMs += query.IntervalMs + } + + series.Points = points + + queryRes := &tsdb.QueryResult{} + queryRes.Series = append(queryRes.Series, series) + return queryRes + }, + }) + + registerScenario(&Scenario{ + Id: "no_data_points", + Name: "No Data Points", + Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { + return &tsdb.QueryResult{ + Series: make(tsdb.TimeSeriesSlice, 0), + } + }, + }) + + registerScenario(&Scenario{ + Id: "datapoints_outside_range", + Name: "Datapoints Outside Range", + Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { + queryRes := &tsdb.QueryResult{} + + series := newSeriesForQuery(query) + outsideTime := context.TimeRange.MustGetFrom().Add(-1*time.Hour).Unix() * 1000 + + timestamp := float64(outsideTime) + logger.Info("time", "from", timestamp) + val := float64(10) + + series.Points = append(series.Points, [2]*float64{&val, ×tamp}) + queryRes.Series = append(queryRes.Series, series) + return queryRes + }, + }) + +} + +func registerScenario(scenario *Scenario) { + ScenarioRegistry[scenario.Id] = scenario +} + +func newSeriesForQuery(query *tsdb.Query) *tsdb.TimeSeries { + alias := query.Model.Get("alias").MustString("") + if alias == "" { + alias = query.RefId + "-series" + } + + return &tsdb.TimeSeries{Name: alias} +} diff --git a/pkg/tsdb/testdata/testdata.go b/pkg/tsdb/testdata/testdata.go index b1eca4cb46a..5b40bb6de5a 100644 --- a/pkg/tsdb/testdata/testdata.go +++ b/pkg/tsdb/testdata/testdata.go @@ -1,17 +1,20 @@ package testdata import ( - "math/rand" - + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/tsdb" ) type TestDataExecutor struct { *tsdb.DataSourceInfo + log log.Logger } func NewTestDataExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor { - return &TestDataExecutor{dsInfo} + return &TestDataExecutor{ + DataSourceInfo: dsInfo, + log: log.New("tsdb.testdata"), + } } func init() { @@ -22,33 +25,15 @@ func (e *TestDataExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC result := &tsdb.BatchResult{} result.QueryResults = make(map[string]*tsdb.QueryResult) - from, _ := context.TimeRange.FromTime() - to, _ := context.TimeRange.ToTime() - - queryRes := &tsdb.QueryResult{} - for _, query := range queries { - // scenario := query.Model.Get("scenario").MustString("random_walk") - series := &tsdb.TimeSeries{Name: "test-series-0"} - - stepInSeconds := (to.Unix() - from.Unix()) / query.MaxDataPoints - points := make([][2]*float64, 0) - walker := rand.Float64() * 100 - time := from.Unix() - - for i := int64(0); i < query.MaxDataPoints; i++ { - timestamp := float64(time) - val := float64(walker) - points = append(points, [2]*float64{&val, ×tamp}) - - walker += rand.Float64() - 0.5 - time += stepInSeconds + scenarioId := query.Model.Get("scenarioId").MustString("random_walk") + if scenario, exist := ScenarioRegistry[scenarioId]; exist { + result.QueryResults[query.RefId] = scenario.Handler(query, context) + result.QueryResults[query.RefId].RefId = query.RefId + } else { + e.log.Error("Scenario not found", "scenarioId", scenarioId) } - - series.Points = points - queryRes.Series = append(queryRes.Series, series) } - result.QueryResults["A"] = queryRes return result } diff --git a/pkg/tsdb/time_range.go b/pkg/tsdb/time_range.go index dee3e683516..67227b834dc 100644 --- a/pkg/tsdb/time_range.go +++ b/pkg/tsdb/time_range.go @@ -7,8 +7,8 @@ import ( "time" ) -func NewTimeRange(from, to string) TimeRange { - return TimeRange{ +func NewTimeRange(from, to string) *TimeRange { + return &TimeRange{ From: from, To: to, Now: time.Now(), @@ -21,13 +21,37 @@ type TimeRange struct { Now time.Time } -func (tr TimeRange) FromTime() (time.Time, error) { - if val, err := strconv.ParseInt(tr.From, 10, 64); err == nil { - return time.Unix(val, 0), nil +func (tr *TimeRange) MustGetFrom() time.Time { + if res, err := tr.ParseFrom(); err != nil { + return time.Unix(0, 0) + } else { + return res + } +} + +func (tr *TimeRange) MustGetTo() time.Time { + if res, err := tr.ParseTo(); err != nil { + return time.Unix(0, 0) + } else { + return res + } +} + +func tryParseUnixMsEpoch(val string) (time.Time, bool) { + if val, err := strconv.ParseInt(val, 10, 64); err == nil { + seconds := val / 1000 + nano := (val - seconds*1000) * 1000000 + return time.Unix(seconds, nano), true + } + return time.Time{}, false +} + +func (tr *TimeRange) ParseFrom() (time.Time, error) { + if res, ok := tryParseUnixMsEpoch(tr.From); ok { + return res, nil } fromRaw := strings.Replace(tr.From, "now-", "", 1) - diff, err := time.ParseDuration("-" + fromRaw) if err != nil { return time.Time{}, err @@ -36,7 +60,7 @@ func (tr TimeRange) FromTime() (time.Time, error) { return tr.Now.Add(diff), nil } -func (tr TimeRange) ToTime() (time.Time, error) { +func (tr *TimeRange) ParseTo() (time.Time, error) { if tr.To == "now" { return tr.Now, nil } else if strings.HasPrefix(tr.To, "now-") { @@ -50,8 +74,8 @@ func (tr TimeRange) ToTime() (time.Time, error) { return tr.Now.Add(diff), nil } - if val, err := strconv.ParseInt(tr.To, 10, 64); err == nil { - return time.Unix(val, 0), nil + if res, ok := tryParseUnixMsEpoch(tr.To); ok { + return res, nil } return time.Time{}, fmt.Errorf("cannot parse to value %s", tr.To) diff --git a/pkg/tsdb/time_range_test.go b/pkg/tsdb/time_range_test.go index f4acb5e6d80..5412d0d05f3 100644 --- a/pkg/tsdb/time_range_test.go +++ b/pkg/tsdb/time_range_test.go @@ -23,13 +23,13 @@ func TestTimeRange(t *testing.T) { fiveMinAgo, _ := time.ParseDuration("-5m") expected := now.Add(fiveMinAgo) - res, err := tr.FromTime() + res, err := tr.ParseFrom() So(err, ShouldBeNil) So(res.Unix(), ShouldEqual, expected.Unix()) }) Convey("now ", func() { - res, err := tr.ToTime() + res, err := tr.ParseTo() So(err, ShouldBeNil) So(res.Unix(), ShouldEqual, now.Unix()) }) @@ -46,7 +46,7 @@ func TestTimeRange(t *testing.T) { fiveHourAgo, _ := time.ParseDuration("-5h") expected := now.Add(fiveHourAgo) - res, err := tr.FromTime() + res, err := tr.ParseFrom() So(err, ShouldBeNil) So(res.Unix(), ShouldEqual, expected.Unix()) }) @@ -54,7 +54,7 @@ func TestTimeRange(t *testing.T) { Convey("now-10m ", func() { fiveMinAgo, _ := time.ParseDuration("-10m") expected := now.Add(fiveMinAgo) - res, err := tr.ToTime() + res, err := tr.ParseTo() So(err, ShouldBeNil) So(res.Unix(), ShouldEqual, expected.Unix()) }) @@ -68,13 +68,13 @@ func TestTimeRange(t *testing.T) { Now: now, } - res, err := tr.FromTime() + res, err := tr.ParseFrom() So(err, ShouldBeNil) - So(res.Unix(), ShouldEqual, 1474973725473) + So(res.UnixNano()/int64(time.Millisecond), ShouldEqual, 1474973725473) - res, err = tr.ToTime() + res, err = tr.ParseTo() So(err, ShouldBeNil) - So(res.Unix(), ShouldEqual, 1474975757930) + So(res.UnixNano()/int64(time.Millisecond), ShouldEqual, 1474975757930) }) Convey("Cannot parse asdf", func() { @@ -85,10 +85,10 @@ func TestTimeRange(t *testing.T) { Now: now, } - _, err = tr.FromTime() + _, err = tr.ParseFrom() So(err, ShouldNotBeNil) - _, err = tr.ToTime() + _, err = tr.ParseTo() So(err, ShouldNotBeNil) }) }) diff --git a/pkg/tsdb/tsdb_test.go b/pkg/tsdb/tsdb_test.go index 24d84a27c74..429dd01d6ba 100644 --- a/pkg/tsdb/tsdb_test.go +++ b/pkg/tsdb/tsdb_test.go @@ -14,9 +14,9 @@ func TestMetricQuery(t *testing.T) { Convey("Given 3 queries for 2 data sources", func() { request := &Request{ Queries: QuerySlice{ - {RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1}}, - {RefId: "B", Query: "asd", DataSource: &DataSourceInfo{Id: 1}}, - {RefId: "C", Query: "asd", DataSource: &DataSourceInfo{Id: 2}}, + {RefId: "A", DataSource: &DataSourceInfo{Id: 1}}, + {RefId: "B", DataSource: &DataSourceInfo{Id: 1}}, + {RefId: "C", DataSource: &DataSourceInfo{Id: 2}}, }, } @@ -31,9 +31,9 @@ func TestMetricQuery(t *testing.T) { Convey("Given query 2 depends on query 1", func() { request := &Request{ Queries: QuerySlice{ - {RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1}}, - {RefId: "B", Query: "asd", DataSource: &DataSourceInfo{Id: 2}}, - {RefId: "C", Query: "#A / #B", DataSource: &DataSourceInfo{Id: 3}, Depends: []string{"A", "B"}}, + {RefId: "A", DataSource: &DataSourceInfo{Id: 1}}, + {RefId: "B", DataSource: &DataSourceInfo{Id: 2}}, + {RefId: "C", DataSource: &DataSourceInfo{Id: 3}, Depends: []string{"A", "B"}}, }, } @@ -55,7 +55,7 @@ func TestMetricQuery(t *testing.T) { Convey("When executing request with one query", t, func() { req := &Request{ Queries: QuerySlice{ - {RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, + {RefId: "A", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, }, } @@ -74,8 +74,8 @@ func TestMetricQuery(t *testing.T) { Convey("When executing one request with two queries from same data source", t, func() { req := &Request{ Queries: QuerySlice{ - {RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, - {RefId: "B", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, + {RefId: "A", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, + {RefId: "B", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, }, } @@ -100,9 +100,9 @@ func TestMetricQuery(t *testing.T) { Convey("When executing one request with three queries from different datasources", t, func() { req := &Request{ Queries: QuerySlice{ - {RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, - {RefId: "B", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, - {RefId: "C", Query: "asd", DataSource: &DataSourceInfo{Id: 2, PluginId: "test"}}, + {RefId: "A", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, + {RefId: "B", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}}, + {RefId: "C", DataSource: &DataSourceInfo{Id: 2, PluginId: "test"}}, }, } @@ -117,7 +117,7 @@ func TestMetricQuery(t *testing.T) { Convey("When query uses data source of unknown type", t, func() { req := &Request{ Queries: QuerySlice{ - {RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "asdasdas"}}, + {RefId: "A", DataSource: &DataSourceInfo{Id: 1, PluginId: "asdasdas"}}, }, } @@ -129,10 +129,10 @@ func TestMetricQuery(t *testing.T) { req := &Request{ Queries: QuerySlice{ { - RefId: "A", Query: "asd", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}, + RefId: "A", DataSource: &DataSourceInfo{Id: 1, PluginId: "test"}, }, { - RefId: "B", Query: "#A / 2", DataSource: &DataSourceInfo{Id: 2, PluginId: "test"}, Depends: []string{"A"}, + RefId: "B", DataSource: &DataSourceInfo{Id: 2, PluginId: "test"}, Depends: []string{"A"}, }, }, } diff --git a/public/app/plugins/app/testdata/datasource/datasource.ts b/public/app/plugins/app/testdata/datasource/datasource.ts index 32edfb59755..a06daa19262 100644 --- a/public/app/plugins/app/testdata/datasource/datasource.ts +++ b/public/app/plugins/app/testdata/datasource/datasource.ts @@ -10,18 +10,38 @@ class TestDataDatasource { query(options) { var queries = _.filter(options.targets, item => { return item.hide !== true; + }).map(item => { + return { + refId: item.refId, + scenarioId: item.scenarioId, + intervalMs: options.intervalMs, + maxDataPoints: options.maxDataPoints, + }; }); if (queries.length === 0) { return this.$q.when({data: []}); } - return this.backendSrv.get('/api/metrics/test', { - from: options.range.from.valueOf(), - to: options.range.to.valueOf(), - scenario: options.targets[0].scenario, - interval: options.intervalMs, - maxDataPoints: options.maxDataPoints, + return this.backendSrv.post('/api/tsdb/query', { + from: options.range.from.valueOf().toString(), + to: options.range.to.valueOf().toString(), + queries: queries, + }).then(res => { + var data = []; + + if (res.results) { + _.forEach(res.results, queryRes => { + for (let series of queryRes.series) { + data.push({ + target: series.name, + datapoints: series.points + }); + } + }); + } + + return {data: data}; }); } diff --git a/public/app/plugins/app/testdata/datasource/query_ctrl.ts b/public/app/plugins/app/testdata/datasource/query_ctrl.ts index 44a62fd1a11..f22ef8948ad 100644 --- a/public/app/plugins/app/testdata/datasource/query_ctrl.ts +++ b/public/app/plugins/app/testdata/datasource/query_ctrl.ts @@ -6,19 +6,20 @@ import {QueryCtrl} from 'app/plugins/sdk'; export class TestDataQueryCtrl extends QueryCtrl { static templateUrl = 'partials/query.editor.html'; - scenarioDefs: any; + scenarioList: any; /** @ngInject **/ - constructor($scope, $injector) { + constructor($scope, $injector, private backendSrv) { super($scope, $injector); - this.target.scenario = this.target.scenario || 'random_walk'; + this.target.scenarioId = this.target.scenarioId || 'random_walk'; + this.scenarioList = []; + } - this.scenarioDefs = { - 'random_walk': {text: 'Random Walk'}, - 'no_datapoints': {text: 'No Datapoints'}, - 'data_outside_range': {text: 'Data Outside Range'}, - }; + $onInit() { + return this.backendSrv.get('/api/tsdb/testdata/scenarios').then(res => { + this.scenarioList = res; + }); } } diff --git a/public/app/plugins/app/testdata/partials/query.editor.html b/public/app/plugins/app/testdata/partials/query.editor.html index d9068dfda49..53f84309661 100644 --- a/public/app/plugins/app/testdata/partials/query.editor.html +++ b/public/app/plugins/app/testdata/partials/query.editor.html @@ -2,17 +2,17 @@
-
- +
+
- +
- +
diff --git a/public/app/plugins/panel/graph/data_processor.ts b/public/app/plugins/panel/graph/data_processor.ts index bb8af84cf00..6233ac345c9 100644 --- a/public/app/plugins/panel/graph/data_processor.ts +++ b/public/app/plugins/panel/graph/data_processor.ts @@ -78,7 +78,7 @@ export class DataProcessor { } timeSeriesHandler(seriesData, index, options) { - var datapoints = seriesData.datapoints; + var datapoints = seriesData.datapoints || []; var alias = seriesData.target; var colorIndex = index % colors.length;