diff --git a/pkg/services/publicdashboards/api/query.go b/pkg/services/publicdashboards/api/query.go index 2c5c5b410c6..0e3b29eb671 100644 --- a/pkg/services/publicdashboards/api/query.go +++ b/pkg/services/publicdashboards/api/query.go @@ -4,10 +4,8 @@ import ( "net/http" "strconv" - "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" - "github.com/grafana/grafana/pkg/services/dashboards" . "github.com/grafana/grafana/pkg/services/publicdashboards/models" "github.com/grafana/grafana/pkg/services/publicdashboards/validation" "github.com/grafana/grafana/pkg/web" @@ -21,34 +19,11 @@ func (api *Api) ViewPublicDashboard(c *contextmodel.ReqContext) response.Respons return response.Err(ErrInvalidAccessToken.Errorf("ViewPublicDashboard: invalid access token")) } - pubdash, dash, err := api.PublicDashboardService.FindEnabledPublicDashboardAndDashboardByAccessToken( - c.Req.Context(), - accessToken, - ) + dto, err := api.PublicDashboardService.GetPublicDashboardForView(c.Req.Context(), accessToken) if err != nil { return response.Err(err) } - meta := dtos.DashboardMeta{ - Slug: dash.Slug, - Type: dashboards.DashTypeDB, - CanStar: false, - CanSave: false, - CanEdit: false, - CanAdmin: false, - CanDelete: false, - Created: dash.Created, - Updated: dash.Updated, - Version: dash.Version, - IsFolder: false, - FolderId: dash.FolderID, - PublicDashboardEnabled: pubdash.IsEnabled, - } - - dash.Data.Get("timepicker").Set("hidden", !pubdash.TimeSelectionEnabled) - - dto := dtos.DashboardFullWithMeta{Meta: meta, Dashboard: dash.Data} - return response.JSON(http.StatusOK, dto) } diff --git a/pkg/services/publicdashboards/api/query_test.go b/pkg/services/publicdashboards/api/query_test.go index 5e9aced61d6..2ac2f5671ab 100644 --- a/pkg/services/publicdashboards/api/query_test.go +++ b/pkg/services/publicdashboards/api/query_test.go @@ -47,7 +47,7 @@ func TestAPIViewPublicDashboard(t *testing.T) { Name string AccessToken string ExpectedHttpResponse int - DashboardResult *dashboards.Dashboard + DashboardResult *dtos.DashboardFullWithMeta Err error FixedErrorResponse string }{ @@ -55,10 +55,20 @@ func TestAPIViewPublicDashboard(t *testing.T) { Name: "It gets a public dashboard", AccessToken: validAccessToken, ExpectedHttpResponse: http.StatusOK, - DashboardResult: &dashboards.Dashboard{ - Data: simplejson.NewFromAny(map[string]interface{}{ + DashboardResult: &dtos.DashboardFullWithMeta{ + Dashboard: simplejson.NewFromAny(map[string]interface{}{ "Uid": DashboardUid, }), + Meta: dtos.DashboardMeta{ + Type: dashboards.DashTypeDB, + CanStar: false, + CanSave: false, + CanEdit: false, + CanAdmin: false, + CanDelete: false, + IsFolder: false, + PublicDashboardEnabled: true, + }, }, Err: nil, FixedErrorResponse: "", @@ -84,8 +94,8 @@ func TestAPIViewPublicDashboard(t *testing.T) { for _, test := range testCases { t.Run(test.Name, func(t *testing.T) { service := publicdashboards.NewFakePublicDashboardService(t) - service.On("FindEnabledPublicDashboardAndDashboardByAccessToken", mock.Anything, mock.AnythingOfType("string")). - Return(&PublicDashboard{Uid: "pubdashuid"}, test.DashboardResult, test.Err).Maybe() + service.On("GetPublicDashboardForView", mock.Anything, mock.AnythingOfType("string")). + Return(test.DashboardResult, test.Err).Maybe() cfg := setting.NewCfg() diff --git a/pkg/services/publicdashboards/public_dashboard_service_mock.go b/pkg/services/publicdashboards/public_dashboard_service_mock.go index 4bc9691d548..4e8e03362dd 100644 --- a/pkg/services/publicdashboards/public_dashboard_service_mock.go +++ b/pkg/services/publicdashboards/public_dashboard_service_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.32.0. DO NOT EDIT. package publicdashboards @@ -28,6 +28,10 @@ func (_m *FakePublicDashboardService) Create(ctx context.Context, u *user.Signed ret := _m.Called(ctx, u, dto) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, *models.SavePublicDashboardDTO) (*models.PublicDashboard, error)); ok { + return rf(ctx, u, dto) + } if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, *models.SavePublicDashboardDTO) *models.PublicDashboard); ok { r0 = rf(ctx, u, dto) } else { @@ -36,7 +40,6 @@ func (_m *FakePublicDashboardService) Create(ctx context.Context, u *user.Signed } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, *models.SavePublicDashboardDTO) error); ok { r1 = rf(ctx, u, dto) } else { @@ -79,13 +82,16 @@ func (_m *FakePublicDashboardService) ExistsEnabledByAccessToken(ctx context.Con ret := _m.Called(ctx, accessToken) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, accessToken) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, accessToken) } else { @@ -100,13 +106,16 @@ func (_m *FakePublicDashboardService) ExistsEnabledByDashboardUid(ctx context.Co ret := _m.Called(ctx, dashboardUid) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return rf(ctx, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, dashboardUid) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, dashboardUid) } else { @@ -121,6 +130,10 @@ func (_m *FakePublicDashboardService) Find(ctx context.Context, uid string) (*mo ret := _m.Called(ctx, uid) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, uid) + } if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok { r0 = rf(ctx, uid) } else { @@ -129,7 +142,6 @@ func (_m *FakePublicDashboardService) Find(ctx context.Context, uid string) (*mo } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, uid) } else { @@ -144,6 +156,10 @@ func (_m *FakePublicDashboardService) FindAllWithPagination(ctx context.Context, ret := _m.Called(ctx, query) var r0 *models.PublicDashboardListResponseWithPagination + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.PublicDashboardListQuery) (*models.PublicDashboardListResponseWithPagination, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *models.PublicDashboardListQuery) *models.PublicDashboardListResponseWithPagination); ok { r0 = rf(ctx, query) } else { @@ -152,7 +168,6 @@ func (_m *FakePublicDashboardService) FindAllWithPagination(ctx context.Context, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.PublicDashboardListQuery) error); ok { r1 = rf(ctx, query) } else { @@ -167,6 +182,10 @@ func (_m *FakePublicDashboardService) FindAnnotations(ctx context.Context, reqDT ret := _m.Called(ctx, reqDTO, accessToken) var r0 []models.AnnotationEvent + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.AnnotationsQueryDTO, string) ([]models.AnnotationEvent, error)); ok { + return rf(ctx, reqDTO, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, models.AnnotationsQueryDTO, string) []models.AnnotationEvent); ok { r0 = rf(ctx, reqDTO, accessToken) } else { @@ -175,7 +194,6 @@ func (_m *FakePublicDashboardService) FindAnnotations(ctx context.Context, reqDT } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.AnnotationsQueryDTO, string) error); ok { r1 = rf(ctx, reqDTO, accessToken) } else { @@ -190,6 +208,10 @@ func (_m *FakePublicDashboardService) FindByAccessToken(ctx context.Context, acc ret := _m.Called(ctx, accessToken) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok { r0 = rf(ctx, accessToken) } else { @@ -198,7 +220,6 @@ func (_m *FakePublicDashboardService) FindByAccessToken(ctx context.Context, acc } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, accessToken) } else { @@ -213,6 +234,10 @@ func (_m *FakePublicDashboardService) FindByDashboardUid(ctx context.Context, or ret := _m.Called(ctx, orgId, dashboardUid) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, orgId, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.PublicDashboard); ok { r0 = rf(ctx, orgId, dashboardUid) } else { @@ -221,7 +246,6 @@ func (_m *FakePublicDashboardService) FindByDashboardUid(ctx context.Context, or } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok { r1 = rf(ctx, orgId, dashboardUid) } else { @@ -236,6 +260,10 @@ func (_m *FakePublicDashboardService) FindDashboard(ctx context.Context, orgId i ret := _m.Called(ctx, orgId, dashboardUid) var r0 *dashboards.Dashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*dashboards.Dashboard, error)); ok { + return rf(ctx, orgId, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, int64, string) *dashboards.Dashboard); ok { r0 = rf(ctx, orgId, dashboardUid) } else { @@ -244,7 +272,6 @@ func (_m *FakePublicDashboardService) FindDashboard(ctx context.Context, orgId i } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok { r1 = rf(ctx, orgId, dashboardUid) } else { @@ -259,6 +286,11 @@ func (_m *FakePublicDashboardService) FindEnabledPublicDashboardAndDashboardByAc ret := _m.Called(ctx, accessToken) var r0 *models.PublicDashboard + var r1 *dashboards.Dashboard + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.PublicDashboard, *dashboards.Dashboard, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok { r0 = rf(ctx, accessToken) } else { @@ -267,7 +299,6 @@ func (_m *FakePublicDashboardService) FindEnabledPublicDashboardAndDashboardByAc } } - var r1 *dashboards.Dashboard if rf, ok := ret.Get(1).(func(context.Context, string) *dashboards.Dashboard); ok { r1 = rf(ctx, accessToken) } else { @@ -276,7 +307,6 @@ func (_m *FakePublicDashboardService) FindEnabledPublicDashboardAndDashboardByAc } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { r2 = rf(ctx, accessToken) } else { @@ -291,6 +321,11 @@ func (_m *FakePublicDashboardService) FindPublicDashboardAndDashboardByAccessTok ret := _m.Called(ctx, accessToken) var r0 *models.PublicDashboard + var r1 *dashboards.Dashboard + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.PublicDashboard, *dashboards.Dashboard, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok { r0 = rf(ctx, accessToken) } else { @@ -299,7 +334,6 @@ func (_m *FakePublicDashboardService) FindPublicDashboardAndDashboardByAccessTok } } - var r1 *dashboards.Dashboard if rf, ok := ret.Get(1).(func(context.Context, string) *dashboards.Dashboard); ok { r1 = rf(ctx, accessToken) } else { @@ -308,7 +342,6 @@ func (_m *FakePublicDashboardService) FindPublicDashboardAndDashboardByAccessTok } } - var r2 error if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { r2 = rf(ctx, accessToken) } else { @@ -323,13 +356,16 @@ func (_m *FakePublicDashboardService) GetMetricRequest(ctx context.Context, dash ret := _m.Called(ctx, dashboard, publicDashboard, panelId, reqDTO) var r0 dtos.MetricRequest + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *dashboards.Dashboard, *models.PublicDashboard, int64, models.PublicDashboardQueryDTO) (dtos.MetricRequest, error)); ok { + return rf(ctx, dashboard, publicDashboard, panelId, reqDTO) + } if rf, ok := ret.Get(0).(func(context.Context, *dashboards.Dashboard, *models.PublicDashboard, int64, models.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, *dashboards.Dashboard, *models.PublicDashboard, int64, models.PublicDashboardQueryDTO) error); ok { r1 = rf(ctx, dashboard, publicDashboard, panelId, reqDTO) } else { @@ -344,13 +380,42 @@ func (_m *FakePublicDashboardService) GetOrgIdByAccessToken(ctx context.Context, ret := _m.Called(ctx, accessToken) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (int64, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) int64); ok { r0 = rf(ctx, accessToken) } else { r0 = ret.Get(0).(int64) } + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, accessToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetPublicDashboardForView provides a mock function with given fields: ctx, accessToken +func (_m *FakePublicDashboardService) GetPublicDashboardForView(ctx context.Context, accessToken string) (*dtos.DashboardFullWithMeta, error) { + ret := _m.Called(ctx, accessToken) + + var r0 *dtos.DashboardFullWithMeta var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*dtos.DashboardFullWithMeta, error)); ok { + return rf(ctx, accessToken) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *dtos.DashboardFullWithMeta); ok { + r0 = rf(ctx, accessToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*dtos.DashboardFullWithMeta) + } + } + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, accessToken) } else { @@ -365,6 +430,10 @@ func (_m *FakePublicDashboardService) GetQueryDataResponse(ctx context.Context, ret := _m.Called(ctx, skipDSCache, reqDTO, panelId, accessToken) var r0 *backend.QueryDataResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, bool, models.PublicDashboardQueryDTO, int64, string) (*backend.QueryDataResponse, error)); ok { + return rf(ctx, skipDSCache, reqDTO, panelId, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, bool, models.PublicDashboardQueryDTO, int64, string) *backend.QueryDataResponse); ok { r0 = rf(ctx, skipDSCache, reqDTO, panelId, accessToken) } else { @@ -373,7 +442,6 @@ func (_m *FakePublicDashboardService) GetQueryDataResponse(ctx context.Context, } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, bool, models.PublicDashboardQueryDTO, int64, string) error); ok { r1 = rf(ctx, skipDSCache, reqDTO, panelId, accessToken) } else { @@ -388,13 +456,16 @@ func (_m *FakePublicDashboardService) NewPublicDashboardAccessToken(ctx context. ret := _m.Called(ctx) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) string); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -409,13 +480,16 @@ func (_m *FakePublicDashboardService) NewPublicDashboardUid(ctx context.Context) ret := _m.Called(ctx) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) string); ok { r0 = rf(ctx) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -430,6 +504,10 @@ func (_m *FakePublicDashboardService) Update(ctx context.Context, u *user.Signed ret := _m.Called(ctx, u, dto) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, *models.SavePublicDashboardDTO) (*models.PublicDashboard, error)); ok { + return rf(ctx, u, dto) + } if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, *models.SavePublicDashboardDTO) *models.PublicDashboard); ok { r0 = rf(ctx, u, dto) } else { @@ -438,7 +516,6 @@ func (_m *FakePublicDashboardService) Update(ctx context.Context, u *user.Signed } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, *models.SavePublicDashboardDTO) error); ok { r1 = rf(ctx, u, dto) } else { @@ -448,13 +525,12 @@ func (_m *FakePublicDashboardService) Update(ctx context.Context, u *user.Signed return r0, r1 } -type mockConstructorTestingTNewFakePublicDashboardService interface { +// NewFakePublicDashboardService creates a new instance of FakePublicDashboardService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFakePublicDashboardService(t interface { mock.TestingT Cleanup(func()) -} - -// NewFakePublicDashboardService creates a new instance of FakePublicDashboardService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFakePublicDashboardService(t mockConstructorTestingTNewFakePublicDashboardService) *FakePublicDashboardService { +}) *FakePublicDashboardService { mock := &FakePublicDashboardService{} mock.Mock.Test(t) diff --git a/pkg/services/publicdashboards/public_dashboard_service_wrapper_mock.go b/pkg/services/publicdashboards/public_dashboard_service_wrapper_mock.go index 69ee15d2b9a..f257f713c81 100644 --- a/pkg/services/publicdashboards/public_dashboard_service_wrapper_mock.go +++ b/pkg/services/publicdashboards/public_dashboard_service_wrapper_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.32.0. DO NOT EDIT. package publicdashboards @@ -33,6 +33,10 @@ func (_m *FakePublicDashboardServiceWrapper) FindByDashboardUid(ctx context.Cont ret := _m.Called(ctx, orgId, dashboardUid) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, orgId, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.PublicDashboard); ok { r0 = rf(ctx, orgId, dashboardUid) } else { @@ -41,7 +45,6 @@ func (_m *FakePublicDashboardServiceWrapper) FindByDashboardUid(ctx context.Cont } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok { r1 = rf(ctx, orgId, dashboardUid) } else { @@ -51,13 +54,12 @@ func (_m *FakePublicDashboardServiceWrapper) FindByDashboardUid(ctx context.Cont return r0, r1 } -type mockConstructorTestingTNewFakePublicDashboardServiceWrapper interface { +// NewFakePublicDashboardServiceWrapper creates a new instance of FakePublicDashboardServiceWrapper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFakePublicDashboardServiceWrapper(t interface { mock.TestingT Cleanup(func()) -} - -// NewFakePublicDashboardServiceWrapper creates a new instance of FakePublicDashboardServiceWrapper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFakePublicDashboardServiceWrapper(t mockConstructorTestingTNewFakePublicDashboardServiceWrapper) *FakePublicDashboardServiceWrapper { +}) *FakePublicDashboardServiceWrapper { mock := &FakePublicDashboardServiceWrapper{} mock.Mock.Test(t) diff --git a/pkg/services/publicdashboards/public_dashboard_store_mock.go b/pkg/services/publicdashboards/public_dashboard_store_mock.go index 4f0fdc4cd9f..677ea5c5798 100644 --- a/pkg/services/publicdashboards/public_dashboard_store_mock.go +++ b/pkg/services/publicdashboards/public_dashboard_store_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.32.0. DO NOT EDIT. package publicdashboards @@ -21,13 +21,16 @@ func (_m *FakePublicDashboardStore) Create(ctx context.Context, cmd models.SaveP ret := _m.Called(ctx, cmd) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.SavePublicDashboardCommand) (int64, error)); ok { + return rf(ctx, cmd) + } if rf, ok := ret.Get(0).(func(context.Context, models.SavePublicDashboardCommand) int64); ok { r0 = rf(ctx, cmd) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.SavePublicDashboardCommand) error); ok { r1 = rf(ctx, cmd) } else { @@ -42,13 +45,16 @@ func (_m *FakePublicDashboardStore) Delete(ctx context.Context, uid string) (int ret := _m.Called(ctx, uid) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (int64, error)); ok { + return rf(ctx, uid) + } if rf, ok := ret.Get(0).(func(context.Context, string) int64); ok { r0 = rf(ctx, uid) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, uid) } else { @@ -63,13 +69,16 @@ func (_m *FakePublicDashboardStore) ExistsEnabledByAccessToken(ctx context.Conte ret := _m.Called(ctx, accessToken) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, accessToken) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, accessToken) } else { @@ -84,13 +93,16 @@ func (_m *FakePublicDashboardStore) ExistsEnabledByDashboardUid(ctx context.Cont ret := _m.Called(ctx, dashboardUid) var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return rf(ctx, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, dashboardUid) } else { r0 = ret.Get(0).(bool) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, dashboardUid) } else { @@ -105,6 +117,10 @@ func (_m *FakePublicDashboardStore) Find(ctx context.Context, uid string) (*mode ret := _m.Called(ctx, uid) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, uid) + } if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok { r0 = rf(ctx, uid) } else { @@ -113,7 +129,6 @@ func (_m *FakePublicDashboardStore) Find(ctx context.Context, uid string) (*mode } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, uid) } else { @@ -128,6 +143,10 @@ func (_m *FakePublicDashboardStore) FindAllWithPagination(ctx context.Context, q ret := _m.Called(ctx, query) var r0 *models.PublicDashboardListResponseWithPagination + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *models.PublicDashboardListQuery) (*models.PublicDashboardListResponseWithPagination, error)); ok { + return rf(ctx, query) + } if rf, ok := ret.Get(0).(func(context.Context, *models.PublicDashboardListQuery) *models.PublicDashboardListResponseWithPagination); ok { r0 = rf(ctx, query) } else { @@ -136,7 +155,6 @@ func (_m *FakePublicDashboardStore) FindAllWithPagination(ctx context.Context, q } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *models.PublicDashboardListQuery) error); ok { r1 = rf(ctx, query) } else { @@ -151,6 +169,10 @@ func (_m *FakePublicDashboardStore) FindByAccessToken(ctx context.Context, acces ret := _m.Called(ctx, accessToken) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok { r0 = rf(ctx, accessToken) } else { @@ -159,7 +181,6 @@ func (_m *FakePublicDashboardStore) FindByAccessToken(ctx context.Context, acces } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, accessToken) } else { @@ -174,6 +195,10 @@ func (_m *FakePublicDashboardStore) FindByDashboardFolder(ctx context.Context, d ret := _m.Called(ctx, dashboard) var r0 []*models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *dashboards.Dashboard) ([]*models.PublicDashboard, error)); ok { + return rf(ctx, dashboard) + } if rf, ok := ret.Get(0).(func(context.Context, *dashboards.Dashboard) []*models.PublicDashboard); ok { r0 = rf(ctx, dashboard) } else { @@ -182,7 +207,6 @@ func (_m *FakePublicDashboardStore) FindByDashboardFolder(ctx context.Context, d } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, *dashboards.Dashboard) error); ok { r1 = rf(ctx, dashboard) } else { @@ -197,6 +221,10 @@ func (_m *FakePublicDashboardStore) FindByDashboardUid(ctx context.Context, orgI ret := _m.Called(ctx, orgId, dashboardUid) var r0 *models.PublicDashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*models.PublicDashboard, error)); ok { + return rf(ctx, orgId, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.PublicDashboard); ok { r0 = rf(ctx, orgId, dashboardUid) } else { @@ -205,7 +233,6 @@ func (_m *FakePublicDashboardStore) FindByDashboardUid(ctx context.Context, orgI } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok { r1 = rf(ctx, orgId, dashboardUid) } else { @@ -220,6 +247,10 @@ func (_m *FakePublicDashboardStore) FindDashboard(ctx context.Context, orgId int ret := _m.Called(ctx, orgId, dashboardUid) var r0 *dashboards.Dashboard + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*dashboards.Dashboard, error)); ok { + return rf(ctx, orgId, dashboardUid) + } if rf, ok := ret.Get(0).(func(context.Context, int64, string) *dashboards.Dashboard); ok { r0 = rf(ctx, orgId, dashboardUid) } else { @@ -228,7 +259,6 @@ func (_m *FakePublicDashboardStore) FindDashboard(ctx context.Context, orgId int } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok { r1 = rf(ctx, orgId, dashboardUid) } else { @@ -243,6 +273,10 @@ func (_m *FakePublicDashboardStore) GetMetrics(ctx context.Context) (*models.Met ret := _m.Called(ctx) var r0 *models.Metrics + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*models.Metrics, error)); ok { + return rf(ctx) + } if rf, ok := ret.Get(0).(func(context.Context) *models.Metrics); ok { r0 = rf(ctx) } else { @@ -251,7 +285,6 @@ func (_m *FakePublicDashboardStore) GetMetrics(ctx context.Context) (*models.Met } } - var r1 error if rf, ok := ret.Get(1).(func(context.Context) error); ok { r1 = rf(ctx) } else { @@ -266,13 +299,16 @@ func (_m *FakePublicDashboardStore) GetOrgIdByAccessToken(ctx context.Context, a ret := _m.Called(ctx, accessToken) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (int64, error)); ok { + return rf(ctx, accessToken) + } if rf, ok := ret.Get(0).(func(context.Context, string) int64); ok { r0 = rf(ctx, accessToken) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { r1 = rf(ctx, accessToken) } else { @@ -287,13 +323,16 @@ func (_m *FakePublicDashboardStore) Update(ctx context.Context, cmd models.SaveP ret := _m.Called(ctx, cmd) var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.SavePublicDashboardCommand) (int64, error)); ok { + return rf(ctx, cmd) + } if rf, ok := ret.Get(0).(func(context.Context, models.SavePublicDashboardCommand) int64); ok { r0 = rf(ctx, cmd) } else { r0 = ret.Get(0).(int64) } - var r1 error if rf, ok := ret.Get(1).(func(context.Context, models.SavePublicDashboardCommand) error); ok { r1 = rf(ctx, cmd) } else { @@ -303,13 +342,12 @@ func (_m *FakePublicDashboardStore) Update(ctx context.Context, cmd models.SaveP return r0, r1 } -type mockConstructorTestingTNewFakePublicDashboardStore interface { +// NewFakePublicDashboardStore creates a new instance of FakePublicDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFakePublicDashboardStore(t interface { mock.TestingT Cleanup(func()) -} - -// NewFakePublicDashboardStore creates a new instance of FakePublicDashboardStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFakePublicDashboardStore(t mockConstructorTestingTNewFakePublicDashboardStore) *FakePublicDashboardStore { +}) *FakePublicDashboardStore { mock := &FakePublicDashboardStore{} mock.Mock.Test(t) diff --git a/pkg/services/publicdashboards/publicdashboard.go b/pkg/services/publicdashboards/publicdashboard.go index fc1e98ed2ad..df070738b07 100644 --- a/pkg/services/publicdashboards/publicdashboard.go +++ b/pkg/services/publicdashboards/publicdashboard.go @@ -15,6 +15,7 @@ import ( //go:generate go run ./commands/generate_datasources/main.go //go:generate mockery --name Service --structname FakePublicDashboardService --inpackage --filename public_dashboard_service_mock.go type Service interface { + GetPublicDashboardForView(ctx context.Context, accessToken string) (*dtos.DashboardFullWithMeta, error) FindPublicDashboardAndDashboardByAccessToken(ctx context.Context, accessToken string) (*PublicDashboard, *dashboards.Dashboard, error) FindEnabledPublicDashboardAndDashboardByAccessToken(ctx context.Context, accessToken string) (*PublicDashboard, *dashboards.Dashboard, error) FindByAccessToken(ctx context.Context, accessToken string) (*PublicDashboard, error) diff --git a/pkg/services/publicdashboards/service/query.go b/pkg/services/publicdashboards/service/query.go index b3805a72720..db023a37051 100644 --- a/pkg/services/publicdashboards/service/query.go +++ b/pkg/services/publicdashboards/service/query.go @@ -342,6 +342,27 @@ func sanitizeMetadataFromQueryData(res *backend.QueryDataResponse) { } } +// sanitizeData removes the query expressions from the dashboard data +func sanitizeData(data *simplejson.Json) { + for _, panelObj := range data.Get("panels").MustArray() { + panel := simplejson.NewFromAny(panelObj) + + // if the panel is a row and it is collapsed, get the queries from the panels inside the row + if panel.Get("type").MustString() == "row" && panel.Get("collapsed").MustBool() { + // recursive call to get queries from panels inside a row + sanitizeData(panel) + continue + } + + for _, targetObj := range panel.Get("targets").MustArray() { + target := simplejson.NewFromAny(targetObj) + target.Del("expr") + target.Del("query") + target.Del("rawSql") + } + } +} + // NewDataTimeRange declared to be able to stub this function in tests var NewDataTimeRange = legacydata.NewDataTimeRange diff --git a/pkg/services/publicdashboards/service/service.go b/pkg/services/publicdashboards/service/service.go index 77926942f88..c6727257afe 100644 --- a/pkg/services/publicdashboards/service/service.go +++ b/pkg/services/publicdashboards/service/service.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/annotations" @@ -64,6 +65,34 @@ func ProvideService( } } +func (pd *PublicDashboardServiceImpl) GetPublicDashboardForView(ctx context.Context, accessToken string) (*dtos.DashboardFullWithMeta, error) { + pubdash, dash, err := pd.FindEnabledPublicDashboardAndDashboardByAccessToken(ctx, accessToken) + if err != nil { + return nil, err + } + + meta := dtos.DashboardMeta{ + Slug: dash.Slug, + Type: dashboards.DashTypeDB, + CanStar: false, + CanSave: false, + CanEdit: false, + CanAdmin: false, + CanDelete: false, + Created: dash.Created, + Updated: dash.Updated, + Version: dash.Version, + IsFolder: false, + FolderId: dash.FolderID, + PublicDashboardEnabled: pubdash.IsEnabled, + } + dash.Data.Get("timepicker").Set("hidden", !pubdash.TimeSelectionEnabled) + + sanitizeData(dash.Data) + + return &dtos.DashboardFullWithMeta{Meta: meta, Dashboard: dash.Data}, nil +} + // FindByDashboardUid this method would be replaced by another implementation for Enterprise version func (pd *PublicDashboardServiceImpl) FindByDashboardUid(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) { return pd.serviceWrapper.FindByDashboardUid(ctx, orgId, dashboardUid) diff --git a/pkg/services/publicdashboards/service/service_test.go b/pkg/services/publicdashboards/service/service_test.go index a0a0e96ead5..94b10a81a33 100644 --- a/pkg/services/publicdashboards/service/service_test.go +++ b/pkg/services/publicdashboards/service/service_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" @@ -42,6 +43,401 @@ func TestLogPrefix(t *testing.T) { assert.Equal(t, LogPrefix, "publicdashboards.service") } +func TestGetPublicDashboardForView(t *testing.T) { + type storeResp struct { + pd *PublicDashboard + d *dashboards.Dashboard + err error + } + + const dashboardWithRowsAndHiddenQueries = ` +{ + "panels": [ + { + "id": 2, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "_yxMP8Ynk" + }, + "exemplar": true, + "expr": "go_goroutines{job=\"$job\"}", + "interval": "", + "legendFormat": "", + "refId": "A", + "hide": true + }, + { + "datasource": { + "type": "prometheus", + "uid": "promds2" + }, + "exemplar": true, + "expr": "query2", + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "id": 3, + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "title": "This panel is a Row", + "type": "row", + "panels": [ + { + "id": 4, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "_yxMP8Ynk" + }, + "exemplar": true, + "expr": "go_goroutines{job=\"$job\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "promds2" + }, + "exemplar": true, + "expr": "query2", + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Panel inside a row", + "type": "timeseries" + } + ] + }, + { + "aliasColors": { + "total avg": "#6ed0e0" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "mssql", + "uid": "P6B08AC199690F328" + }, + "fieldConfig": { + "defaults": { + "links": [] + }, + "overrides": [] + }, + "fill": 2, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "10.2.0-pre", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "total avg", + "fill": 0, + "pointradius": 3, + "points": true + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "", + "datasource": { + "type": "mssql", + "uid": "P6B08AC199690F328" + }, + "format": "time_series", + "rawSql": "SELECT\n $__timeGroup(createdAt,'$summarize') as time,\n avg(value) as value,\n hostname as metric\nFROM \n grafana_metric\nWHERE\n $__timeFilter(createdAt) AND\n measurement = 'logins.count' AND\n hostname IN($host)\nGROUP BY $__timeGroup(createdAt,'$summarize'), hostname\nORDER BY 1", + "refId": "A" + }, + { + "alias": "", + "datasource": { + "type": "mssql", + "uid": "P6B08AC199690F328" + }, + "format": "time_series", + "rawSql": "SELECT\n $__timeGroup(createdAt,'$summarize') as time,\n min(value) as value,\n 'total avg' as metric\nFROM \n grafana_metric\nWHERE\n $__timeFilter(createdAt) AND\n measurement = 'logins.count'\nGROUP BY $__timeGroup(createdAt,'$summarize')\nORDER BY 1", + "refId": "B" + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Average logins / $summarize", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "show": true + }, + { + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "datasource": { + "type": "influxdb", + "uid": "P49A45DF074423DFB" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^retentionPeriod 4a2f27036bf63a3c$/", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.2.0-pre", + "targets": [ + { + "datasource": { + "type": "influxdb", + "uid": "P49A45DF074423DFB" + }, + "query": "buckets()", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "stat" + } + ], + "schemaVersion": 35, + "timepicker": { + "hidden": false + } +}` + data, _ := simplejson.NewJson([]byte(dashboardWithRowsAndHiddenQueries)) + + now := time.Now() + + // #nosec G101 -- This is dummy/test token + accessToken := "c54b1c4dd2b143a1a7a43005264d256d" + d := &dashboards.Dashboard{UID: "mydashboard", Data: data, Slug: "dashboardSlug", Created: now, Updated: now, Version: 1, FolderID: 1} + + testCases := []struct { + Name string + AccessToken string + StoreResp *storeResp + ErrResp error + DashResp *dtos.DashboardFullWithMeta + }{ + { + Name: "returns a dashboard with the time picker shown", + AccessToken: accessToken, + StoreResp: &storeResp{ + pd: &PublicDashboard{AccessToken: accessToken, IsEnabled: true, TimeSelectionEnabled: true}, + d: d, + err: nil, + }, + ErrResp: nil, + DashResp: &dtos.DashboardFullWithMeta{ + Dashboard: data, + Meta: dtos.DashboardMeta{ + Slug: d.Slug, + Type: dashboards.DashTypeDB, + CanStar: false, + CanSave: false, + CanEdit: false, + CanAdmin: false, + CanDelete: false, + Created: d.Created, + Updated: d.Updated, + Version: d.Version, + IsFolder: false, + FolderId: d.FolderID, + PublicDashboardEnabled: true, + }, + }, + }, + { + Name: "returns a dashboard with the time picker hidden", + AccessToken: accessToken, + StoreResp: &storeResp{ + pd: &PublicDashboard{AccessToken: accessToken, IsEnabled: true, TimeSelectionEnabled: false}, + d: d, + err: nil, + }, + ErrResp: nil, + DashResp: &dtos.DashboardFullWithMeta{ + Dashboard: data, + Meta: dtos.DashboardMeta{ + Slug: d.Slug, + Type: dashboards.DashTypeDB, + CanStar: false, + CanSave: false, + CanEdit: false, + CanAdmin: false, + CanDelete: false, + Created: d.Created, + Updated: d.Updated, + Version: d.Version, + IsFolder: false, + FolderId: d.FolderID, + PublicDashboardEnabled: true, + }, + }, + }, + } + + for _, test := range testCases { + t.Run(test.Name, func(t *testing.T) { + fakeStore := FakePublicDashboardStore{} + service := &PublicDashboardServiceImpl{ + log: log.New("test.logger"), + store: &fakeStore, + } + + fakeStore.On("FindByAccessToken", mock.Anything, mock.Anything).Return(test.StoreResp.pd, test.StoreResp.err) + fakeStore.On("FindDashboard", mock.Anything, mock.Anything, mock.Anything).Return(test.StoreResp.d, test.StoreResp.err) + + dashboardFullWithMeta, err := service.GetPublicDashboardForView(context.Background(), test.AccessToken) + if test.ErrResp != nil { + assert.Error(t, test.ErrResp, err) + } else { + require.NoError(t, err) + } + + //assert.Equal(t, test.DashResp, dash) + + if test.DashResp != nil { + assert.Equal(t, test.DashResp.Meta.Slug, dashboardFullWithMeta.Meta.Slug) + assert.Equal(t, test.DashResp.Meta.Type, dashboardFullWithMeta.Meta.Type) + assert.Equal(t, false, dashboardFullWithMeta.Meta.CanStar) + assert.Equal(t, false, dashboardFullWithMeta.Meta.CanSave) + assert.Equal(t, false, dashboardFullWithMeta.Meta.CanEdit) + assert.Equal(t, false, dashboardFullWithMeta.Meta.CanAdmin) + assert.Equal(t, false, dashboardFullWithMeta.Meta.CanDelete) + assert.Equal(t, test.DashResp.Meta.Created, dashboardFullWithMeta.Meta.Created) + assert.Equal(t, test.DashResp.Meta.Updated, dashboardFullWithMeta.Meta.Updated) + assert.Equal(t, test.DashResp.Meta.Version, dashboardFullWithMeta.Meta.Version) + assert.Equal(t, false, dashboardFullWithMeta.Meta.IsFolder) + assert.Equal(t, test.DashResp.Meta.FolderId, dashboardFullWithMeta.Meta.FolderId) + assert.Equal(t, test.DashResp.Meta.PublicDashboardEnabled, dashboardFullWithMeta.Meta.PublicDashboardEnabled) + + // hide the timepicker if the time selection is disabled + assert.Equal(t, test.StoreResp.pd.TimeSelectionEnabled, !dashboardFullWithMeta.Dashboard.Get("timepicker").Get("hidden").MustBool()) + + for _, panelObj := range dashboardFullWithMeta.Dashboard.Get("panels").MustArray() { + panel := simplejson.NewFromAny(panelObj) + + // if the panel is a row and it is collapsed, get the queries from the panels inside the row + if panel.Get("type").MustString() == "row" && panel.Get("collapsed").MustBool() { + // recursive call to get queries from panels inside a row + sanitizeData(panel) + continue + } + + for _, targetObj := range panel.Get("targets").MustArray() { + target := simplejson.NewFromAny(targetObj) + assert.Empty(t, target.Get("expr").MustString()) + assert.Empty(t, target.Get("query").MustString()) + assert.Empty(t, target.Get("rawSql").MustString()) + } + } + } + }) + } +} + func TestGetPublicDashboard(t *testing.T) { type storeResp struct { pd *PublicDashboard