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.
This commit is contained in:
owensmallwood
2022-09-07 12:08:52 -06:00
committed by GitHub
parent 4223d3a6a7
commit 108678a8cc
9 changed files with 204 additions and 99 deletions
+2 -2
View File
@@ -158,8 +158,8 @@ func (api *Api) QueryPublicDashboard(c *models.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "invalid panel ID", err) return response.Error(http.StatusBadRequest, "invalid panel ID", err)
} }
reqDTO := &PublicDashboardQueryDTO{} reqDTO := PublicDashboardQueryDTO{}
if err = web.Bind(c.Req, reqDTO); err != nil { if err = web.Bind(c.Req, &reqDTO); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
@@ -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
}
+11 -12
View File
@@ -1,10 +1,12 @@
package models package models
import ( import (
"strconv"
"time" "time"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb/legacydata"
) )
// PublicDashboardErr represents a dashboard error. // PublicDashboardErr represents a dashboard error.
@@ -81,24 +83,21 @@ type TimeSettings struct {
// build time settings object from json on public dashboard. If empty, use // build time settings object from json on public dashboard. If empty, use
// defaults on the dashboard // defaults on the dashboard
func (pd PublicDashboard) BuildTimeSettings(dashboard *models.Dashboard) *TimeSettings { func (pd PublicDashboard) BuildTimeSettings(dashboard *models.Dashboard) TimeSettings {
ts := &TimeSettings{ from := dashboard.Data.GetPath("time", "from").MustString()
From: dashboard.Data.GetPath("time", "from").MustString(), to := dashboard.Data.GetPath("time", "to").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 { if pd.TimeSettings == nil {
return ts 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 return ts
} }
@@ -5,6 +5,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/publicdashboards/internal"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -13,38 +14,30 @@ func TestPublicDashboardTableName(t *testing.T) {
} }
func TestBuildTimeSettings(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 { testCases := []struct {
name string name string
dashboard *models.Dashboard dashboard *models.Dashboard
pubdash *PublicDashboard pubdash *PublicDashboard
timeResult *TimeSettings timeResult TimeSettings
}{ }{
{ {
name: "should use dashboard time if pubdash time empty", name: "should use dashboard time if pubdash time empty",
dashboard: &models.Dashboard{Data: dashboardData}, dashboard: &models.Dashboard{Data: dashboardData},
pubdash: &PublicDashboard{}, pubdash: &PublicDashboard{},
timeResult: &TimeSettings{ timeResult: TimeSettings{
From: "now-8", From: fromMs,
To: "now", To: toMs,
}, },
}, },
{ {
name: "should use dashboard time if pubdash to/from empty", name: "should use dashboard time even if pubdash time exists",
dashboard: &models.Dashboard{Data: dashboardData},
pubdash: &PublicDashboard{},
timeResult: &TimeSettings{
From: "now-8",
To: "now",
},
},
{
name: "should use pubdash time",
dashboard: &models.Dashboard{Data: dashboardData}, dashboard: &models.Dashboard{Data: dashboardData},
pubdash: &PublicDashboard{TimeSettings: simplejson.NewFromAny(map[string]interface{}{"from": "now-12", "to": "now"})}, pubdash: &PublicDashboard{TimeSettings: simplejson.NewFromAny(map[string]interface{}{"from": "now-12", "to": "now"})},
timeResult: &TimeSettings{ timeResult: TimeSettings{
From: "now-12", From: fromMs,
To: "now", To: toMs,
}, },
}, },
} }
@@ -4,13 +4,19 @@ package publicdashboards
import ( import (
context "context" context "context"
testing "testing"
"github.com/grafana/grafana-plugin-sdk-go/backend" backend "github.com/grafana/grafana-plugin-sdk-go/backend"
models "github.com/grafana/grafana/pkg/models"
publicdashboardsmodels "github.com/grafana/grafana/pkg/services/publicdashboards/models" dtos "github.com/grafana/grafana/pkg/api/dtos"
mock "github.com/stretchr/testify/mock" 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" user "github.com/grafana/grafana/pkg/services/user"
) )
@@ -63,27 +69,6 @@ func (_m *FakePublicDashboardService) BuildAnonymousUser(ctx context.Context, da
return r0, r1 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 // GetDashboard provides a mock function with given fields: ctx, dashboardUid
func (_m *FakePublicDashboardService) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) { func (_m *FakePublicDashboardService) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) {
ret := _m.Called(ctx, dashboardUid) ret := _m.Called(ctx, dashboardUid)
@@ -107,6 +92,27 @@ func (_m *FakePublicDashboardService) GetDashboard(ctx context.Context, dashboar
return r0, r1 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 // GetPublicDashboard provides a mock function with given fields: ctx, accessToken
func (_m *FakePublicDashboardService) GetPublicDashboard(ctx context.Context, accessToken string) (*publicdashboardsmodels.PublicDashboard, *models.Dashboard, error) { func (_m *FakePublicDashboardService) GetPublicDashboard(ctx context.Context, accessToken string) (*publicdashboardsmodels.PublicDashboard, *models.Dashboard, error) {
ret := _m.Called(ctx, accessToken) ret := _m.Called(ctx, accessToken)
@@ -162,6 +168,29 @@ func (_m *FakePublicDashboardService) GetPublicDashboardConfig(ctx context.Conte
return r0, r1 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 // PublicDashboardEnabled provides a mock function with given fields: ctx, dashboardUid
func (_m *FakePublicDashboardService) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) { func (_m *FakePublicDashboardService) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) {
ret := _m.Called(ctx, dashboardUid) ret := _m.Called(ctx, dashboardUid)
@@ -4,6 +4,7 @@ import (
"context" "context"
"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/models" "github.com/grafana/grafana/pkg/models"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models" . "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/user" "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 //go:generate mockery --name Service --structname FakePublicDashboardService --inpackage --filename public_dashboard_service_mock.go
type Service interface { type Service interface {
AccessTokenExists(ctx context.Context, accessToken string) (bool, error)
BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*user.SignedInUser, error) BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*user.SignedInUser, error)
GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error)
GetDashboard(ctx context.Context, dashboardUid string) (*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) 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) 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 //go:generate mockery --name Store --structname FakePublicDashboardStore --inpackage --filename public_dashboard_store_mock.go
type Store interface { 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) 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) 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 SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error
UpdatePublicDashboardConfig(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)
} }
@@ -190,23 +190,13 @@ func (pd *PublicDashboardServiceImpl) updatePublicDashboardConfig(ctx context.Co
return dto.PublicDashboard.Uid, pd.store.UpdatePublicDashboardConfig(ctx, cmd) 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) { func (pd *PublicDashboardServiceImpl) GetQueryDataResponse(ctx context.Context, skipCache bool, queryDto PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error) {
if err := validation.ValidateQueryPublicDashboardRequest(reqDTO); err != nil {
return nil, ErrPublicDashboardBadRequest
}
publicDashboard, dashboard, err := pd.GetPublicDashboard(ctx, accessToken) publicDashboard, dashboard, err := pd.GetPublicDashboard(ctx, accessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
metricReqDTO, err := pd.buildPublicDashboardMetricRequest( metricReq, err := pd.GetMetricRequest(ctx, dashboard, publicDashboard, panelId, queryDto)
ctx,
dashboard,
publicDashboard,
panelId,
reqDTO,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -216,12 +206,31 @@ func (pd *PublicDashboardServiceImpl) GetQueryDataResponse(ctx context.Context,
return nil, err 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 // 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 // group queries by panel
queriesByPanel := queryModels.GroupQueriesByPanelId(dashboard.Data) queriesByPanel := queryModels.GroupQueriesByPanelId(dashboard.Data)
queries, ok := queriesByPanel[panelId] 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 // this is an additional validation, all data sources implements QueryData interface and should have proper validations
// of these limits // of these limits
// for the maxDataPoints we took a hard limit from prometheus which is 11000 // 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 // arbitrary max value for all data sources, it is actually a hard limit defined in prometheus
safeResolution := int64(11000) safeResolution := int64(11000)
@@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/publicdashboards/internal"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/google/uuid" "github.com/google/uuid"
@@ -18,7 +19,7 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
dashboardsDB "github.com/grafana/grafana/pkg/services/dashboards/database" dashboardsDB "github.com/grafana/grafana/pkg/services/dashboards/database"
. "github.com/grafana/grafana/pkg/services/publicdashboards" . "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/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/tsdb/intervalv2" "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) sqlStore := sqlstore.InitTestDB(t)
dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures()) dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures())
publicdashboardStore := database.ProvideStore(sqlStore) publicdashboardStore := database.ProvideStore(sqlStore)
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}) publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 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{ service := &PublicDashboardServiceImpl{
log: log.New("test.logger"), log: log.New("test.logger"),
@@ -366,7 +411,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
intervalCalculator: intervalv2.NewCalculator(), intervalCalculator: intervalv2.NewCalculator(),
} }
publicDashboardQueryDTO := &PublicDashboardQueryDTO{ publicDashboardQueryDTO := PublicDashboardQueryDTO{
IntervalMs: int64(10000000), IntervalMs: int64(10000000),
MaxDataPoints: int64(200), MaxDataPoints: int64(200),
} }
@@ -400,7 +445,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
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.buildPublicDashboardMetricRequest( reqDTO, err := service.buildMetricRequest(
context.Background(), context.Background(),
publicDashboard, publicDashboard,
publicDashboardPD, publicDashboardPD,
@@ -409,8 +454,8 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, timeSettings.Get("from").MustString(), reqDTO.From) require.Equal(t, from, reqDTO.From)
require.Equal(t, timeSettings.Get("to").MustString(), reqDTO.To) require.Equal(t, to, reqDTO.To)
for i := range reqDTO.Queries { for i := range reqDTO.Queries {
require.Equal(t, publicDashboardQueryDTO.IntervalMs, reqDTO.Queries[i].Get("intervalMs").MustInt64()) 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) { t.Run("returns an error when panel missing", func(t *testing.T) {
_, err := service.buildPublicDashboardMetricRequest( _, err := service.buildMetricRequest(
context.Background(), context.Background(),
publicDashboard, publicDashboard,
publicDashboardPD, publicDashboardPD,
@@ -514,6 +559,10 @@ func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardSto
"templating": map[string]interface{}{ "templating": map[string]interface{}{
"list": templateVars, "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) dash, err := dashboardStore.SaveDashboard(cmd)
@@ -526,8 +575,8 @@ func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardSto
func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T) { func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T) {
type args struct { type args struct {
reqDTO *PublicDashboardQueryDTO reqDTO PublicDashboardQueryDTO
ts *TimeSettings ts TimeSettings
} }
tests := []struct { tests := []struct {
name string name string
@@ -538,11 +587,11 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T
{ {
name: "return original interval", name: "return original interval",
args: args{ args: args{
reqDTO: &PublicDashboardQueryDTO{ reqDTO: PublicDashboardQueryDTO{
IntervalMs: 10000, IntervalMs: 10000,
MaxDataPoints: 300, MaxDataPoints: 300,
}, },
ts: &TimeSettings{ ts: TimeSettings{
From: "now-3h", From: "now-3h",
To: "now", To: "now",
}, },
@@ -553,11 +602,11 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T
{ {
name: "return safe interval because of a small interval", name: "return safe interval because of a small interval",
args: args{ args: args{
reqDTO: &PublicDashboardQueryDTO{ reqDTO: PublicDashboardQueryDTO{
IntervalMs: 1000, IntervalMs: 1000,
MaxDataPoints: 300, MaxDataPoints: 300,
}, },
ts: &TimeSettings{ ts: TimeSettings{
From: "now-6h", From: "now-6h",
To: "now", To: "now",
}, },
@@ -568,11 +617,11 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T
{ {
name: "return safe interval for long time range", name: "return safe interval for long time range",
args: args{ args: args{
reqDTO: &PublicDashboardQueryDTO{ reqDTO: PublicDashboardQueryDTO{
IntervalMs: 100, IntervalMs: 100,
MaxDataPoints: 300, MaxDataPoints: 300,
}, },
ts: &TimeSettings{ ts: TimeSettings{
From: "now-90d", From: "now-90d",
To: "now", To: "now",
}, },
@@ -583,8 +632,8 @@ func TestPublicDashboardServiceImpl_getSafeIntervalAndMaxDataPoints(t *testing.T
{ {
name: "return safe interval when reqDTO is empty", name: "return safe interval when reqDTO is empty",
args: args{ args: args{
reqDTO: &PublicDashboardQueryDTO{}, reqDTO: PublicDashboardQueryDTO{},
ts: &TimeSettings{ ts: TimeSettings{
From: "now-90d", From: "now-90d",
To: "now", To: "now",
}, },
@@ -21,7 +21,7 @@ func hasTemplateVariables(dashboard *models.Dashboard) bool {
return len(templateVariables) > 0 return len(templateVariables) > 0
} }
func ValidateQueryPublicDashboardRequest(req *publicDashboardModels.PublicDashboardQueryDTO) error { func ValidateQueryPublicDashboardRequest(req publicDashboardModels.PublicDashboardQueryDTO) error {
if req.IntervalMs < 0 { if req.IntervalMs < 0 {
return fmt.Errorf("intervalMS should be greater than 0") return fmt.Errorf("intervalMS should be greater than 0")
} }