From 554055e39db317b130d9c55e0a518d18be12c47d Mon Sep 17 00:00:00 2001 From: Dimitris Sotirakis Date: Thu, 25 Feb 2021 18:29:17 +0200 Subject: [PATCH] Convert annotations to dataframes (#31400) * Stop using transformation to tables * Update datasource.ts to parse from dataframes * Add test on transformToDataframe func * Remove parseResponse and do the work inside parseToAnnotations method * Name the dataframe after the RefID * Small fix when mapping to annotation * Add tests in annotation_query_test.go class * Small fix in loop iteration * Change for loop signature --- pkg/tsdb/cloudmonitoring/annotation_query.go | 1 + .../cloudmonitoring/annotation_query_test.go | 47 ++++++++++++++++--- .../cloudmonitoring/time_series_filter.go | 30 +++++++----- .../datasource/cloud-monitoring/datasource.ts | 29 +++++++----- 4 files changed, 77 insertions(+), 30 deletions(-) diff --git a/pkg/tsdb/cloudmonitoring/annotation_query.go b/pkg/tsdb/cloudmonitoring/annotation_query.go index 0aa5cea4a2f..a08f6e60f82 100644 --- a/pkg/tsdb/cloudmonitoring/annotation_query.go +++ b/pkg/tsdb/cloudmonitoring/annotation_query.go @@ -28,6 +28,7 @@ func (e *CloudMonitoringExecutor) executeAnnotationQuery(ctx context.Context, ts title := metricQuery.Get("title").MustString() text := metricQuery.Get("text").MustString() tags := metricQuery.Get("tags").MustString() + err = queries[0].parseToAnnotations(queryRes, resp, title, text, tags) result.Results[firstQuery.RefId] = queryRes diff --git a/pkg/tsdb/cloudmonitoring/annotation_query_test.go b/pkg/tsdb/cloudmonitoring/annotation_query_test.go index b0d50acf140..857517232ca 100644 --- a/pkg/tsdb/cloudmonitoring/annotation_query_test.go +++ b/pkg/tsdb/cloudmonitoring/annotation_query_test.go @@ -10,18 +10,51 @@ import ( ) func TestCloudMonitoringExecutor_parseToAnnotations(t *testing.T) { - data, err := loadTestFile("./test-data/2-series-response-no-agg.json") + d, err := loadTestFile("./test-data/2-series-response-no-agg.json") require.NoError(t, err) - require.Len(t, data.TimeSeries, 3) + require.Len(t, d.TimeSeries, 3) res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"} query := &cloudMonitoringTimeSeriesFilter{} - err = query.parseToAnnotations(res, data, "atitle {{metric.label.instance_name}} {{metric.value}}", "atext {{resource.label.zone}}", "atag") + err = query.parseToAnnotations(res, d, "atitle {{metric.label.instance_name}} {{metric.value}}", "atext {{resource.label.zone}}", "atag") require.NoError(t, err) - require.Len(t, res.Tables, 1) - require.Len(t, res.Tables[0].Rows, 9) - assert.Equal(t, "atitle collector-asia-east-1 9.856650", res.Tables[0].Rows[0][1]) - assert.Equal(t, "atext asia-east1-a", res.Tables[0].Rows[0][3]) + decoded, _ := res.Dataframes.Decoded() + require.Len(t, decoded, 3) + assert.Equal(t, "title", decoded[0].Fields[1].Name) + assert.Equal(t, "tags", decoded[0].Fields[2].Name) + assert.Equal(t, "text", decoded[0].Fields[3].Name) +} + +func TestCloudMonitoringExecutor_parseToAnnotations_emptyTimeSeries(t *testing.T) { + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"} + query := &cloudMonitoringTimeSeriesFilter{} + + response := cloudMonitoringResponse{ + TimeSeries: []timeSeries{}, + } + + err := query.parseToAnnotations(res, response, "atitle", "atext", "atag") + require.NoError(t, err) + + decoded, _ := res.Dataframes.Decoded() + require.Len(t, decoded, 0) +} + +func TestCloudMonitoringExecutor_parseToAnnotations_noPointsInSeries(t *testing.T) { + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"} + query := &cloudMonitoringTimeSeriesFilter{} + + response := cloudMonitoringResponse{ + TimeSeries: []timeSeries{ + {Points: nil}, + }, + } + + err := query.parseToAnnotations(res, response, "atitle", "atext", "atag") + require.NoError(t, err) + + decoded, _ := res.Dataframes.Decoded() + require.Len(t, decoded, 0) } diff --git a/pkg/tsdb/cloudmonitoring/time_series_filter.go b/pkg/tsdb/cloudmonitoring/time_series_filter.go index 643d43ba43c..4924f333530 100644 --- a/pkg/tsdb/cloudmonitoring/time_series_filter.go +++ b/pkg/tsdb/cloudmonitoring/time_series_filter.go @@ -268,27 +268,33 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) handleNonDistributionSe setDisplayNameAsFieldName(dataField) } -func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, title string, text string, tags string) error { - annotations := make([]map[string]string, 0) - - for _, series := range data.TimeSeries { - // reverse the order to be ascending +func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(queryRes *tsdb.QueryResult, response cloudMonitoringResponse, title string, text string, tags string) error { + frames := data.Frames{} + for _, series := range response.TimeSeries { + if len(series.Points) == 0 { + continue + } + annotation := make(map[string][]string) for i := len(series.Points) - 1; i >= 0; i-- { point := series.Points[i] value := strconv.FormatFloat(point.Value.DoubleValue, 'f', 6, 64) if series.ValueType == "STRING" { value = point.Value.StringValue } - annotation := make(map[string]string) - annotation["time"] = point.Interval.EndTime.UTC().Format(time.RFC3339) - annotation["title"] = formatAnnotationText(title, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels) - annotation["tags"] = tags - annotation["text"] = formatAnnotationText(text, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels) - annotations = append(annotations, annotation) + annotation["time"] = append(annotation["time"], point.Interval.EndTime.UTC().Format(time.RFC3339)) + annotation["title"] = append(annotation["title"], formatAnnotationText(title, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels)) + annotation["tags"] = append(annotation["tags"], tags) + annotation["text"] = append(annotation["text"], formatAnnotationText(text, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels)) } + frames = append(frames, data.NewFrame(queryRes.RefId, + data.NewField("time", nil, annotation["time"]), + data.NewField("title", nil, annotation["title"]), + data.NewField("tags", nil, annotation["tags"]), + data.NewField("text", nil, annotation["text"]), + )) } + queryRes.Dataframes = tsdb.NewDecodedDataFrames(frames) - transformAnnotationToTable(annotations, queryRes) return nil } diff --git a/public/app/plugins/datasource/cloud-monitoring/datasource.ts b/public/app/plugins/datasource/cloud-monitoring/datasource.ts index 41bc91442ff..01bab8fc4ba 100644 --- a/public/app/plugins/datasource/cloud-monitoring/datasource.ts +++ b/public/app/plugins/datasource/cloud-monitoring/datasource.ts @@ -12,7 +12,7 @@ import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { CloudMonitoringOptions, CloudMonitoringQuery, Filter, MetricDescriptor, QueryType, EditorMode } from './types'; import API from './api'; -import { DataSourceWithBackend } from '@grafana/runtime'; +import { DataSourceWithBackend, toDataQueryResponse } from '@grafana/runtime'; import { CloudMonitoringVariableSupport } from './variables'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { from, Observable, of, throwError } from 'rxjs'; @@ -79,17 +79,24 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< }) .pipe( map(({ data }) => { - const results = data.results['annotationQuery'].tables[0].rows.map((v: any) => { - return { - annotation: annotation, - time: Date.parse(v[0]), - title: v[1], - tags: [], - text: v[3], - } as any; + const dataQueryResponse = toDataQueryResponse({ + data: data, }); - - return results; + const df: any = []; + if (dataQueryResponse.data.length !== 0) { + for (let i = 0; i < dataQueryResponse.data.length; i++) { + for (let j = 0; j < dataQueryResponse.data[i].fields[0].values.length; j++) { + df.push({ + annotation: annotation, + time: Date.parse(dataQueryResponse.data[i].fields[0].values.get(j)), + title: dataQueryResponse.data[i].fields[1].values.get(j), + tags: [], + text: dataQueryResponse.data[i].fields[3].values.get(j), + }); + } + } + } + return df; }) ) .toPromise();