mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PublicDashboards: Support timezone on query API (#68560)
This commit is contained in:
parent
7ea9be6832
commit
fc374f93a3
@ -2,12 +2,9 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/kinds/dashboard"
|
"github.com/grafana/grafana/pkg/kinds/dashboard"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublicDashboardErr represents a dashboard error.
|
// PublicDashboardErr represents a dashboard error.
|
||||||
@ -127,25 +124,6 @@ func (ts *TimeSettings) ToDB() ([]byte, error) {
|
|||||||
return json.Marshal(ts)
|
return json.Marshal(ts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildTimeSettings build time settings object using selected values if enabled and are valid or dashboard default values
|
|
||||||
func (pd PublicDashboard) BuildTimeSettings(dashboard *dashboards.Dashboard, reqDTO PublicDashboardQueryDTO) TimeSettings {
|
|
||||||
from := dashboard.Data.GetPath("time", "from").MustString()
|
|
||||||
to := dashboard.Data.GetPath("time", "to").MustString()
|
|
||||||
|
|
||||||
if pd.TimeSelectionEnabled {
|
|
||||||
from = reqDTO.TimeRange.From
|
|
||||||
to = reqDTO.TimeRange.To
|
|
||||||
}
|
|
||||||
|
|
||||||
timeRange := legacydata.NewDataTimeRange(from, to)
|
|
||||||
|
|
||||||
// Were using epoch ms because this is used to build a MetricRequest, which is used by query caching, which expected the time range in epoch milliseconds.
|
|
||||||
return TimeSettings{
|
|
||||||
From: strconv.FormatInt(timeRange.GetFromAsMsEpoch(), 10),
|
|
||||||
To: strconv.FormatInt(timeRange.GetToAsMsEpoch(), 10),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DTO for transforming user input in the api
|
// DTO for transforming user input in the api
|
||||||
type SavePublicDashboardDTO struct {
|
type SavePublicDashboardDTO struct {
|
||||||
DashboardUid string
|
DashboardUid string
|
||||||
@ -153,11 +131,17 @@ type SavePublicDashboardDTO struct {
|
|||||||
PublicDashboard *PublicDashboardDTO
|
PublicDashboard *PublicDashboardDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TimeRangeDTO struct {
|
||||||
|
From string
|
||||||
|
To string
|
||||||
|
Timezone string
|
||||||
|
}
|
||||||
|
|
||||||
type PublicDashboardQueryDTO struct {
|
type PublicDashboardQueryDTO struct {
|
||||||
IntervalMs int64
|
IntervalMs int64
|
||||||
MaxDataPoints int64
|
MaxDataPoints int64
|
||||||
QueryCachingTTL int64
|
QueryCachingTTL int64
|
||||||
TimeRange TimeSettings
|
TimeRange TimeRangeDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnnotationsQueryDTO struct {
|
type AnnotationsQueryDTO struct {
|
||||||
|
@ -1,74 +1,11 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/publicdashboards/internal"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPublicDashboardTableName(t *testing.T) {
|
func TestPublicDashboardTableName(t *testing.T) {
|
||||||
assert.Equal(t, "dashboard_public", PublicDashboard{}.TableName())
|
assert.Equal(t, "dashboard_public", PublicDashboard{}.TableName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildTimeSettings(t *testing.T) {
|
|
||||||
var dashboardData = simplejson.NewFromAny(map[string]interface{}{"time": map[string]interface{}{"from": "2022-09-01T00:00:00.000Z", "to": "2022-09-01T12:00:00.000Z"}})
|
|
||||||
defaultFromMs, defaultToMs := internal.GetTimeRangeFromDashboard(t, dashboardData)
|
|
||||||
|
|
||||||
selectionFromMs := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
|
||||||
selectionToMs := strconv.FormatInt(time.Now().Add(time.Hour).UnixMilli(), 10)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
dashboard *dashboards.Dashboard
|
|
||||||
pubdash *PublicDashboard
|
|
||||||
timeResult TimeSettings
|
|
||||||
reqDTO PublicDashboardQueryDTO
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "should use dashboard time if pubdash time empty",
|
|
||||||
dashboard: &dashboards.Dashboard{Data: dashboardData},
|
|
||||||
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
|
||||||
timeResult: TimeSettings{
|
|
||||||
From: defaultFromMs,
|
|
||||||
To: defaultToMs,
|
|
||||||
},
|
|
||||||
reqDTO: PublicDashboardQueryDTO{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "should use dashboard time when time selection is disabled",
|
|
||||||
dashboard: &dashboards.Dashboard{Data: dashboardData},
|
|
||||||
pubdash: &PublicDashboard{TimeSelectionEnabled: false, TimeSettings: &TimeSettings{From: "now-12", To: "now"}},
|
|
||||||
timeResult: TimeSettings{
|
|
||||||
From: defaultFromMs,
|
|
||||||
To: defaultToMs,
|
|
||||||
},
|
|
||||||
reqDTO: PublicDashboardQueryDTO{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "should use selected values if time selection is enabled",
|
|
||||||
dashboard: &dashboards.Dashboard{Data: dashboardData},
|
|
||||||
pubdash: &PublicDashboard{TimeSelectionEnabled: true, TimeSettings: &TimeSettings{From: "now-12", To: "now"}},
|
|
||||||
reqDTO: PublicDashboardQueryDTO{
|
|
||||||
TimeRange: TimeSettings{
|
|
||||||
From: selectionFromMs,
|
|
||||||
To: selectionToMs,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
timeResult: TimeSettings{
|
|
||||||
From: selectionFromMs,
|
|
||||||
To: selectionToMs,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
assert.Equal(t, test.timeResult, test.pubdash.BuildTimeSettings(test.dashboard, test.reqDTO))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,8 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
@ -14,6 +16,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/publicdashboards/validation"
|
"github.com/grafana/grafana/pkg/services/publicdashboards/validation"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindAnnotations returns annotations for a public dashboard
|
// FindAnnotations returns annotations for a public dashboard
|
||||||
@ -106,7 +109,6 @@ func (pd *PublicDashboardServiceImpl) GetMetricRequest(ctx context.Context, dash
|
|||||||
}
|
}
|
||||||
|
|
||||||
metricReqDTO, err := pd.buildMetricRequest(
|
metricReqDTO, err := pd.buildMetricRequest(
|
||||||
ctx,
|
|
||||||
dashboard,
|
dashboard,
|
||||||
publicDashboard,
|
publicDashboard,
|
||||||
panelId,
|
panelId,
|
||||||
@ -151,7 +153,7 @@ func (pd *PublicDashboardServiceImpl) GetQueryDataResponse(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildMetricRequest merges public dashboard parameters with dashboard and returns a metrics request to be sent to query backend
|
// buildMetricRequest merges public dashboard parameters with dashboard and returns a metrics request to be sent to query backend
|
||||||
func (pd *PublicDashboardServiceImpl) buildMetricRequest(ctx context.Context, dashboard *dashboards.Dashboard, publicDashboard *models.PublicDashboard, panelId int64, reqDTO models.PublicDashboardQueryDTO) (dtos.MetricRequest, error) {
|
func (pd *PublicDashboardServiceImpl) buildMetricRequest(dashboard *dashboards.Dashboard, publicDashboard *models.PublicDashboard, panelId int64, reqDTO models.PublicDashboardQueryDTO) (dtos.MetricRequest, error) {
|
||||||
// group queries by panel
|
// group queries by panel
|
||||||
queriesByPanel := groupQueriesByPanelId(dashboard.Data)
|
queriesByPanel := groupQueriesByPanelId(dashboard.Data)
|
||||||
queries, ok := queriesByPanel[panelId]
|
queries, ok := queriesByPanel[panelId]
|
||||||
@ -159,7 +161,7 @@ func (pd *PublicDashboardServiceImpl) buildMetricRequest(ctx context.Context, da
|
|||||||
return dtos.MetricRequest{}, models.ErrPanelNotFound.Errorf("buildMetricRequest: public dashboard panel not found")
|
return dtos.MetricRequest{}, models.ErrPanelNotFound.Errorf("buildMetricRequest: public dashboard panel not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := publicDashboard.BuildTimeSettings(dashboard, reqDTO)
|
ts := buildTimeSettings(dashboard, reqDTO, publicDashboard)
|
||||||
|
|
||||||
// determine safe resolution to query data at
|
// determine safe resolution to query data at
|
||||||
safeInterval, safeResolution := pd.getSafeIntervalAndMaxDataPoints(reqDTO, ts)
|
safeInterval, safeResolution := pd.getSafeIntervalAndMaxDataPoints(reqDTO, ts)
|
||||||
@ -319,3 +321,57 @@ func sanitizeMetadataFromQueryData(res *backend.QueryDataResponse) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDataTimeRange declared to be able to stub this function in tests
|
||||||
|
var NewDataTimeRange = legacydata.NewDataTimeRange
|
||||||
|
|
||||||
|
// BuildTimeSettings build time settings object using selected values if enabled and are valid or dashboard default values
|
||||||
|
func buildTimeSettings(d *dashboards.Dashboard, reqDTO models.PublicDashboardQueryDTO, pd *models.PublicDashboard) models.TimeSettings {
|
||||||
|
from, to, timezone := getTimeRangeValuesOrDefault(reqDTO, d, pd.TimeSelectionEnabled)
|
||||||
|
|
||||||
|
timeRange := NewDataTimeRange(from, to)
|
||||||
|
|
||||||
|
timeFrom, _ := timeRange.ParseFrom(
|
||||||
|
legacydata.WithLocation(timezone),
|
||||||
|
)
|
||||||
|
timeTo, _ := timeRange.ParseTo(
|
||||||
|
legacydata.WithLocation(timezone),
|
||||||
|
)
|
||||||
|
timeToAsEpoch := timeTo.UnixMilli()
|
||||||
|
timeFromAsEpoch := timeFrom.UnixMilli()
|
||||||
|
|
||||||
|
// Were using epoch ms because this is used to build a MetricRequest, which is used by query caching, which want the time range in epoch milliseconds.
|
||||||
|
return models.TimeSettings{
|
||||||
|
From: strconv.FormatInt(timeFromAsEpoch, 10),
|
||||||
|
To: strconv.FormatInt(timeToAsEpoch, 10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns from, to and timezone from the request if the timeSelection is enabled or the dashboard default values
|
||||||
|
func getTimeRangeValuesOrDefault(reqDTO models.PublicDashboardQueryDTO, d *dashboards.Dashboard, timeSelectionEnabled bool) (string, string, *time.Location) {
|
||||||
|
from := d.Data.GetPath("time", "from").MustString()
|
||||||
|
to := d.Data.GetPath("time", "to").MustString()
|
||||||
|
dashboardTimezone := d.Data.GetPath("timezone").MustString()
|
||||||
|
|
||||||
|
// we use the values from the request if the time selection is enabled and the values are valid
|
||||||
|
if timeSelectionEnabled {
|
||||||
|
if reqDTO.TimeRange.From != "" && reqDTO.TimeRange.To != "" {
|
||||||
|
from = reqDTO.TimeRange.From
|
||||||
|
to = reqDTO.TimeRange.To
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqDTO.TimeRange.Timezone != "" {
|
||||||
|
if userTimezone, err := time.LoadLocation(reqDTO.TimeRange.Timezone); err == nil {
|
||||||
|
return from, to, userTimezone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the dashboardTimezone is blank or there is an error default is UTC
|
||||||
|
timezone, err := time.LoadLocation(dashboardTimezone)
|
||||||
|
if err != nil {
|
||||||
|
return from, to, time.UTC
|
||||||
|
}
|
||||||
|
|
||||||
|
return from, to, timezone
|
||||||
|
}
|
||||||
|
@ -3,7 +3,9 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
@ -26,6 +28,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -1229,7 +1232,6 @@ func TestBuildMetricRequest(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("extracts queries from provided dashboard", func(t *testing.T) {
|
t.Run("extracts queries from provided dashboard", func(t *testing.T) {
|
||||||
reqDTO, err := service.buildMetricRequest(
|
reqDTO, err := service.buildMetricRequest(
|
||||||
context.Background(),
|
|
||||||
publicDashboard,
|
publicDashboard,
|
||||||
publicDashboardPD,
|
publicDashboardPD,
|
||||||
1,
|
1,
|
||||||
@ -1280,7 +1282,6 @@ func TestBuildMetricRequest(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("returns an error when panel missing", func(t *testing.T) {
|
t.Run("returns an error when panel missing", func(t *testing.T) {
|
||||||
_, err := service.buildMetricRequest(
|
_, err := service.buildMetricRequest(
|
||||||
context.Background(),
|
|
||||||
publicDashboard,
|
publicDashboard,
|
||||||
publicDashboardPD,
|
publicDashboardPD,
|
||||||
49,
|
49,
|
||||||
@ -1319,7 +1320,6 @@ func TestBuildMetricRequest(t *testing.T) {
|
|||||||
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashWithHiddenQuery", 1, 0, true, []map[string]interface{}{}, customPanels)
|
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashWithHiddenQuery", 1, 0, true, []map[string]interface{}{}, customPanels)
|
||||||
|
|
||||||
reqDTO, err := service.buildMetricRequest(
|
reqDTO, err := service.buildMetricRequest(
|
||||||
context.Background(),
|
|
||||||
publicDashboard,
|
publicDashboard,
|
||||||
publicDashboardPD,
|
publicDashboardPD,
|
||||||
1,
|
1,
|
||||||
@ -1611,6 +1611,158 @@ func TestSanitizeMetadataFromQueryData(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildTimeSettings(t *testing.T) {
|
||||||
|
var defaultDashboardData = simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"time": map[string]interface{}{
|
||||||
|
"from": "2022-09-01T00:00:00.000Z", "to": "2022-09-01T12:00:00.000Z",
|
||||||
|
},
|
||||||
|
"timezone": "America/Argentina/Mendoza",
|
||||||
|
})
|
||||||
|
|
||||||
|
defaultFromMs, defaultToMs := internal.GetTimeRangeFromDashboard(t, defaultDashboardData)
|
||||||
|
|
||||||
|
fakeTimezone, _ := time.LoadLocation("Europe/Madrid")
|
||||||
|
fakeNow := time.Date(2018, 12, 9, 20, 30, 0, 0, fakeTimezone)
|
||||||
|
|
||||||
|
// stub time range construction to have a fixed time.Now and be able to tests relative time ranges
|
||||||
|
NewDataTimeRange = func(from, to string) legacydata.DataTimeRange {
|
||||||
|
return legacydata.DataTimeRange{
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
Now: fakeNow,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startOfYesterdayMadrid, endOfYesterdayMadrid := getStartAndEndOfTheDayBefore(fakeNow, "Europe/Madrid")
|
||||||
|
|
||||||
|
// the day before fakeNow in Australia/Sydney timezone is not the same day before as in Europe/Madrid
|
||||||
|
startOfYesterdaySydney, endOfYesterdaySydney := getStartAndEndOfTheDayBefore(fakeNow, "Australia/Sydney")
|
||||||
|
startOfYesterdayUTC, endOfYesterdayUTC := getStartAndEndOfTheDayBefore(fakeNow, "UTC")
|
||||||
|
|
||||||
|
selectionFromMs := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||||
|
selectionToMs := strconv.FormatInt(time.Now().Add(time.Hour).UnixMilli(), 10)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
dashboard *dashboards.Dashboard
|
||||||
|
pubdash *PublicDashboard
|
||||||
|
reqDTO PublicDashboardQueryDTO
|
||||||
|
want TimeSettings
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should return default time range with timezone with relative time range",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: buildJsonDataWithTimeRange("now-1d/d", "now-1d/d", "Australia/Sydney")},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: strconv.FormatInt(startOfYesterdaySydney.UnixMilli(), 10),
|
||||||
|
To: strconv.FormatInt(endOfYesterdaySydney.UnixMilli(), 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return default time range with UTC timezone with relative time range with unknown timezone",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: buildJsonDataWithTimeRange("now-1d/d", "now-1d/d", "browser")},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: strconv.FormatInt(startOfYesterdayUTC.UnixMilli(), 10),
|
||||||
|
To: strconv.FormatInt(endOfYesterdayUTC.UnixMilli(), 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return default time range with timezone with relative time range if time selection is not enabled",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: buildJsonDataWithTimeRange("now-1d/d", "now-1d/d", "Australia/Sydney")},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{
|
||||||
|
TimeRange: TimeRangeDTO{
|
||||||
|
Timezone: "Europe/Madrid",
|
||||||
|
}},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: strconv.FormatInt(startOfYesterdaySydney.UnixMilli(), 10),
|
||||||
|
To: strconv.FormatInt(endOfYesterdaySydney.UnixMilli(), 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return user time range with dashboard timezone with relative time range",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: buildJsonDataWithTimeRange("now-1d/d", "now-1d/d", "Europe/Madrid")},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: strconv.FormatInt(startOfYesterdayMadrid.UnixMilli(), 10),
|
||||||
|
To: strconv.FormatInt(endOfYesterdayMadrid.UnixMilli(), 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return user time range with dashboard timezone with relative time range for the last hour",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: buildJsonDataWithTimeRange("now-1h", "now", "Europe/Madrid")},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: strconv.FormatInt(fakeNow.Add(-time.Hour).UnixMilli(), 10),
|
||||||
|
To: strconv.FormatInt(fakeNow.UnixMilli(), 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should use dashboard time if pubdash time empty",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: defaultDashboardData},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: defaultFromMs,
|
||||||
|
To: defaultToMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should use dashboard time when time selection is disabled",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: defaultDashboardData},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: false},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{
|
||||||
|
TimeRange: TimeRangeDTO{
|
||||||
|
From: selectionFromMs,
|
||||||
|
To: selectionToMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: defaultFromMs,
|
||||||
|
To: defaultToMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should use selected values if time selection is enabled",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: defaultDashboardData},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: true},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{
|
||||||
|
TimeRange: TimeRangeDTO{
|
||||||
|
From: selectionFromMs,
|
||||||
|
To: selectionToMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: selectionFromMs,
|
||||||
|
To: selectionToMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should use default values if time selection is enabled but the time range is empty",
|
||||||
|
dashboard: &dashboards.Dashboard{Data: defaultDashboardData},
|
||||||
|
pubdash: &PublicDashboard{TimeSelectionEnabled: true},
|
||||||
|
reqDTO: PublicDashboardQueryDTO{
|
||||||
|
TimeRange: TimeRangeDTO{},
|
||||||
|
},
|
||||||
|
want: TimeSettings{
|
||||||
|
From: defaultFromMs,
|
||||||
|
To: defaultToMs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.want, buildTimeSettings(test.dashboard, test.reqDTO, test.pubdash))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func groupQueriesByDataSource(t *testing.T, queries []*simplejson.Json) (result [][]*simplejson.Json) {
|
func groupQueriesByDataSource(t *testing.T, queries []*simplejson.Json) (result [][]*simplejson.Json) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
byDataSource := make(map[string][]*simplejson.Json)
|
byDataSource := make(map[string][]*simplejson.Json)
|
||||||
@ -1626,3 +1778,21 @@ func groupQueriesByDataSource(t *testing.T, queries []*simplejson.Json) (result
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getStartAndEndOfTheDayBefore(fakeNow time.Time, timezoneName string) (time.Time, time.Time) {
|
||||||
|
timezone, _ := time.LoadLocation(timezoneName)
|
||||||
|
fakeNowWithTimezone := fakeNow.In(timezone)
|
||||||
|
yy, mm, dd := fakeNowWithTimezone.Add(-24 * time.Hour).Date()
|
||||||
|
startOfYesterdaySydney := time.Date(yy, mm, dd, 0, 0, 0, 0, timezone)
|
||||||
|
endOfYesterdaySydney := time.Date(yy, mm, dd, 23, 59, 59, 999999999, timezone)
|
||||||
|
return startOfYesterdaySydney, endOfYesterdaySydney
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildJsonDataWithTimeRange(from, to, timezone string) *simplejson.Json {
|
||||||
|
return simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"time": map[string]interface{}{
|
||||||
|
"from": from, "to": to,
|
||||||
|
},
|
||||||
|
"timezone": timezone,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -40,7 +40,7 @@ func TestValidateQueryPublicDashboardRequest(t *testing.T) {
|
|||||||
req: PublicDashboardQueryDTO{
|
req: PublicDashboardQueryDTO{
|
||||||
IntervalMs: 1000,
|
IntervalMs: 1000,
|
||||||
MaxDataPoints: 1000,
|
MaxDataPoints: 1000,
|
||||||
TimeRange: TimeSettings{
|
TimeRange: TimeRangeDTO{
|
||||||
From: "now-1h",
|
From: "now-1h",
|
||||||
To: "now",
|
To: "now",
|
||||||
},
|
},
|
||||||
@ -88,7 +88,7 @@ func TestValidateQueryPublicDashboardRequest(t *testing.T) {
|
|||||||
name: "Returns validation error when time range from is invalid",
|
name: "Returns validation error when time range from is invalid",
|
||||||
args: args{
|
args: args{
|
||||||
req: PublicDashboardQueryDTO{
|
req: PublicDashboardQueryDTO{
|
||||||
TimeRange: TimeSettings{
|
TimeRange: TimeRangeDTO{
|
||||||
From: "invalid",
|
From: "invalid",
|
||||||
To: "1622560000000",
|
To: "1622560000000",
|
||||||
},
|
},
|
||||||
@ -103,7 +103,7 @@ func TestValidateQueryPublicDashboardRequest(t *testing.T) {
|
|||||||
name: "Returns validation error when time range to is invalid",
|
name: "Returns validation error when time range to is invalid",
|
||||||
args: args{
|
args: args{
|
||||||
req: PublicDashboardQueryDTO{
|
req: PublicDashboardQueryDTO{
|
||||||
TimeRange: TimeSettings{
|
TimeRange: TimeRangeDTO{
|
||||||
From: "1622560000000",
|
From: "1622560000000",
|
||||||
To: "invalid",
|
To: "invalid",
|
||||||
},
|
},
|
||||||
@ -118,7 +118,7 @@ func TestValidateQueryPublicDashboardRequest(t *testing.T) {
|
|||||||
name: "Returns validation error when time range from or to is blank",
|
name: "Returns validation error when time range from or to is blank",
|
||||||
args: args{
|
args: args{
|
||||||
req: PublicDashboardQueryDTO{
|
req: PublicDashboardQueryDTO{
|
||||||
TimeRange: TimeSettings{
|
TimeRange: TimeRangeDTO{
|
||||||
From: "",
|
From: "",
|
||||||
To: "",
|
To: "",
|
||||||
},
|
},
|
||||||
|
@ -113,7 +113,11 @@ export class PublicDashboardDataSource extends DataSourceApi<DataQuery, DataSour
|
|||||||
intervalMs,
|
intervalMs,
|
||||||
maxDataPoints,
|
maxDataPoints,
|
||||||
queryCachingTTL,
|
queryCachingTTL,
|
||||||
timeRange: { from: fromRange.valueOf().toString(), to: toRange.valueOf().toString() },
|
timeRange: {
|
||||||
|
from: fromRange.valueOf().toString(),
|
||||||
|
to: toRange.valueOf().toString(),
|
||||||
|
timezone: this.getBrowserTimezone(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return getBackendSrv()
|
return getBackendSrv()
|
||||||
@ -155,4 +159,9 @@ export class PublicDashboardDataSource extends DataSourceApi<DataQuery, DataSour
|
|||||||
testDatasource(): Promise<TestDataSourceResponse> {
|
testDatasource(): Promise<TestDataSourceResponse> {
|
||||||
return Promise.resolve({ message: '', status: '' });
|
return Promise.resolve({ message: '', status: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to get the browser timezone otherwise return blank
|
||||||
|
getBrowserTimezone(): string {
|
||||||
|
return window.Intl?.DateTimeFormat().resolvedOptions()?.timeZone || '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user