chore: replace artisanal FakeDashboardService with generated mock (#49276)

* chore: replace handmade FakeDashboardService with generated mock

Maintaining a handcrafted FakeDashboardService is not sustainable now that we are in the process of moving the dashboard-related functions out of sqlstore.

* remove dialect global variable
This commit is contained in:
Kristin Laemmert
2022-05-23 11:14:27 -04:00
committed by GitHub
parent ccd160a75e
commit 8c753999df
14 changed files with 640 additions and 275 deletions

View File

@@ -6,17 +6,12 @@ import (
"github.com/grafana/grafana/pkg/models"
)
// Generating mocks is handled by vektra/mockery
// 1. install go mockery https://github.com/vektra/mockery#go-install
// 2. add your method to the relevant services
// 3. from the same directory as this file run `go generate` and it will update the mock
// If you don't see any output, this most likely means your OS can't find the mockery binary
// `which mockery` to confirm and follow one of the installation methods
//go:generate mockery --name DashboardService --structname FakeDashboardService --inpackage --filename dashboard_service_mock.go
// DashboardService is a service for operating on dashboards.
type DashboardService interface {
BuildSaveDashboardCommand(ctx context.Context, dto *SaveDashboardDTO, shouldValidateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error)
DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error
FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error
GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error
GetDashboardUIDById(ctx context.Context, query *models.GetDashboardRefByIdQuery) error
@@ -25,6 +20,7 @@ type DashboardService interface {
MakeUserAdmin(ctx context.Context, orgID int64, userID, dashboardID int64, setViewAndEditPermissions bool) error
SaveDashboard(ctx context.Context, dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error)
SavePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*models.PublicDashboardConfig, error)
SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error
UpdateDashboardACL(ctx context.Context, uid int64, items []*models.DashboardAcl) error
}
@@ -51,6 +47,7 @@ type DashboardProvisioningService interface {
type Store interface {
DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error
DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *models.DeleteOrphanedProvisionedDashboardsCommand) error
FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error
GetDashboardUIDById(ctx context.Context, query *models.GetDashboardRefByIdQuery) error
GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error
@@ -65,6 +62,7 @@ type Store interface {
SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error)
SaveProvisionedDashboard(cmd models.SaveDashboardCommand, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
SavePublicDashboardConfig(cmd models.SavePublicDashboardConfigCommand) (*models.PublicDashboardConfig, error)
SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error
UnprovisionDashboard(ctx context.Context, id int64) error
UpdateDashboardACL(ctx context.Context, uid int64, items []*models.DashboardAcl) error
// ValidateDashboardBeforeSave validates a dashboard before save.

View File

@@ -1,79 +1,263 @@
// Code generated by mockery v2.12.2. DO NOT EDIT.
package dashboards
import (
"context"
context "context"
"github.com/grafana/grafana/pkg/models"
models "github.com/grafana/grafana/pkg/models"
mock "github.com/stretchr/testify/mock"
testing "testing"
)
// FakeDashboardService is an autogenerated mock type for the DashboardService type
type FakeDashboardService struct {
DashboardService
SaveDashboardResult *models.Dashboard
SavedDashboards []*SaveDashboardDTO
ProvisionedDashData *models.DashboardProvisioning
PublicDashboardConfigResult *models.PublicDashboardConfig
PublicDashboardConfigError error
SaveDashboardError error
GetDashboardFn func(ctx context.Context, cmd *models.GetDashboardQuery) error
mock.Mock
}
func (s *FakeDashboardService) SaveDashboard(ctx context.Context, dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
s.SavedDashboards = append(s.SavedDashboards, dto)
// BuildSaveDashboardCommand provides a mock function with given fields: ctx, dto, shouldValidateAlerts, validateProvisionedDashboard
func (_m *FakeDashboardService) BuildSaveDashboardCommand(ctx context.Context, dto *SaveDashboardDTO, shouldValidateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
ret := _m.Called(ctx, dto, shouldValidateAlerts, validateProvisionedDashboard)
if s.SaveDashboardResult == nil && s.SaveDashboardError == nil {
s.SaveDashboardResult = dto.Dashboard
}
return s.SaveDashboardResult, s.SaveDashboardError
}
func (s *FakeDashboardService) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*models.PublicDashboardConfig, error) {
return s.PublicDashboardConfigResult, s.PublicDashboardConfigError
}
func (s *FakeDashboardService) SavePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*models.PublicDashboardConfig, error) {
return s.PublicDashboardConfigResult, s.PublicDashboardConfigError
}
func (s *FakeDashboardService) ImportDashboard(ctx context.Context, dto *SaveDashboardDTO) (*models.Dashboard, error) {
return s.SaveDashboard(ctx, dto, true)
}
func (s *FakeDashboardService) DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error {
for index, dash := range s.SavedDashboards {
if dash.Dashboard.Id == dashboardId && dash.OrgId == orgId {
s.SavedDashboards = append(s.SavedDashboards[:index], s.SavedDashboards[index+1:]...)
break
var r0 *models.SaveDashboardCommand
if rf, ok := ret.Get(0).(func(context.Context, *SaveDashboardDTO, bool, bool) *models.SaveDashboardCommand); ok {
r0 = rf(ctx, dto, shouldValidateAlerts, validateProvisionedDashboard)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.SaveDashboardCommand)
}
}
return nil
}
func (s *FakeDashboardService) GetProvisionedDashboardDataByDashboardID(id int64) (*models.DashboardProvisioning, error) {
return s.ProvisionedDashData, nil
}
func (s *FakeDashboardService) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *models.DeleteOrphanedProvisionedDashboardsCommand) error {
return nil
}
func (s *FakeDashboardService) GetDashboard(ctx context.Context, cmd *models.GetDashboardQuery) error {
if s.GetDashboardFn != nil {
return s.GetDashboardFn(ctx, cmd)
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *SaveDashboardDTO, bool, bool) error); ok {
r1 = rf(ctx, dto, shouldValidateAlerts, validateProvisionedDashboard)
} else {
r1 = ret.Error(1)
}
// A minimal result for tests that need a valid result, but don't care what's in it.
d := models.NewDashboard("mocked")
d.Id = 1
d.Uid = "1"
cmd.Result = d
return nil
return r0, r1
}
func (s *FakeDashboardService) GetDashboardUIDById(ctx context.Context, query *models.GetDashboardRefByIdQuery) error {
return nil
// DeleteDashboard provides a mock function with given fields: ctx, dashboardId, orgId
func (_m *FakeDashboardService) DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error {
ret := _m.Called(ctx, dashboardId, orgId)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok {
r0 = rf(ctx, dashboardId, orgId)
} else {
r0 = ret.Error(0)
}
return r0
}
func (s *FakeDashboardService) GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error {
return nil
// FindDashboards provides a mock function with given fields: ctx, query
func (_m *FakeDashboardService) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
ret := _m.Called(ctx, query)
var r0 []DashboardSearchProjection
if rf, ok := ret.Get(0).(func(context.Context, *models.FindPersistedDashboardsQuery) []DashboardSearchProjection); ok {
r0 = rf(ctx, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]DashboardSearchProjection)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.FindPersistedDashboardsQuery) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetDashboard provides a mock function with given fields: ctx, query
func (_m *FakeDashboardService) GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error {
ret := _m.Called(ctx, query)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.GetDashboardQuery) error); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetDashboardUIDById provides a mock function with given fields: ctx, query
func (_m *FakeDashboardService) GetDashboardUIDById(ctx context.Context, query *models.GetDashboardRefByIdQuery) error {
ret := _m.Called(ctx, query)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.GetDashboardRefByIdQuery) error); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetDashboards provides a mock function with given fields: ctx, query
func (_m *FakeDashboardService) GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error {
ret := _m.Called(ctx, query)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.GetDashboardsQuery) error); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetPublicDashboardConfig provides a mock function with given fields: ctx, orgId, dashboardUid
func (_m *FakeDashboardService) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*models.PublicDashboardConfig, error) {
ret := _m.Called(ctx, orgId, dashboardUid)
var r0 *models.PublicDashboardConfig
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.PublicDashboardConfig); ok {
r0 = rf(ctx, orgId, dashboardUid)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.PublicDashboardConfig)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok {
r1 = rf(ctx, orgId, dashboardUid)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ImportDashboard provides a mock function with given fields: ctx, dto
func (_m *FakeDashboardService) ImportDashboard(ctx context.Context, dto *SaveDashboardDTO) (*models.Dashboard, error) {
ret := _m.Called(ctx, dto)
var r0 *models.Dashboard
if rf, ok := ret.Get(0).(func(context.Context, *SaveDashboardDTO) *models.Dashboard); ok {
r0 = rf(ctx, dto)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Dashboard)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *SaveDashboardDTO) error); ok {
r1 = rf(ctx, dto)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MakeUserAdmin provides a mock function with given fields: ctx, orgID, userID, dashboardID, setViewAndEditPermissions
func (_m *FakeDashboardService) MakeUserAdmin(ctx context.Context, orgID int64, userID int64, dashboardID int64, setViewAndEditPermissions bool) error {
ret := _m.Called(ctx, orgID, userID, dashboardID, setViewAndEditPermissions)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, int64, int64, bool) error); ok {
r0 = rf(ctx, orgID, userID, dashboardID, setViewAndEditPermissions)
} else {
r0 = ret.Error(0)
}
return r0
}
// SaveDashboard provides a mock function with given fields: ctx, dto, allowUiUpdate
func (_m *FakeDashboardService) SaveDashboard(ctx context.Context, dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
ret := _m.Called(ctx, dto, allowUiUpdate)
var r0 *models.Dashboard
if rf, ok := ret.Get(0).(func(context.Context, *SaveDashboardDTO, bool) *models.Dashboard); ok {
r0 = rf(ctx, dto, allowUiUpdate)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Dashboard)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *SaveDashboardDTO, bool) error); ok {
r1 = rf(ctx, dto, allowUiUpdate)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SavePublicDashboardConfig provides a mock function with given fields: ctx, dto
func (_m *FakeDashboardService) SavePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*models.PublicDashboardConfig, error) {
ret := _m.Called(ctx, dto)
var r0 *models.PublicDashboardConfig
if rf, ok := ret.Get(0).(func(context.Context, *SavePublicDashboardConfigDTO) *models.PublicDashboardConfig); ok {
r0 = rf(ctx, dto)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.PublicDashboardConfig)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *SavePublicDashboardConfigDTO) error); ok {
r1 = rf(ctx, dto)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SearchDashboards provides a mock function with given fields: ctx, query
func (_m *FakeDashboardService) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
ret := _m.Called(ctx, query)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.FindPersistedDashboardsQuery) error); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateDashboardACL provides a mock function with given fields: ctx, uid, items
func (_m *FakeDashboardService) UpdateDashboardACL(ctx context.Context, uid int64, items []*models.DashboardAcl) error {
ret := _m.Called(ctx, uid, items)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, []*models.DashboardAcl) error); ok {
r0 = rf(ctx, uid, items)
} else {
r0 = ret.Error(0)
}
return r0
}
// NewFakeDashboardService creates a new instance of FakeDashboardService. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewFakeDashboardService(t testing.TB) *FakeDashboardService {
mock := &FakeDashboardService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -15,19 +15,22 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/util"
)
type DashboardStore struct {
sqlStore *sqlstore.SQLStore
log log.Logger
dialect migrator.Dialect
}
// DashboardStore implements the Store interface
var _ dashboards.Store = (*DashboardStore)(nil)
func ProvideDashboardStore(sqlStore *sqlstore.SQLStore) *DashboardStore {
return &DashboardStore{sqlStore: sqlStore, log: log.New("dashboard-store")}
return &DashboardStore{sqlStore: sqlStore, log: log.New("dashboard-store"), dialect: sqlStore.Dialect}
}
func (d *DashboardStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (bool, error) {
@@ -926,3 +929,143 @@ func (d *DashboardStore) GetDashboards(ctx context.Context, query *models.GetDas
return err
})
}
func (d *DashboardStore) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
res, err := d.FindDashboards(ctx, query)
if err != nil {
return err
}
makeQueryResult(query, res)
return nil
}
func getHitType(item dashboards.DashboardSearchProjection) models.HitType {
var hitType models.HitType
if item.IsFolder {
hitType = models.DashHitFolder
} else {
hitType = models.DashHitDB
}
return hitType
}
func makeQueryResult(query *models.FindPersistedDashboardsQuery, res []dashboards.DashboardSearchProjection) {
query.Result = make([]*models.Hit, 0)
hits := make(map[int64]*models.Hit)
for _, item := range res {
hit, exists := hits[item.ID]
if !exists {
hit = &models.Hit{
ID: item.ID,
UID: item.UID,
Title: item.Title,
URI: "db/" + item.Slug,
URL: models.GetDashboardFolderUrl(item.IsFolder, item.UID, item.Slug),
Type: getHitType(item),
FolderID: item.FolderID,
FolderUID: item.FolderUID,
FolderTitle: item.FolderTitle,
Tags: []string{},
}
if item.FolderID > 0 {
hit.FolderURL = models.GetFolderUrl(item.FolderUID, item.FolderSlug)
}
if query.Sort.MetaName != "" {
hit.SortMeta = item.SortMeta
hit.SortMetaName = query.Sort.MetaName
}
query.Result = append(query.Result, hit)
hits[item.ID] = hit
}
if len(item.Term) > 0 {
hit.Tags = append(hit.Tags, item.Term)
}
}
}
func (d *DashboardStore) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) {
filters := []interface{}{
permissions.DashboardPermissionFilter{
OrgRole: query.SignedInUser.OrgRole,
OrgId: query.SignedInUser.OrgId,
Dialect: d.dialect,
UserId: query.SignedInUser.UserId,
PermissionLevel: query.Permission,
},
}
if !ac.IsDisabled(d.sqlStore.Cfg) {
// if access control is enabled, overwrite the filters so far
filters = []interface{}{
permissions.NewAccessControlDashboardPermissionFilter(query.SignedInUser, query.Permission, query.Type),
}
}
for _, filter := range query.Sort.Filter {
filters = append(filters, filter)
}
filters = append(filters, query.Filters...)
if query.OrgId != 0 {
filters = append(filters, searchstore.OrgFilter{OrgId: query.OrgId})
} else if query.SignedInUser.OrgId != 0 {
filters = append(filters, searchstore.OrgFilter{OrgId: query.SignedInUser.OrgId})
}
if len(query.Tags) > 0 {
filters = append(filters, searchstore.TagsFilter{Tags: query.Tags})
}
if len(query.DashboardIds) > 0 {
filters = append(filters, searchstore.DashboardFilter{IDs: query.DashboardIds})
}
if query.IsStarred {
filters = append(filters, searchstore.StarredFilter{UserId: query.SignedInUser.UserId})
}
if len(query.Title) > 0 {
filters = append(filters, searchstore.TitleFilter{Dialect: d.dialect, Title: query.Title})
}
if len(query.Type) > 0 {
filters = append(filters, searchstore.TypeFilter{Dialect: d.dialect, Type: query.Type})
}
if len(query.FolderIds) > 0 {
filters = append(filters, searchstore.FolderFilter{IDs: query.FolderIds})
}
var res []dashboards.DashboardSearchProjection
sb := &searchstore.Builder{Dialect: d.dialect, Filters: filters}
limit := query.Limit
if limit < 1 {
limit = 1000
}
page := query.Page
if page < 1 {
page = 1
}
sql, params := sb.ToSQL(limit, page)
err := d.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
return sess.SQL(sql, params...).Find(&res)
})
if err != nil {
return nil, err
}
return res, nil
}

