mirror of
https://github.com/grafana/grafana.git
synced 2025-01-16 11:42:35 -06:00
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
This commit is contained in:
parent
27c143ef9b
commit
554055e39d
@ -28,6 +28,7 @@ func (e *CloudMonitoringExecutor) executeAnnotationQuery(ctx context.Context, ts
|
|||||||
title := metricQuery.Get("title").MustString()
|
title := metricQuery.Get("title").MustString()
|
||||||
text := metricQuery.Get("text").MustString()
|
text := metricQuery.Get("text").MustString()
|
||||||
tags := metricQuery.Get("tags").MustString()
|
tags := metricQuery.Get("tags").MustString()
|
||||||
|
|
||||||
err = queries[0].parseToAnnotations(queryRes, resp, title, text, tags)
|
err = queries[0].parseToAnnotations(queryRes, resp, title, text, tags)
|
||||||
result.Results[firstQuery.RefId] = queryRes
|
result.Results[firstQuery.RefId] = queryRes
|
||||||
|
|
||||||
|
@ -10,18 +10,51 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestCloudMonitoringExecutor_parseToAnnotations(t *testing.T) {
|
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.NoError(t, err)
|
||||||
require.Len(t, data.TimeSeries, 3)
|
require.Len(t, d.TimeSeries, 3)
|
||||||
|
|
||||||
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"}
|
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"}
|
||||||
query := &cloudMonitoringTimeSeriesFilter{}
|
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.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, res.Tables, 1)
|
decoded, _ := res.Dataframes.Decoded()
|
||||||
require.Len(t, res.Tables[0].Rows, 9)
|
require.Len(t, decoded, 3)
|
||||||
assert.Equal(t, "atitle collector-asia-east-1 9.856650", res.Tables[0].Rows[0][1])
|
assert.Equal(t, "title", decoded[0].Fields[1].Name)
|
||||||
assert.Equal(t, "atext asia-east1-a", res.Tables[0].Rows[0][3])
|
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)
|
||||||
}
|
}
|
||||||
|
@ -268,27 +268,33 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) handleNonDistributionSe
|
|||||||
setDisplayNameAsFieldName(dataField)
|
setDisplayNameAsFieldName(dataField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, title string, text string, tags string) error {
|
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(queryRes *tsdb.QueryResult, response cloudMonitoringResponse, title string, text string, tags string) error {
|
||||||
annotations := make([]map[string]string, 0)
|
frames := data.Frames{}
|
||||||
|
for _, series := range response.TimeSeries {
|
||||||
for _, series := range data.TimeSeries {
|
if len(series.Points) == 0 {
|
||||||
// reverse the order to be ascending
|
continue
|
||||||
|
}
|
||||||
|
annotation := make(map[string][]string)
|
||||||
for i := len(series.Points) - 1; i >= 0; i-- {
|
for i := len(series.Points) - 1; i >= 0; i-- {
|
||||||
point := series.Points[i]
|
point := series.Points[i]
|
||||||
value := strconv.FormatFloat(point.Value.DoubleValue, 'f', 6, 64)
|
value := strconv.FormatFloat(point.Value.DoubleValue, 'f', 6, 64)
|
||||||
if series.ValueType == "STRING" {
|
if series.ValueType == "STRING" {
|
||||||
value = point.Value.StringValue
|
value = point.Value.StringValue
|
||||||
}
|
}
|
||||||
annotation := make(map[string]string)
|
annotation["time"] = append(annotation["time"], point.Interval.EndTime.UTC().Format(time.RFC3339))
|
||||||
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["title"] = formatAnnotationText(title, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels)
|
annotation["tags"] = append(annotation["tags"], tags)
|
||||||
annotation["tags"] = tags
|
annotation["text"] = append(annotation["text"], formatAnnotationText(text, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels))
|
||||||
annotation["text"] = formatAnnotationText(text, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels)
|
|
||||||
annotations = append(annotations, annotation)
|
|
||||||
}
|
}
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
|||||||
|
|
||||||
import { CloudMonitoringOptions, CloudMonitoringQuery, Filter, MetricDescriptor, QueryType, EditorMode } from './types';
|
import { CloudMonitoringOptions, CloudMonitoringQuery, Filter, MetricDescriptor, QueryType, EditorMode } from './types';
|
||||||
import API from './api';
|
import API from './api';
|
||||||
import { DataSourceWithBackend } from '@grafana/runtime';
|
import { DataSourceWithBackend, toDataQueryResponse } from '@grafana/runtime';
|
||||||
import { CloudMonitoringVariableSupport } from './variables';
|
import { CloudMonitoringVariableSupport } from './variables';
|
||||||
import { catchError, map, mergeMap } from 'rxjs/operators';
|
import { catchError, map, mergeMap } from 'rxjs/operators';
|
||||||
import { from, Observable, of, throwError } from 'rxjs';
|
import { from, Observable, of, throwError } from 'rxjs';
|
||||||
@ -79,17 +79,24 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend<
|
|||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
map(({ data }) => {
|
map(({ data }) => {
|
||||||
const results = data.results['annotationQuery'].tables[0].rows.map((v: any) => {
|
const dataQueryResponse = toDataQueryResponse({
|
||||||
return {
|
data: data,
|
||||||
annotation: annotation,
|
|
||||||
time: Date.parse(v[0]),
|
|
||||||
title: v[1],
|
|
||||||
tags: [],
|
|
||||||
text: v[3],
|
|
||||||
} as any;
|
|
||||||
});
|
});
|
||||||
|
const df: any = [];
|
||||||
return results;
|
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();
|
.toPromise();
|
||||||
|
Loading…
Reference in New Issue
Block a user