[v9.2.x] Google Cloud Monitoring: Set frame interval to draw null values (#57914)
This commit is contained in:
@@ -663,7 +663,7 @@ func unmarshalResponse(res *http.Response) (cloudMonitoringResponse, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func addConfigData(frames data.Frames, dl string, unit string) data.Frames {
|
||||
func addConfigData(frames data.Frames, dl string, unit string, period string) data.Frames {
|
||||
for i := range frames {
|
||||
if frames[i].Fields[1].Config == nil {
|
||||
frames[i].Fields[1].Config = &data.FieldConfig{}
|
||||
@@ -681,6 +681,15 @@ func addConfigData(frames data.Frames, dl string, unit string) data.Frames {
|
||||
frames[i].Fields[1].Config.Unit = val
|
||||
}
|
||||
}
|
||||
if frames[i].Fields[0].Config == nil {
|
||||
frames[i].Fields[0].Config = &data.FieldConfig{}
|
||||
}
|
||||
if period != "" {
|
||||
err := addInterval(period, frames[i].Fields[0])
|
||||
if err != nil {
|
||||
slog.Error("Failed to add interval", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes
|
||||
}
|
||||
if len(response.TimeSeries) > 0 {
|
||||
dl := timeSeriesFilter.buildDeepLink()
|
||||
frames = addConfigData(frames, dl, response.Unit)
|
||||
frames = addConfigData(frames, dl, response.Unit, timeSeriesFilter.Params.Get("aggregation.alignmentPeriod"))
|
||||
}
|
||||
|
||||
queryRes.Frames = frames
|
||||
|
||||
@@ -389,6 +389,21 @@ func TestTimeSeriesFilter(t *testing.T) {
|
||||
assert.Equal(t, "114250375703598695", labels["resource.label.instance_id"])
|
||||
})
|
||||
|
||||
t.Run("includes time interval", func(t *testing.T) {
|
||||
data, err := loadTestFile("./test-data/5-series-response-meta-data.json")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 3, len(data.TimeSeries))
|
||||
res := &backend.DataResponse{}
|
||||
query := &cloudMonitoringTimeSeriesFilter{Params: url.Values{
|
||||
"aggregation.alignmentPeriod": []string{"+60s"},
|
||||
}}
|
||||
err = query.parseResponse(res, data, "")
|
||||
require.NoError(t, err)
|
||||
frames := res.Frames
|
||||
timeField := frames[0].Fields[0]
|
||||
assert.Equal(t, float64(60*1000), timeField.Config.Interval)
|
||||
})
|
||||
|
||||
t.Run("parseResponse successfully parses metadata for distribution valueType", func(t *testing.T) {
|
||||
t.Run("exponential bounds", func(t *testing.T) {
|
||||
data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json")
|
||||
|
||||
@@ -20,22 +20,21 @@ import (
|
||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||
)
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) appendGraphPeriod(req *backend.QueryDataRequest) string {
|
||||
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) appendGraphPeriod(req *backend.QueryDataRequest) string {
|
||||
// GraphPeriod needs to be explicitly disabled.
|
||||
// If not set, the default behavior is to set an automatic value
|
||||
if timeSeriesQuery.GraphPeriod != "disabled" {
|
||||
graphPeriod := timeSeriesQuery.GraphPeriod
|
||||
if graphPeriod == "auto" || graphPeriod == "" {
|
||||
if timeSeriesQuery.GraphPeriod == "auto" || timeSeriesQuery.GraphPeriod == "" {
|
||||
intervalCalculator := intervalv2.NewCalculator(intervalv2.CalculatorOptions{})
|
||||
interval := intervalCalculator.Calculate(req.Queries[0].TimeRange, time.Duration(timeSeriesQuery.IntervalMS/1000)*time.Second, req.Queries[0].MaxDataPoints)
|
||||
graphPeriod = interval.Text
|
||||
timeSeriesQuery.GraphPeriod = interval.Text
|
||||
}
|
||||
return fmt.Sprintf(" | graph_period %s", graphPeriod)
|
||||
return fmt.Sprintf(" | graph_period %s", timeSeriesQuery.GraphPeriod)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, req *backend.QueryDataRequest,
|
||||
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) run(ctx context.Context, req *backend.QueryDataRequest,
|
||||
s *Service, dsInfo datasourceInfo, tracer tracing.Tracer) (*backend.DataResponse, cloudMonitoringResponse, string, error) {
|
||||
dr := &backend.DataResponse{}
|
||||
projectName := timeSeriesQuery.ProjectName
|
||||
@@ -93,7 +92,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, r
|
||||
return dr, d, timeSeriesQuery.Query, nil
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *backend.DataResponse,
|
||||
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *backend.DataResponse,
|
||||
response cloudMonitoringResponse, executedQueryString string) error {
|
||||
frames := data.Frames{}
|
||||
|
||||
@@ -256,7 +255,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ba
|
||||
}
|
||||
if len(response.TimeSeriesData) > 0 {
|
||||
dl := timeSeriesQuery.buildDeepLink()
|
||||
frames = addConfigData(frames, dl, response.Unit)
|
||||
frames = addConfigData(frames, dl, response.Unit, timeSeriesQuery.GraphPeriod)
|
||||
}
|
||||
|
||||
queryRes.Frames = frames
|
||||
@@ -264,7 +263,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ba
|
||||
return nil
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRes *backend.DataResponse,
|
||||
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRes *backend.DataResponse,
|
||||
data cloudMonitoringResponse, title, text string) error {
|
||||
annotations := make([]*annotationEvent, 0)
|
||||
|
||||
@@ -316,7 +315,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRe
|
||||
return nil
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) buildDeepLink() string {
|
||||
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) buildDeepLink() string {
|
||||
u, err := url.Parse("https://console.cloud.google.com/monitoring/metrics-explorer")
|
||||
if err != nil {
|
||||
slog.Error("Failed to generate deep link: unable to parse metrics explorer URL", "projectName", timeSeriesQuery.ProjectName, "query", timeSeriesQuery.RefID)
|
||||
@@ -371,6 +370,6 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) buildDeepLink() string {
|
||||
return accountChooserURL.String()
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) getRefID() string {
|
||||
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) getRefID() string {
|
||||
return timeSeriesQuery.RefID
|
||||
}
|
||||
|
||||
@@ -102,6 +102,28 @@ func TestTimeSeriesQuery(t *testing.T) {
|
||||
assert.Equal(t, "6724404429462225363", labels["resource.label.instance_id"])
|
||||
})
|
||||
|
||||
t.Run("includes time interval", func(t *testing.T) {
|
||||
data, err := loadTestFile("./test-data/7-series-response-mql.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
|
||||
res := &backend.DataResponse{}
|
||||
query := &cloudMonitoringTimeSeriesQuery{
|
||||
ProjectName: "test-proj",
|
||||
Query: "test-query",
|
||||
timeRange: backend.TimeRange{
|
||||
From: fromStart,
|
||||
To: fromStart.Add(34 * time.Minute),
|
||||
},
|
||||
GraphPeriod: "60s",
|
||||
}
|
||||
err = query.parseResponse(res, data, "")
|
||||
require.NoError(t, err)
|
||||
frames := res.Frames
|
||||
timeField := frames[0].Fields[0]
|
||||
assert.Equal(t, float64(60*1000), timeField.Config.Interval)
|
||||
})
|
||||
|
||||
t.Run("appends graph_period to the query", func(t *testing.T) {
|
||||
query := &cloudMonitoringTimeSeriesQuery{}
|
||||
assert.Equal(t, query.appendGraphPeriod(&backend.QueryDataRequest{Queries: []backend.DataQuery{{}}}), " | graph_period 1ms")
|
||||
|
||||
@@ -2,6 +2,9 @@ package cloudmonitoring
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||
)
|
||||
|
||||
func reverse(s string) string {
|
||||
@@ -24,3 +27,21 @@ func containsLabel(labels []string, newLabel string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func addInterval(period string, field *data.Field) error {
|
||||
period = strings.TrimPrefix(period, "+")
|
||||
p, err := intervalv2.ParseIntervalStringToTimeDuration(period)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
if field.Config != nil {
|
||||
field.Config.Interval = float64(p.Milliseconds())
|
||||
} else {
|
||||
field.SetConfig(&data.FieldConfig{
|
||||
Interval: float64(p.Milliseconds()),
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user