View File

@@ -242,7 +242,7 @@ func TestDashboardDataAccess(t *testing.T) {
SignedInUser: &models.SignedInUser{},
}
err = sqlStore.SearchDashboards(context.Background(), &query)
err = dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 0)
@@ -315,7 +315,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
}
err := sqlStore.SearchDashboards(context.Background(), &query)
err := dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 1)
@@ -339,7 +339,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
}
err := sqlStore.SearchDashboards(context.Background(), &query)
err := dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 1)
@@ -364,7 +364,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
}
err := sqlStore.SearchDashboards(context.Background(), &query)
err := dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 1)
@@ -386,7 +386,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
}
err := sqlStore.SearchDashboards(context.Background(), &query)
err := dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 3)
@@ -407,7 +407,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
}
err := sqlStore.SearchDashboards(context.Background(), &query)
err := dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 2)
@@ -433,7 +433,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
}
err := sqlStore.SearchDashboards(context.Background(), &query)
err := dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 2)
@@ -471,7 +471,7 @@ func TestDashboardDataAccess(t *testing.T) {
},
IsStarred: true,
}
err = sqlStore.SearchDashboards(context.Background(), &query)
err = dashboardStore.SearchDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 1)

View File

@@ -20,3 +20,17 @@ type SavePublicDashboardConfigDTO struct {
OrgId int64
PublicDashboardConfig models.PublicDashboardConfig
}
type DashboardSearchProjection struct {
ID int64 `xorm:"id"`
UID string `xorm:"uid"`
Title string
Slug string
Term string
IsFolder bool
FolderID int64 `xorm:"folder_id"`
FolderUID string `xorm:"folder_uid"`
FolderSlug string
FolderTitle string
SortMeta int64
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/alerting"
m "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/setting"
@@ -22,19 +22,19 @@ import (
var (
provisionerPermissions = map[string][]string{
m.ActionFoldersCreate: {},
m.ActionFoldersWrite: {m.ScopeFoldersAll},
m.ActionDashboardsCreate: {m.ScopeFoldersAll},
m.ActionDashboardsWrite: {m.ScopeFoldersAll},
dashboards.ActionFoldersCreate: {},
dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersAll},
dashboards.ActionDashboardsCreate: {dashboards.ScopeFoldersAll},
dashboards.ActionDashboardsWrite: {dashboards.ScopeFoldersAll},
}
// DashboardServiceImpl implements the DashboardService interface
_ m.DashboardService = (*DashboardServiceImpl)(nil)
_ dashboards.DashboardService = (*DashboardServiceImpl)(nil)
)
type DashboardServiceImpl struct {
cfg *setting.Cfg
log log.Logger
dashboardStore m.Store
dashboardStore dashboards.Store
dashAlertExtractor alerting.DashAlertExtractor
features featuremgmt.FeatureToggles
folderPermissions accesscontrol.FolderPermissionsService
@@ -42,7 +42,7 @@ type DashboardServiceImpl struct {
}
func ProvideDashboardService(
cfg *setting.Cfg, store m.Store, dashAlertExtractor alerting.DashAlertExtractor,
cfg *setting.Cfg, store dashboards.Store, dashAlertExtractor alerting.DashAlertExtractor,
features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
) *DashboardServiceImpl {
@@ -69,7 +69,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(orgID
return dr.dashboardStore.GetProvisionedDataByDashboardUID(orgID, dashboardUID)
}
func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, dto *m.SaveDashboardDTO, shouldValidateAlerts bool,
func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO, shouldValidateAlerts bool,
validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
dash := dto.Dashboard
@@ -204,7 +204,7 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
return nil
}
func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dto *m.SaveDashboardDTO,
func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO,
provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
dr.log.Warn("Changing refresh interval for provisioned dashboard to minimum refresh interval", "dashboardUid",
@@ -258,7 +258,7 @@ func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dt
return dash, nil
}
func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.Context, dto *m.SaveDashboardDTO) (*models.Dashboard, error) {
func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.Context, dto *dashboards.SaveDashboardDTO) (*models.Dashboard, error) {
dto.User = &models.SignedInUser{
UserId: 0,
OrgRole: models.ROLE_ADMIN,
@@ -299,7 +299,7 @@ func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.C
return dash, nil
}
func (dr *DashboardServiceImpl) SaveDashboard(ctx context.Context, dto *m.SaveDashboardDTO,
func (dr *DashboardServiceImpl) SaveDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO,
allowUiUpdate bool) (*models.Dashboard, error) {
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval",
@@ -356,7 +356,7 @@ func (dr *DashboardServiceImpl) GetPublicDashboardConfig(ctx context.Context, or
// SavePublicDashboardConfig is a helper method to persist the sharing config
// to the database. It handles validations for sharing config and persistence
func (dr *DashboardServiceImpl) SavePublicDashboardConfig(ctx context.Context, dto *m.SavePublicDashboardConfigDTO) (*models.PublicDashboardConfig, error) {
func (dr *DashboardServiceImpl) SavePublicDashboardConfig(ctx context.Context, dto *dashboards.SavePublicDashboardConfigDTO) (*models.PublicDashboardConfig, error) {
cmd := models.SavePublicDashboardConfigCommand{
Uid: dto.Uid,
OrgId: dto.OrgId,
@@ -440,7 +440,7 @@ func (dr *DashboardServiceImpl) deleteDashboard(ctx context.Context, dashboardId
return dr.dashboardStore.DeleteDashboard(ctx, cmd)
}
func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *m.SaveDashboardDTO) (
func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO) (
*models.Dashboard, error) {
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval",
@@ -476,7 +476,7 @@ func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, que
return dr.dashboardStore.GetDashboardsByPluginID(ctx, query)
}
func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto *m.SaveDashboardDTO, dash *models.Dashboard, provisioned bool) error {
func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto *dashboards.SaveDashboardDTO, dash *models.Dashboard, provisioned bool) error {
inFolder := dash.FolderId > 0
if !accesscontrol.IsDisabled(dr.cfg) {
var permissions []accesscontrol.SetResourcePermissionCommand
@@ -522,3 +522,11 @@ func (dr *DashboardServiceImpl) GetDashboardUIDById(ctx context.Context, query *
func (dr *DashboardServiceImpl) GetDashboards(ctx context.Context, query *models.GetDashboardsQuery) error {
return dr.dashboardStore.GetDashboards(ctx, query)
}
func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) {
return dr.dashboardStore.FindDashboards(ctx, query)
}
func (dr *DashboardServiceImpl) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
return dr.dashboardStore.SearchDashboards(ctx, query)
}

View File

@@ -37,8 +37,7 @@ func TestProvideFolderService(t *testing.T) {
ac := acmock.New()
ProvideFolderService(
cfg, &dashboards.FakeDashboardService{DashboardService: dashboardService},
store, nil, features, folderPermissions, ac,
cfg, dashboardService, store, nil, features, folderPermissions, ac,
)
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 2)

View File

@@ -44,6 +44,29 @@ func (_m *FakeDashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Co
return r0
}
// FindDashboards provides a mock function with given fields: ctx, query
func (_m *FakeDashboardStore) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
ret := _m.Called(ctx, query)
var r0 []DashboardSearchProjection
if rf, ok := ret.Get(0).(func(context.Context, *models.FindPersistedDashboardsQuery) []DashboardSearchProjection); ok {
r0 = rf(ctx, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]DashboardSearchProjection)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.FindPersistedDashboardsQuery) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetDashboard provides a mock function with given fields: ctx, query
func (_m *FakeDashboardStore) GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error {
ret := _m.Called(ctx, query)
@@ -344,6 +367,20 @@ func (_m *FakeDashboardStore) SavePublicDashboardConfig(cmd models.SavePublicDas
return r0, r1
}
// SearchDashboards provides a mock function with given fields: ctx, query
func (_m *FakeDashboardStore) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
ret := _m.Called(ctx, query)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *models.FindPersistedDashboardsQuery) error); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Error(0)
}
return r0
}
// UnprovisionDashboard provides a mock function with given fields: ctx, id
func (_m *FakeDashboardStore) UnprovisionDashboard(ctx context.Context, id int64) error {
ret := _m.Called(ctx, id)

View File

@@ -101,8 +101,7 @@ func TestAccessControlDashboardGuardian_CanSave(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions)
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t))
can, err := guardian.CanSave()
require.NoError(t, err)
assert.Equal(t, tt.expected, can)
@@ -193,7 +192,7 @@ func TestAccessControlDashboardGuardian_CanEdit(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions)
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t))
if tt.viewersCanEdit {
setting.ViewersCanEdit = true
@@ -277,7 +276,7 @@ func TestAccessControlDashboardGuardian_CanView(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions)
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t))
can, err := guardian.CanView()
require.NoError(t, err)
@@ -381,7 +380,7 @@ func TestAccessControlDashboardGuardian_CanAdmin(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions)
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t))
can, err := guardian.CanAdmin()
require.NoError(t, err)
@@ -461,7 +460,7 @@ func TestAccessControlDashboardGuardian_CanDelete(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions)
guardian, _ := setupAccessControlGuardianTest(t, tt.dashUID, tt.permissions, testDashSvc(t))
can, err := guardian.CanDelete()
require.NoError(t, err)
@@ -525,7 +524,7 @@ func TestAccessControlDashboardGuardian_CanCreate(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, "0", tt.permissions)
guardian, _ := setupAccessControlGuardianTest(t, "0", tt.permissions, nil)
can, err := guardian.CanCreate(tt.folderID, tt.isFolder)
require.NoError(t, err)
@@ -557,7 +556,7 @@ func TestAccessControlDashboardGuardian_GetHiddenACL(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
guardian, _ := setupAccessControlGuardianTest(t, "1", nil)
guardian, _ := setupAccessControlGuardianTest(t, "1", nil, testDashSvc(t))
mocked := accesscontrolmock.NewMockedPermissionsService()
guardian.dashboardPermissionsService = mocked
@@ -579,7 +578,7 @@ func TestAccessControlDashboardGuardian_GetHiddenACL(t *testing.T) {
}
}
func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []*accesscontrol.Permission) (*AccessControlDashboardGuardian, *models.Dashboard) {
func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []*accesscontrol.Permission, dashboardSvc dashboards.DashboardService) (*AccessControlDashboardGuardian, *models.Dashboard) {
t.Helper()
store := sqlstore.InitTestDB(t)
@@ -601,6 +600,20 @@ func setupAccessControlGuardianTest(t *testing.T, uid string, permissions []*acc
dashboardPermissions, err := ossaccesscontrol.ProvideDashboardPermissions(
setting.NewCfg(), routing.NewRouteRegister(), store, ac, database.ProvideService(store), &dashboards.FakeDashboardStore{})
require.NoError(t, err)
return NewAccessControlDashboardGuardian(context.Background(), dash.Id, &models.SignedInUser{OrgId: 1}, store, ac, folderPermissions, dashboardPermissions, &dashboards.FakeDashboardService{}), dash
if dashboardSvc == nil {
dashboardSvc = &dashboards.FakeDashboardService{}
}
return NewAccessControlDashboardGuardian(context.Background(), dash.Id, &models.SignedInUser{OrgId: 1}, store, ac, folderPermissions, dashboardPermissions, dashboardSvc), dash
}
func testDashSvc(t *testing.T) dashboards.DashboardService {
dashSvc := dashboards.NewFakeDashboardService(t)
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardQuery)
d := models.NewDashboard("mocked")
d.Id = 1
d.Uid = "1"
q.Result = d
}).Return(nil)
return dashSvc
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/components/imguploader"
@@ -78,19 +79,17 @@ func TestBrowserScreenshotService(t *testing.T) {
s := NewBrowserScreenshotService(&d, r)
// a non-existent dashboard should return error
d.GetDashboardFn = func(ctx context.Context, cmd *models.GetDashboardQuery) error {
return models.ErrDashboardNotFound
}
d.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Return(models.ErrDashboardNotFound).Once()
ctx := context.Background()
opts := ScreenshotOptions{}
screenshot, err := s.Take(ctx, opts)
assert.EqualError(t, err, "Dashboard not found")
assert.Nil(t, screenshot)
d.GetDashboardFn = func(ctx context.Context, cmd *models.GetDashboardQuery) error {
cmd.Result = &models.Dashboard{Id: 1, Uid: "foo", Slug: "bar", OrgId: 2}
return nil
}
d.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardQuery)
q.Result = &models.Dashboard{Id: 1, Uid: "foo", Slug: "bar", OrgId: 2}
}).Return(nil)
renderOpts := rendering.Opts{
AuthOpts: rendering.AuthOpts{