From 108678a8ccee3c4e8cc39fb6870f4c426a5ad22d Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Wed, 7 Sep 2022 12:08:52 -0600 Subject: [PATCH] Public Dashboards: Only use queries from dashboard for caching (#54596) Don't accept frontend queries for query caching when its from a public dashboard. Use queries on the dashboard instead. --- pkg/services/publicdashboards/api/api.go | 4 +- .../publicdashboards/internal/test_helpers.go | 24 ++++++ .../publicdashboards/models/models.go | 23 +++-- .../publicdashboards/models/models_test.go | 29 +++---- .../public_dashboard_service_mock.go | 79 ++++++++++++------ .../publicdashboards/publicdashboard.go | 18 ++-- .../publicdashboards/service/service.go | 41 +++++---- .../publicdashboards/service/service_test.go | 83 +++++++++++++++---- .../publicdashboards/validation/validator.go | 2 +- 9 files changed, 204 insertions(+), 99 deletions(-) create mode 100644 pkg/services/publicdashboards/internal/test_helpers.go diff --git a/pkg/services/publicdashboards/api/api.go b/pkg/services/publicdashboards/api/api.go index 08e3c8d08de..62f13aff5c8 100644 --- a/pkg/services/publicdashboards/api/api.go +++ b/pkg/services/publicdashboards/api/api.go @@ -158,8 +158,8 @@ func (api *Api) QueryPublicDashboard(c *models.ReqContext) response.Response { return response.Error(http.StatusBadRequest, "invalid panel ID", err) } - reqDTO := &PublicDashboardQueryDTO{} - if err = web.Bind(c.Req, reqDTO); err != nil { + reqDTO := PublicDashboardQueryDTO{} + if err = web.Bind(c.Req, &reqDTO); err != nil { return response.Error(http.StatusBadRequest, "bad request data", err) } diff --git a/pkg/services/publicdashboards/internal/test_helpers.go b/pkg/services/publicdashboards/internal/test_helpers.go new file mode 100644 index 00000000000..e321f1da8e0 --- /dev/null +++ b/pkg/services/publicdashboards/internal/test_helpers.go @@ -0,0 +1,24 @@ +package internal + +import ( + "strconv" + "testing" + "time" + + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/stretchr/testify/require" +) + +// Gets time range from dashboard in unix milliseconds +func GetTimeRangeFromDashboard(t *testing.T, dashboardData *simplejson.Json) (string, string) { + to := dashboardData.GetPath("time", "to").MustString() + from := dashboardData.GetPath("time", "from").MustString() + toTime, err := time.Parse("2006-01-02T15:04:05.000Z", to) + require.NoError(t, err) + fromTime, err := time.Parse("2006-01-02T15:04:05.000Z", from) + require.NoError(t, err) + toUnixMilli := strconv.FormatInt(toTime.UnixMilli(), 10) + fromUnixMilli := strconv.FormatInt(fromTime.UnixMilli(), 10) + + return fromUnixMilli, toUnixMilli +} diff --git a/pkg/services/publicdashboards/models/models.go b/pkg/services/publicdashboards/models/models.go index a9b31d5e0a8..5c9007bf33a 100644 --- a/pkg/services/publicdashboards/models/models.go +++ b/pkg/services/publicdashboards/models/models.go @@ -1,10 +1,12 @@ package models import ( + "strconv" "time" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/tsdb/legacydata" ) // PublicDashboardErr represents a dashboard error. @@ -81,24 +83,21 @@ type TimeSettings struct { // build time settings object from json on public dashboard. If empty, use // defaults on the dashboard -func (pd PublicDashboard) BuildTimeSettings(dashboard *models.Dashboard) *TimeSettings { - ts := &TimeSettings{ - From: dashboard.Data.GetPath("time", "from").MustString(), - To: dashboard.Data.GetPath("time", "to").MustString(), +func (pd PublicDashboard) BuildTimeSettings(dashboard *models.Dashboard) TimeSettings { + from := dashboard.Data.GetPath("time", "from").MustString() + to := dashboard.Data.GetPath("time", "to").MustString() + 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. + ts := TimeSettings{ + From: strconv.FormatInt(timeRange.GetFromAsMsEpoch(), 10), + To: strconv.FormatInt(timeRange.GetToAsMsEpoch(), 10), } if pd.TimeSettings == nil { return ts } - // merge time settings from public dashboard - to := pd.TimeSettings.Get("to").MustString("") - from := pd.TimeSettings.Get("from").MustString("") - if to != "" && from != "" { - ts.From = from - ts.To = to - } - return ts } diff --git a/pkg/services/publicdashboards/models/models_test.go b/pkg/services/publicdashboards/models/models_test.go index b5278c247d8..160b0387491 100644 --- a/pkg/services/publicdashboards/models/models_test.go +++ b/pkg/services/publicdashboards/models/models_test.go @@ -5,6 +5,7 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/publicdashboards/internal" "github.com/stretchr/testify/assert" ) @@ -13,38 +14,30 @@ func TestPublicDashboardTableName(t *testing.T) { } func TestBuildTimeSettings(t *testing.T) { - var dashboardData = simplejson.NewFromAny(map[string]interface{}{"time": map[string]interface{}{"from": "now-8", "to": "now"}}) + 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"}}) + fromMs, toMs := internal.GetTimeRangeFromDashboard(t, dashboardData) testCases := []struct { name string dashboard *models.Dashboard pubdash *PublicDashboard - timeResult *TimeSettings + timeResult TimeSettings }{ { name: "should use dashboard time if pubdash time empty", dashboard: &models.Dashboard{Data: dashboardData}, pubdash: &PublicDashboard{}, - timeResult: &TimeSettings{ - From: "now-8", - To: "now", + timeResult: TimeSettings{ + From: fromMs, + To: toMs, }, }, { - name: "should use dashboard time if pubdash to/from empty", - dashboard: &models.Dashboard{Data: dashboardData}, - pubdash: &PublicDashboard{}, - timeResult: &TimeSettings{ - From: "now-8", - To: "now", - }, - }, - { - name: "should use pubdash time", + name: "should use dashboard time even if pubdash time exists", dashboard: &models.Dashboard{Data: dashboardData}, pubdash: &PublicDashboard{TimeSettings: simplejson.NewFromAny(map[string]interface{}{"from": "now-12", "to": "now"})}, - timeResult: &TimeSettings{ - From: "now-12", - To: "now", + timeResult: TimeSettings{ + From: fromMs, + To: toMs, }, }, } diff --git a/pkg/services/publicdashboards/public_dashboard_service_mock.go b/pkg/services/publicdashboards/public_dashboard_service_mock.go index 8d683be9748..898306bf485 100644 --- a/pkg/services/publicdashboards/public_dashboard_service_mock.go +++ b/pkg/services/publicdashboards/public_dashboard_service_mock.go @@ -4,13 +4,19 @@ package publicdashboards import ( context "context" - testing "testing" - "github.com/grafana/grafana-plugin-sdk-go/backend" - models "github.com/grafana/grafana/pkg/models" - publicdashboardsmodels "github.com/grafana/grafana/pkg/services/publicdashboards/models" + backend "github.com/grafana/grafana-plugin-sdk-go/backend" + + dtos "github.com/grafana/grafana/pkg/api/dtos" + mock "github.com/stretchr/testify/mock" + models "github.com/grafana/grafana/pkg/models" + + publicdashboardsmodels "github.com/grafana/grafana/pkg/services/publicdashboards/models" + + testing "testing" + user "github.com/grafana/grafana/pkg/services/user" ) @@ -63,27 +69,6 @@ func (_m *FakePublicDashboardService) BuildAnonymousUser(ctx context.Context, da return r0, r1 } -// GetQueryDataResponse provides a mock function with given fields: ctx, skipCache, reqDTO, panelId, accessToken -func (_m *FakePublicDashboardService) GetQueryDataResponse(ctx context.Context, skipCache bool, reqDTO *publicdashboardsmodels.PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) { - ret := _m.Called(ctx, skipCache, reqDTO, panelId, accessToken) - - var r0 *backend.QueryDataResponse - if rf, ok := ret.Get(0).(func(context.Context, bool, *publicdashboardsmodels.PublicDashboardQueryDTO, int64, string) *backend.QueryDataResponse); ok { - r0 = rf(ctx, skipCache, reqDTO, panelId, accessToken) - } else { - r0 = ret.Get(0).(*backend.QueryDataResponse) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, bool, *publicdashboardsmodels.PublicDashboardQueryDTO, int64, string) error); ok { - r1 = rf(ctx, skipCache, reqDTO, panelId, accessToken) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetDashboard provides a mock function with given fields: ctx, dashboardUid func (_m *FakePublicDashboardService) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) { ret := _m.Called(ctx, dashboardUid) @@ -107,6 +92,27 @@ func (_m *FakePublicDashboardService) GetDashboard(ctx context.Context, dashboar return r0, r1 } +// GetMetricRequest provides a mock function with given fields: ctx, dashboard, publicDashboard, panelId, reqDTO +func (_m *FakePublicDashboardService) GetMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *publicdashboardsmodels.PublicDashboard, panelId int64, reqDTO publicdashboardsmodels.PublicDashboardQueryDTO) (dtos.MetricRequest, error) { + ret := _m.Called(ctx, dashboard, publicDashboard, panelId, reqDTO) + + var r0 dtos.MetricRequest + if rf, ok := ret.Get(0).(func(context.Context, *models.Dashboard, *publicdashboardsmodels.PublicDashboard, int64, publicdashboardsmodels.PublicDashboardQueryDTO) dtos.MetricRequest); ok { + r0 = rf(ctx, dashboard, publicDashboard, panelId, reqDTO) + } else { + r0 = ret.Get(0).(dtos.MetricRequest) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *models.Dashboard, *publicdashboardsmodels.PublicDashboard, int64, publicdashboardsmodels.PublicDashboardQueryDTO) error); ok { + r1 = rf(ctx, dashboard, publicDashboard, panelId, reqDTO) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetPublicDashboard provides a mock function with given fields: ctx, accessToken func (_m *FakePublicDashboardService) GetPublicDashboard(ctx context.Context, accessToken string) (*publicdashboardsmodels.PublicDashboard, *models.Dashboard, error) { ret := _m.Called(ctx, accessToken) @@ -162,6 +168,29 @@ func (_m *FakePublicDashboardService) GetPublicDashboardConfig(ctx context.Conte return r0, r1 } +// GetQueryDataResponse provides a mock function with given fields: ctx, skipCache, reqDTO, panelId, accessToken +func (_m *FakePublicDashboardService) GetQueryDataResponse(ctx context.Context, skipCache bool, reqDTO publicdashboardsmodels.PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) { + ret := _m.Called(ctx, skipCache, reqDTO, panelId, accessToken) + + var r0 *backend.QueryDataResponse + if rf, ok := ret.Get(0).(func(context.Context, bool, publicdashboardsmodels.PublicDashboardQueryDTO, int64, string) *backend.QueryDataResponse); ok { + r0 = rf(ctx, skipCache, reqDTO, panelId, accessToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*backend.QueryDataResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, bool, publicdashboardsmodels.PublicDashboardQueryDTO, int64, string) error); ok { + r1 = rf(ctx, skipCache, reqDTO, panelId, accessToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // PublicDashboardEnabled provides a mock function with given fields: ctx, dashboardUid func (_m *FakePublicDashboardService) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) { ret := _m.Called(ctx, dashboardUid) diff --git a/pkg/services/publicdashboards/publicdashboard.go b/pkg/services/publicdashboards/publicdashboard.go index 82d583fc7bf..32ebd37ddad 100644 --- a/pkg/services/publicdashboards/publicdashboard.go +++ b/pkg/services/publicdashboards/publicdashboard.go @@ -4,6 +4,7 @@ import ( "context" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/models" . "github.com/grafana/grafana/pkg/services/publicdashboards/models" "github.com/grafana/grafana/pkg/services/user" @@ -13,25 +14,26 @@ import ( //go:generate mockery --name Service --structname FakePublicDashboardService --inpackage --filename public_dashboard_service_mock.go type Service interface { + AccessTokenExists(ctx context.Context, accessToken string) (bool, error) BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*user.SignedInUser, error) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) + GetMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64, reqDTO PublicDashboardQueryDTO) (dtos.MetricRequest, error) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) - SavePublicDashboardConfig(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) - GetQueryDataResponse(ctx context.Context, skipCache bool, reqDTO *PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) + GetQueryDataResponse(ctx context.Context, skipCache bool, reqDTO PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) - AccessTokenExists(ctx context.Context, accessToken string) (bool, error) + SavePublicDashboardConfig(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) } //go:generate mockery --name Store --structname FakePublicDashboardStore --inpackage --filename public_dashboard_store_mock.go type Store interface { - GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) + AccessTokenExists(ctx context.Context, accessToken string) (bool, error) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) - GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) - GetPublicDashboardByUid(ctx context.Context, uid string) (*PublicDashboard, error) GenerateNewPublicDashboardUid(ctx context.Context) (string, error) + GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) + GetPublicDashboardByUid(ctx context.Context, uid string) (*PublicDashboard, error) + GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) + PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error UpdatePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error - PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) - AccessTokenExists(ctx context.Context, accessToken string) (bool, error) } diff --git a/pkg/services/publicdashboards/service/service.go b/pkg/services/publicdashboards/service/service.go index dbe1fdac2b8..a0a56f3810c 100644 --- a/pkg/services/publicdashboards/service/service.go +++ b/pkg/services/publicdashboards/service/service.go @@ -190,23 +190,13 @@ func (pd *PublicDashboardServiceImpl) updatePublicDashboardConfig(ctx context.Co return dto.PublicDashboard.Uid, pd.store.UpdatePublicDashboardConfig(ctx, cmd) } -func (pd *PublicDashboardServiceImpl) GetQueryDataResponse(ctx context.Context, skipCache bool, reqDTO *PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) { - if err := validation.ValidateQueryPublicDashboardRequest(reqDTO); err != nil { - return nil, ErrPublicDashboardBadRequest - } - +func (pd *PublicDashboardServiceImpl) GetQueryDataResponse(ctx context.Context, skipCache bool, queryDto PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) { publicDashboard, dashboard, err := pd.GetPublicDashboard(ctx, accessToken) if err != nil { return nil, err } - metricReqDTO, err := pd.buildPublicDashboardMetricRequest( - ctx, - dashboard, - publicDashboard, - panelId, - reqDTO, - ) + metricReq, err := pd.GetMetricRequest(ctx, dashboard, publicDashboard, panelId, queryDto) if err != nil { return nil, err } @@ -216,12 +206,31 @@ func (pd *PublicDashboardServiceImpl) GetQueryDataResponse(ctx context.Context, return nil, err } - return pd.QueryDataService.QueryDataMultipleSources(ctx, anonymousUser, skipCache, metricReqDTO, true) + return pd.QueryDataService.QueryDataMultipleSources(ctx, anonymousUser, skipCache, metricReq, true) } -// BuildPublicDashboardMetricRequest merges public dashboard parameters with +func (pd *PublicDashboardServiceImpl) GetMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64, queryDto PublicDashboardQueryDTO) (dtos.MetricRequest, error) { + if err := validation.ValidateQueryPublicDashboardRequest(queryDto); err != nil { + return dtos.MetricRequest{}, ErrPublicDashboardBadRequest + } + + metricReqDTO, err := pd.buildMetricRequest( + ctx, + dashboard, + publicDashboard, + panelId, + queryDto, + ) + if err != nil { + return dtos.MetricRequest{}, err + } + + return metricReqDTO, nil +} + +// buildMetricRequest merges public dashboard parameters with // dashboard and returns a metrics request to be sent to query backend -func (pd *PublicDashboardServiceImpl) buildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64, reqDTO *PublicDashboardQueryDTO) (dtos.MetricRequest, error) { +func (pd *PublicDashboardServiceImpl) buildMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64, reqDTO PublicDashboardQueryDTO) (dtos.MetricRequest, error) { // group queries by panel queriesByPanel := queryModels.GroupQueriesByPanelId(dashboard.Data) queries, ok := queriesByPanel[panelId] @@ -290,7 +299,7 @@ func GenerateAccessToken() (string, error) { // this is an additional validation, all data sources implements QueryData interface and should have proper validations // of these limits // for the maxDataPoints we took a hard limit from prometheus which is 11000 -func (pd *PublicDashboardServiceImpl) getSafeIntervalAndMaxDataPoints(reqDTO *PublicDashboardQueryDTO, ts *TimeSettings) (int64, int64) { +func (pd *PublicDashboardServiceImpl) getSafeIntervalAndMaxDataPoints(reqDTO PublicDashboardQueryDTO, ts TimeSettings) (int64, int64) { // arbitrary max value for all data sources, it is actually a hard limit defined in prometheus safeResolution := int64(11000) diff --git a/pkg/services/publicdashboards/service/service_test.go b/pkg/services/publicdashboards/service/service_test.go index 83c6fca3762..ca0852fbf31 100644 --- a/pkg/services/publicdashboards/service/service_test.go +++ b/pkg/services/publicdashboards/service/service_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/publicdashboards/internal" "github.com/grafana/grafana/pkg/services/user" "github.com/google/uuid" @@ -18,7 +19,7 @@ import ( "github.com/grafana/grafana/pkg/models" dashboardsDB "github.com/grafana/grafana/pkg/services/dashboards/database" . "github.com/grafana/grafana/pkg/services/publicdashboards" - database "github.com/grafana/grafana/pkg/services/publicdashboards/database" + "github.com/grafana/grafana/pkg/services/publicdashboards/database" . "github.com/grafana/grafana/pkg/services/publicdashboards/models" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/tsdb/intervalv2" @@ -352,13 +353,57 @@ func TestBuildAnonymousUser(t *testing.T) { }) } -func TestBuildPublicDashboardMetricRequest(t *testing.T) { +func TestGetMetricRequest(t *testing.T) { + sqlStore := sqlstore.InitTestDB(t) + dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures()) + publicdashboardStore := database.ProvideStore(sqlStore) + dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}) + publicDashboard := &PublicDashboard{ + Uid: "1", + DashboardUid: dashboard.Uid, + IsEnabled: true, + AccessToken: "abc123", + } + service := &PublicDashboardServiceImpl{ + log: log.New("test.logger"), + store: publicdashboardStore, + intervalCalculator: intervalv2.NewCalculator(), + } + + t.Run("will return an error when validation fails", func(t *testing.T) { + publicDashboardQueryDTO := PublicDashboardQueryDTO{ + IntervalMs: int64(-1), + MaxDataPoints: int64(-1), + } + + _, err := service.GetMetricRequest(context.Background(), dashboard, publicDashboard, 1, publicDashboardQueryDTO) + + require.Error(t, err) + }) + + t.Run("will not return an error when validation succeeds", func(t *testing.T) { + publicDashboardQueryDTO := PublicDashboardQueryDTO{ + IntervalMs: int64(1), + MaxDataPoints: int64(1), + } + from, to := internal.GetTimeRangeFromDashboard(t, dashboard.Data) + + metricReq, err := service.GetMetricRequest(context.Background(), dashboard, publicDashboard, 1, publicDashboardQueryDTO) + + require.NoError(t, err) + require.Equal(t, from, metricReq.From) + require.Equal(t, to, metricReq.To) + }) +} + +func TestBuildMetricRequest(t *testing.T) { sqlStore := sqlstore.InitTestDB(t) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures()) publicdashboardStore := database.ProvideStore(sqlStore) publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}) nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 1, 0, true, []map[string]interface{}{}) + from, to := internal.GetTimeRangeFromDashboard(t, publicDashboard.Data) service := &PublicDashboardServiceImpl{ log: log.New("test.logger"), @@ -366,7 +411,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) { intervalCalculator: intervalv2.NewCalculator(), } - publicDashboardQueryDTO := &PublicDashboardQueryDTO{ + publicDashboardQueryDTO := PublicDashboardQueryDTO{ IntervalMs: int64(10000000), MaxDataPoints: int64(200), } @@ -400,7 +445,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) { require.NoError(t, err) t.Run("extracts queries from provided dashboard", func(t *testing.T) { - reqDTO, err := service.buildPublicDashboardMetricRequest( + reqDTO, err := service.buildMetricRequest( context.Background(), publicDashboard, publicDashboardPD, @@ -409,8 +454,8 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) { ) require.NoError(t, err) - require.Equal(t, timeSettings.Get("from").MustString(), reqDTO.From) - require.Equal(t, timeSettings.Get("to").MustString(), reqDTO.To) + require.Equal(t, from, reqDTO.From) + require.Equal(t, to, reqDTO.To) for i := range reqDTO.Queries { require.Equal(t, publicDashboardQueryDTO.IntervalMs, reqDTO.Queries[i].Get("intervalMs").MustInt64()) @@ -449,7 +494,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) { }) t.Run("returns an error when panel missing", func(t *testing.T) { - _, err := service.buildPublicDashboardMetricRequest( + _, err := service.buildMetricRequest( context.Background(), publicDashboard, publicDashboardPD, @@ -514,6 +559,10 @@ func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardSto "templating": map[string]interface{}{ "list": templateVars, }, + "time": map[string]interface{}{ + "from": "2022-09-01T00:00:00.000Z", + "to": "2022-09-01T12:00:00.000Z", + }, }), } dash, err := dashboardStore.SaveDashboard(cmd) @@ -526,8 +575,8 @@ func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardSto func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T) { type args struct { - reqDTO *PublicDashboardQueryDTO - ts *TimeSettings + reqDTO PublicDashboardQueryDTO + ts TimeSettings } tests := []struct { name string @@ -538,11 +587,11 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T { name: "return original interval", args: args{ - reqDTO: &PublicDashboardQueryDTO{ + reqDTO: PublicDashboardQueryDTO{ IntervalMs: 10000, MaxDataPoints: 300, }, - ts: &TimeSettings{ + ts: TimeSettings{ From: "now-3h", To: "now", }, @@ -553,11 +602,11 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T { name: "return safe interval because of a small interval", args: args{ - reqDTO: &PublicDashboardQueryDTO{ + reqDTO: PublicDashboardQueryDTO{ IntervalMs: 1000, MaxDataPoints: 300, }, - ts: &TimeSettings{ + ts: TimeSettings{ From: "now-6h", To: "now", }, @@ -568,11 +617,11 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T { name: "return safe interval for long time range", args: args{ - reqDTO: &PublicDashboardQueryDTO{ + reqDTO: PublicDashboardQueryDTO{ IntervalMs: 100, MaxDataPoints: 300, }, - ts: &TimeSettings{ + ts: TimeSettings{ From: "now-90d", To: "now", }, @@ -583,8 +632,8 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T { name: "return safe interval when reqDTO is empty", args: args{ - reqDTO: &PublicDashboardQueryDTO{}, - ts: &TimeSettings{ + reqDTO: PublicDashboardQueryDTO{}, + ts: TimeSettings{ From: "now-90d", To: "now", }, diff --git a/pkg/services/publicdashboards/validation/validator.go b/pkg/services/publicdashboards/validation/validator.go index 4d23c22c7d8..888c80ffea1 100644 --- a/pkg/services/publicdashboards/validation/validator.go +++ b/pkg/services/publicdashboards/validation/validator.go @@ -21,7 +21,7 @@ func hasTemplateVariables(dashboard *models.Dashboard) bool { return len(templateVariables) > 0 } -func ValidateQueryPublicDashboardRequest(req *publicDashboardModels.PublicDashboardQueryDTO) error { +func ValidateQueryPublicDashboardRequest(req publicDashboardModels.PublicDashboardQueryDTO) error { if req.IntervalMs < 0 { return fmt.Errorf("intervalMS should be greater than 0") }