Chore: Move folder service into a separate package (#56591)

* Chore: move folder service interface into a separate package

* copy implementation into a standalone package

* move implementation and tests to the new folder package

* remove leftovers from wire

* add test doubles for folder service

* fix tests in library panels/elements

* fix provideservice in ngalert
This commit is contained in:
Serge Zaitsev 2022-10-10 21:47:53 +02:00 committed by GitHub
parent eb077db2b0
commit 53baecd71f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 148 additions and 308 deletions

View File

@ -30,6 +30,7 @@ import (
dashver "github.com/grafana/grafana/pkg/services/dashboardversion" dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest" "github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/live" "github.com/grafana/grafana/pkg/services/live"
@ -1017,7 +1018,7 @@ func callPostDashboardShouldReturnSuccess(sc *scenarioContext) {
assert.Equal(sc.t, 200, sc.resp.Code) assert.Equal(sc.t, 200, sc.resp.Code)
} }
func postDashboardScenario(t *testing.T, desc string, url string, routePattern string, cmd models.SaveDashboardCommand, dashboardService dashboards.DashboardService, folderService dashboards.FolderService, fn scenarioFunc) { func postDashboardScenario(t *testing.T, desc string, url string, routePattern string, cmd models.SaveDashboardCommand, dashboardService dashboards.DashboardService, folderService folder.Service, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) { t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
hs := HTTPServer{ hs := HTTPServer{

View File

@ -17,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
service "github.com/grafana/grafana/pkg/services/dashboards/service" service "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
@ -26,8 +27,7 @@ import (
func TestFolderPermissionAPIEndpoint(t *testing.T) { func TestFolderPermissionAPIEndpoint(t *testing.T) {
settings := setting.NewCfg() settings := setting.NewCfg()
folderService := &dashboards.FakeFolderService{} folderService := &foldertest.FakeService{}
defer folderService.AssertExpectations(t)
dashboardStore := &dashboards.FakeDashboardStore{} dashboardStore := &dashboards.FakeDashboardStore{}
defer dashboardStore.AssertExpectations(t) defer dashboardStore.AssertExpectations(t)
@ -50,7 +50,10 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
} }
t.Run("Given folder not exists", func(t *testing.T) { t.Run("Given folder not exists", func(t *testing.T) {
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, dashboards.ErrFolderNotFound).Twice() t.Cleanup(func() {
folderService.ExpectedError = nil
})
folderService.ExpectedError = dashboards.ErrFolderNotFound
mockSQLStore := mockstore.NewSQLStoreMock() mockSQLStore := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleEditor, func(sc *scenarioContext) { loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleEditor, func(sc *scenarioContext) {
callGetFolderPermissions(sc, hs) callGetFolderPermissions(sc, hs)
@ -79,10 +82,11 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
origNewGuardian := guardian.New origNewGuardian := guardian.New
t.Cleanup(func() { t.Cleanup(func() {
guardian.New = origNewGuardian guardian.New = origNewGuardian
folderService.ExpectedError = nil
}) })
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false}) guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, dashboards.ErrFolderAccessDenied).Twice() folderService.ExpectedError = dashboards.ErrFolderAccessDenied
mockSQLStore := mockstore.NewSQLStoreMock() mockSQLStore := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleEditor, func(sc *scenarioContext) { loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleEditor, func(sc *scenarioContext) {
@ -126,8 +130,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
}, },
}) })
folderResponse := &models.Folder{Id: 1, Uid: "uid", Title: "Folder"} folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(folderResponse, nil).Twice()
dashboardStore.On("UpdateDashboardACL", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() dashboardStore.On("UpdateDashboardACL", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
mockSQLStore := mockstore.NewSQLStoreMock() mockSQLStore := mockstore.NewSQLStoreMock()
@ -184,8 +187,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists, CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
}) })
folderResponse := &models.Folder{Id: 1, Uid: "uid", Title: "Folder"} folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(folderResponse, nil).Once()
cmd := dtos.UpdateDashboardACLCommand{ cmd := dtos.UpdateDashboardACLCommand{
Items: []dtos.DashboardACLUpdateItem{ Items: []dtos.DashboardACLUpdateItem{
@ -249,8 +251,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride}, CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
) )
folderResponse := &models.Folder{Id: 1, Uid: "uid", Title: "Folder"} folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(folderResponse, nil).Once()
cmd := dtos.UpdateDashboardACLCommand{ cmd := dtos.UpdateDashboardACLCommand{
Items: []dtos.DashboardACLUpdateItem{ Items: []dtos.DashboardACLUpdateItem{
@ -296,8 +297,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
var gotItems []*models.DashboardACL var gotItems []*models.DashboardACL
folderResponse := &models.Folder{Id: 1, Uid: "uid", Title: "Folder"} folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(folderResponse, nil).Twice()
dashboardStore.On("UpdateDashboardACL", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { dashboardStore.On("UpdateDashboardACL", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
gotItems = args.Get(2).([]*models.DashboardACL) gotItems = args.Get(2).([]*models.DashboardACL)
}).Return(nil).Once() }).Return(nil).Once()

View File

@ -19,6 +19,8 @@ import (
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"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/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/quota/quotatest" "github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
@ -29,8 +31,7 @@ import (
) )
func TestFoldersAPIEndpoint(t *testing.T) { func TestFoldersAPIEndpoint(t *testing.T) {
folderService := &dashboards.FakeFolderService{} folderService := &foldertest.FakeService{}
defer folderService.AssertExpectations(t)
t.Run("Given a correct request for creating a folder", func(t *testing.T) { t.Run("Given a correct request for creating a folder", func(t *testing.T) {
cmd := models.CreateFolderCommand{ cmd := models.CreateFolderCommand{
@ -38,8 +39,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
Title: "Folder", Title: "Folder",
} }
folderResult := &models.Folder{Id: 1, Uid: "uid", Title: "Folder"} folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder"}
folderService.On("CreateFolder", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(folderResult, nil).Once()
createFolderScenario(t, "When calling POST on", "/api/folders", "/api/folders", folderService, cmd, createFolderScenario(t, "When calling POST on", "/api/folders", "/api/folders", folderService, cmd,
func(sc *scenarioContext) { func(sc *scenarioContext) {
@ -55,6 +55,9 @@ func TestFoldersAPIEndpoint(t *testing.T) {
}) })
t.Run("Given incorrect requests for creating a folder", func(t *testing.T) { t.Run("Given incorrect requests for creating a folder", func(t *testing.T) {
t.Cleanup(func() {
folderService.ExpectedError = nil
})
testCases := []struct { testCases := []struct {
Error error Error error
ExpectedStatusCode int ExpectedStatusCode int
@ -76,7 +79,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
} }
for _, tc := range testCases { for _, tc := range testCases {
folderService.On("CreateFolder", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, tc.Error).Once() folderService.ExpectedError = tc.Error
createFolderScenario(t, fmt.Sprintf("Expect '%s' error when calling POST on", tc.Error.Error()), createFolderScenario(t, fmt.Sprintf("Expect '%s' error when calling POST on", tc.Error.Error()),
"/api/folders", "/api/folders", folderService, cmd, func(sc *scenarioContext) { "/api/folders", "/api/folders", folderService, cmd, func(sc *scenarioContext) {
@ -91,10 +94,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
Title: "Folder upd", Title: "Folder upd",
} }
folderService.On("UpdateFolder", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { folderService.ExpectedFolder = &models.Folder{Id: 1, Uid: "uid", Title: "Folder upd"}
cmd := args.Get(4).(*models.UpdateFolderCommand)
cmd.Result = &models.Folder{Id: 1, Uid: "uid", Title: "Folder upd"}
}).Return(nil).Once()
updateFolderScenario(t, "When calling PUT on", "/api/folders/uid", "/api/folders/:uid", folderService, cmd, updateFolderScenario(t, "When calling PUT on", "/api/folders/uid", "/api/folders/:uid", folderService, cmd,
func(sc *scenarioContext) { func(sc *scenarioContext) {
@ -130,7 +130,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
} }
for _, tc := range testCases { for _, tc := range testCases {
folderService.On("UpdateFolder", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.Error).Once() folderService.ExpectedError = tc.Error
updateFolderScenario(t, fmt.Sprintf("Expect '%s' error when calling PUT on", tc.Error.Error()), updateFolderScenario(t, fmt.Sprintf("Expect '%s' error when calling PUT on", tc.Error.Error()),
"/api/folders/uid", "/api/folders/:uid", folderService, cmd, func(sc *scenarioContext) { "/api/folders/uid", "/api/folders/:uid", folderService, cmd, func(sc *scenarioContext) {
callUpdateFolder(sc) callUpdateFolder(sc)
@ -142,7 +142,7 @@ func TestFoldersAPIEndpoint(t *testing.T) {
func TestHTTPServer_FolderMetadata(t *testing.T) { func TestHTTPServer_FolderMetadata(t *testing.T) {
setUpRBACGuardian(t) setUpRBACGuardian(t)
folderService := dashboards.NewFakeFolderService(t) folderService := &foldertest.FakeService{}
server := SetupAPITestServer(t, func(hs *HTTPServer) { server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.folderService = folderService hs.folderService = folderService
hs.AccessControl = acmock.New() hs.AccessControl = acmock.New()
@ -150,11 +150,7 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
}) })
t.Run("Should attach access control metadata to multiple folders", func(t *testing.T) { t.Run("Should attach access control metadata to multiple folders", func(t *testing.T) {
folderService.On("GetFolders", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]*models.Folder{ folderService.ExpectedFolders = []*models.Folder{{Uid: "1"}, {Uid: "2"}, {Uid: "3"}}
{Uid: "1"},
{Uid: "2"},
{Uid: "3"},
}, nil)
req := server.NewGetRequest("/api/folders?accesscontrol=true") req := server.NewGetRequest("/api/folders?accesscontrol=true")
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{ webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{
@ -183,7 +179,7 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
}) })
t.Run("Should attach access control metadata to folder response", func(t *testing.T) { t.Run("Should attach access control metadata to folder response", func(t *testing.T) {
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&models.Folder{Uid: "folderUid"}, nil) folderService.ExpectedFolder = &models.Folder{Uid: "folderUid"}
req := server.NewGetRequest("/api/folders/folderUid?accesscontrol=true") req := server.NewGetRequest("/api/folders/folderUid?accesscontrol=true")
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{ webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{
@ -206,7 +202,7 @@ func TestHTTPServer_FolderMetadata(t *testing.T) {
}) })
t.Run("Should attach access control metadata to folder response", func(t *testing.T) { t.Run("Should attach access control metadata to folder response", func(t *testing.T) {
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&models.Folder{Uid: "folderUid"}, nil) folderService.ExpectedFolder = &models.Folder{Uid: "folderUid"}
req := server.NewGetRequest("/api/folders/folderUid") req := server.NewGetRequest("/api/folders/folderUid")
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{ webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{
@ -233,7 +229,7 @@ func callCreateFolder(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
} }
func createFolderScenario(t *testing.T, desc string, url string, routePattern string, folderService dashboards.FolderService, func createFolderScenario(t *testing.T, desc string, url string, routePattern string, folderService folder.Service,
cmd models.CreateFolderCommand, fn scenarioFunc) { cmd models.CreateFolderCommand, fn scenarioFunc) {
setUpRBACGuardian(t) setUpRBACGuardian(t)
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) { t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
@ -273,7 +269,7 @@ func callUpdateFolder(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec() sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
} }
func updateFolderScenario(t *testing.T, desc string, url string, routePattern string, folderService dashboards.FolderService, func updateFolderScenario(t *testing.T, desc string, url string, routePattern string, folderService folder.Service,
cmd models.UpdateFolderCommand, fn scenarioFunc) { cmd models.UpdateFolderCommand, fn scenarioFunc) {
setUpRBACGuardian(t) setUpRBACGuardian(t)
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) { t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
@ -300,7 +296,7 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
} }
type fakeFolderService struct { type fakeFolderService struct {
dashboards.FolderService folder.Service
GetFoldersResult []*models.Folder GetFoldersResult []*models.Folder
GetFoldersError error GetFoldersError error

View File

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware/csrf" "github.com/grafana/grafana/pkg/middleware/csrf"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/querylibrary" "github.com/grafana/grafana/pkg/services/querylibrary"
"github.com/grafana/grafana/pkg/services/searchV2" "github.com/grafana/grafana/pkg/services/searchV2"
"github.com/grafana/grafana/pkg/services/store/object" "github.com/grafana/grafana/pkg/services/store/object"
@ -176,7 +177,7 @@ type HTTPServer struct {
NotificationService *notifications.NotificationService NotificationService *notifications.NotificationService
DashboardService dashboards.DashboardService DashboardService dashboards.DashboardService
dashboardProvisioningService dashboards.DashboardProvisioningService dashboardProvisioningService dashboards.DashboardProvisioningService
folderService dashboards.FolderService folderService folder.Service
DatasourcePermissionsService permissions.DatasourcePermissionsService DatasourcePermissionsService permissions.DatasourcePermissionsService
commentsService *comments.Service commentsService *comments.Service
AlertNotificationService *alerting.AlertNotificationService AlertNotificationService *alerting.AlertNotificationService
@ -232,7 +233,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
ldapGroups ldap.Groups, teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service, ldapGroups ldap.Groups, teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service,
authInfoService login.AuthInfoService, storageService store.StorageService, httpObjectStore object.HTTPObjectStore, authInfoService login.AuthInfoService, storageService store.StorageService, httpObjectStore object.HTTPObjectStore,
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService, notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService dashboards.FolderService, dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
datasourcePermissionsService permissions.DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService, datasourcePermissionsService permissions.DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService,
dashboardsnapshotsService dashboardsnapshots.Service, commentsService *comments.Service, pluginSettings pluginSettings.Service, dashboardsnapshotsService dashboardsnapshots.Service, commentsService *comments.Service, pluginSettings pluginSettings.Service,
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service, avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service,

View File

@ -281,12 +281,10 @@ var wireSet = wire.NewSet(
wire.Bind(new(teamguardian.Store), new(*teamguardianDatabase.TeamGuardianStoreImpl)), wire.Bind(new(teamguardian.Store), new(*teamguardianDatabase.TeamGuardianStoreImpl)),
teamguardianManager.ProvideService, teamguardianManager.ProvideService,
dashboardservice.ProvideDashboardService, dashboardservice.ProvideDashboardService,
dashboardservice.ProvideFolderService,
dashboardstore.ProvideDashboardStore, dashboardstore.ProvideDashboardStore,
wire.Bind(new(dashboards.DashboardService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.DashboardService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.FolderService), new(*dashboardservice.FolderServiceImpl)),
wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)), wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)),
dashboardimportservice.ProvideService, dashboardimportservice.ProvideService,
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)), wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),

View File

@ -70,6 +70,7 @@ import (
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service" encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
"github.com/grafana/grafana/pkg/services/export" "github.com/grafana/grafana/pkg/services/export"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/grpcserver" "github.com/grafana/grafana/pkg/services/grpcserver"
grpccontext "github.com/grafana/grafana/pkg/services/grpcserver/context" grpccontext "github.com/grafana/grafana/pkg/services/grpcserver/context"
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors" "github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
@ -308,12 +309,11 @@ var wireBasicSet = wire.NewSet(
featuremgmt.ProvideManagerService, featuremgmt.ProvideManagerService,
featuremgmt.ProvideToggles, featuremgmt.ProvideToggles,
dashboardservice.ProvideDashboardService, dashboardservice.ProvideDashboardService,
dashboardservice.ProvideFolderService,
dashboardstore.ProvideDashboardStore, dashboardstore.ProvideDashboardStore,
folderimpl.ProvideService,
wire.Bind(new(dashboards.DashboardService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.DashboardService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.DashboardProvisioningService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)), wire.Bind(new(dashboards.PluginService), new(*dashboardservice.DashboardServiceImpl)),
wire.Bind(new(dashboards.FolderService), new(*dashboardservice.FolderServiceImpl)),
wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)), wire.Bind(new(dashboards.Store), new(*dashboardstore.DashboardStore)),
dashboardimportservice.ProvideService, dashboardimportservice.ProvideService,
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)), wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),

View File

@ -1,194 +0,0 @@
// Code generated by mockery v2.12.1. DO NOT EDIT.
package dashboards
import (
context "context"
models "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/user"
mock "github.com/stretchr/testify/mock"
testing "testing"
)
// FakeFolderService is an autogenerated mock type for the FolderService type
type FakeFolderService struct {
mock.Mock
}
// CreateFolder provides a mock function with given fields: ctx, user, orgID, title, uid
func (_m *FakeFolderService) CreateFolder(ctx context.Context, usr *user.SignedInUser, orgID int64, title string, uid string) (*models.Folder, error) {
ret := _m.Called(ctx, usr, orgID, title, uid)
var r0 *models.Folder
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, string, string) *models.Folder); ok {
r0 = rf(ctx, usr, orgID, title, uid)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64, string, string) error); ok {
r1 = rf(ctx, usr, orgID, title, uid)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteFolder provides a mock function with given fields: ctx, user, orgID, uid, forceDeleteRules
func (_m *FakeFolderService) DeleteFolder(ctx context.Context, usr *user.SignedInUser, orgID int64, uid string, forceDeleteRules bool) (*models.Folder, error) {
ret := _m.Called(ctx, usr, orgID, uid, forceDeleteRules)
var r0 *models.Folder
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, string, bool) *models.Folder); ok {
r0 = rf(ctx, usr, orgID, uid, forceDeleteRules)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64, string, bool) error); ok {
r1 = rf(ctx, usr, orgID, uid, forceDeleteRules)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetFolderByID provides a mock function with given fields: ctx, user, id, orgID
func (_m *FakeFolderService) GetFolderByID(ctx context.Context, usr *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) {
ret := _m.Called(ctx, usr, id, orgID)
var r0 *models.Folder
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, int64) *models.Folder); ok {
r0 = rf(ctx, usr, id, orgID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64, int64) error); ok {
r1 = rf(ctx, usr, id, orgID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetFolderByTitle provides a mock function with given fields: ctx, user, orgID, title
func (_m *FakeFolderService) GetFolderByTitle(ctx context.Context, usr *user.SignedInUser, orgID int64, title string) (*models.Folder, error) {
ret := _m.Called(ctx, usr, orgID, title)
var r0 *models.Folder
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, string) *models.Folder); ok {
r0 = rf(ctx, usr, orgID, title)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64, string) error); ok {
r1 = rf(ctx, usr, orgID, title)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetFolderByUID provides a mock function with given fields: ctx, user, orgID, uid
func (_m *FakeFolderService) GetFolderByUID(ctx context.Context, usr *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
ret := _m.Called(ctx, usr, orgID, uid)
var r0 *models.Folder
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, string) *models.Folder); ok {
r0 = rf(ctx, usr, orgID, uid)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64, string) error); ok {
r1 = rf(ctx, usr, orgID, uid)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetFolders provides a mock function with given fields: ctx, user, orgID, limit, page
func (_m *FakeFolderService) GetFolders(ctx context.Context, usr *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
ret := _m.Called(ctx, usr, orgID, limit, page)
var r0 []*models.Folder
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, int64, int64) []*models.Folder); ok {
r0 = rf(ctx, usr, orgID, limit, page)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Folder)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64, int64, int64) error); ok {
r1 = rf(ctx, usr, orgID, limit, page)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MakeUserAdmin provides a mock function with given fields: ctx, orgID, userID, folderID, setViewAndEditPermissions
func (_m *FakeFolderService) MakeUserAdmin(ctx context.Context, orgID int64, userID int64, folderID int64, setViewAndEditPermissions bool) error {
ret := _m.Called(ctx, orgID, userID, folderID, setViewAndEditPermissions)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, int64, int64, bool) error); ok {
r0 = rf(ctx, orgID, userID, folderID, setViewAndEditPermissions)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateFolder provides a mock function with given fields: ctx, user, orgID, existingUid, cmd
func (_m *FakeFolderService) UpdateFolder(ctx context.Context, usr *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error {
ret := _m.Called(ctx, usr, orgID, existingUid, cmd)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64, string, *models.UpdateFolderCommand) error); ok {
r0 = rf(ctx, usr, orgID, existingUid, cmd)
} else {
r0 = ret.Error(0)
}
return r0
}
// NewFakeFolderService creates a new instance of FakeFolderService. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations.
func NewFakeFolderService(t testing.TB) *FakeFolderService {
mock := &FakeFolderService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
package dashboards package folder
import ( import (
"context" "context"
@ -7,10 +7,8 @@ import (
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
) )
// FolderService is a service for operating on folders. //go:generate mockery --name Service --structname FakeService --inpackage --filename foldertest/folder_service_mock.go
// type Service interface {
//go:generate mockery --name FolderService --structname FakeFolderService --inpackage --filename folder_service_mock.go
type FolderService interface {
GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error)
GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error)
GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error)

View File

@ -1,4 +1,4 @@
package service package folderimpl
import ( import (
"context" "context"
@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"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/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
@ -19,7 +20,7 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
type FolderServiceImpl struct { type Service struct {
log log.Logger log log.Logger
cfg *setting.Cfg cfg *setting.Cfg
dashboardService dashboards.DashboardService dashboardService dashboards.DashboardService
@ -32,15 +33,19 @@ type FolderServiceImpl struct {
bus bus.Bus bus bus.Bus
} }
func ProvideFolderService( func ProvideService(
cfg *setting.Cfg, dashboardService dashboards.DashboardService, dashboardStore dashboards.Store, ac accesscontrol.AccessControl,
searchService *search.SearchService, features featuremgmt.FeatureToggles, folderPermissionsService accesscontrol.FolderPermissionsService, bus bus.Bus,
ac accesscontrol.AccessControl, bus bus.Bus, cfg *setting.Cfg,
) *FolderServiceImpl { dashboardService dashboards.DashboardService,
dashboardStore dashboards.Store,
features featuremgmt.FeatureToggles,
folderPermissionsService accesscontrol.FolderPermissionsService,
searchService *search.SearchService,
) folder.Service {
ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(dashboardStore)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderNameScopeResolver(dashboardStore))
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(dashboardStore)) ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(dashboardStore))
return &Service{
return &FolderServiceImpl{
cfg: cfg, cfg: cfg,
log: log.New("folder-service"), log: log.New("folder-service"),
dashboardService: dashboardService, dashboardService: dashboardService,
@ -52,7 +57,7 @@ func ProvideFolderService(
} }
} }
func (f *FolderServiceImpl) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) { func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
searchQuery := search.Query{ searchQuery := search.Query{
SignedInUser: user, SignedInUser: user,
DashboardIds: make([]int64, 0), DashboardIds: make([]int64, 0),
@ -64,7 +69,7 @@ func (f *FolderServiceImpl) GetFolders(ctx context.Context, user *user.SignedInU
Page: page, Page: page,
} }
if err := f.searchService.SearchHandler(ctx, &searchQuery); err != nil { if err := s.searchService.SearchHandler(ctx, &searchQuery); err != nil {
return nil, err return nil, err
} }
@ -81,11 +86,11 @@ func (f *FolderServiceImpl) GetFolders(ctx context.Context, user *user.SignedInU
return folders, nil return folders, nil
} }
func (f *FolderServiceImpl) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) { func (s *Service) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) {
if id == 0 { if id == 0 {
return &models.Folder{Id: id, Title: "General"}, nil return &models.Folder{Id: id, Title: "General"}, nil
} }
dashFolder, err := f.dashboardStore.GetFolderByID(ctx, orgID, id) dashFolder, err := s.dashboardStore.GetFolderByID(ctx, orgID, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -101,8 +106,8 @@ func (f *FolderServiceImpl) GetFolderByID(ctx context.Context, user *user.Signed
return dashFolder, nil return dashFolder, nil
} }
func (f *FolderServiceImpl) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) { func (s *Service) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
dashFolder, err := f.dashboardStore.GetFolderByUID(ctx, orgID, uid) dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, orgID, uid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -118,8 +123,8 @@ func (f *FolderServiceImpl) GetFolderByUID(ctx context.Context, user *user.Signe
return dashFolder, nil return dashFolder, nil
} }
func (f *FolderServiceImpl) GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) { func (s *Service) GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) {
dashFolder, err := f.dashboardStore.GetFolderByTitle(ctx, orgID, title) dashFolder, err := s.dashboardStore.GetFolderByTitle(ctx, orgID, title)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -135,7 +140,7 @@ func (f *FolderServiceImpl) GetFolderByTitle(ctx context.Context, user *user.Sig
return dashFolder, nil return dashFolder, nil
} }
func (f *FolderServiceImpl) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) { func (s *Service) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) {
dashFolder := models.NewDashboardFolder(title) dashFolder := models.NewDashboardFolder(title)
dashFolder.OrgId = orgID dashFolder.OrgId = orgID
@ -159,24 +164,24 @@ func (f *FolderServiceImpl) CreateFolder(ctx context.Context, user *user.SignedI
User: user, User: user,
} }
saveDashboardCmd, err := f.dashboardService.BuildSaveDashboardCommand(ctx, dto, false, false) saveDashboardCmd, err := s.dashboardService.BuildSaveDashboardCommand(ctx, dto, false, false)
if err != nil { if err != nil {
return nil, toFolderError(err) return nil, toFolderError(err)
} }
dash, err := f.dashboardStore.SaveDashboard(ctx, *saveDashboardCmd) dash, err := s.dashboardStore.SaveDashboard(ctx, *saveDashboardCmd)
if err != nil { if err != nil {
return nil, toFolderError(err) return nil, toFolderError(err)
} }
var folder *models.Folder var folder *models.Folder
folder, err = f.dashboardStore.GetFolderByID(ctx, orgID, dash.Id) folder, err = s.dashboardStore.GetFolderByID(ctx, orgID, dash.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var permissionErr error var permissionErr error
if !accesscontrol.IsDisabled(f.cfg) { if !accesscontrol.IsDisabled(s.cfg) {
var permissions []accesscontrol.SetResourcePermissionCommand var permissions []accesscontrol.SetResourcePermissionCommand
if user.IsRealUser() && !user.IsAnonymous { if user.IsRealUser() && !user.IsAnonymous {
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
@ -189,21 +194,21 @@ func (f *FolderServiceImpl) CreateFolder(ctx context.Context, user *user.SignedI
{BuiltinRole: string(org.RoleViewer), Permission: models.PERMISSION_VIEW.String()}, {BuiltinRole: string(org.RoleViewer), Permission: models.PERMISSION_VIEW.String()},
}...) }...)
_, permissionErr = f.permissions.SetPermissions(ctx, orgID, folder.Uid, permissions...) _, permissionErr = s.permissions.SetPermissions(ctx, orgID, folder.Uid, permissions...)
} else if f.cfg.EditorsCanAdmin && user.IsRealUser() && !user.IsAnonymous { } else if s.cfg.EditorsCanAdmin && user.IsRealUser() && !user.IsAnonymous {
permissionErr = f.MakeUserAdmin(ctx, orgID, userID, folder.Id, true) permissionErr = s.MakeUserAdmin(ctx, orgID, userID, folder.Id, true)
} }
if permissionErr != nil { if permissionErr != nil {
f.log.Error("Could not make user admin", "folder", folder.Title, "user", userID, "error", permissionErr) s.log.Error("Could not make user admin", "folder", folder.Title, "user", userID, "error", permissionErr)
} }
return folder, nil return folder, nil
} }
func (f *FolderServiceImpl) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error { func (s *Service) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error {
query := models.GetDashboardQuery{OrgId: orgID, Uid: existingUid} query := models.GetDashboardQuery{OrgId: orgID, Uid: existingUid}
if _, err := f.dashboardStore.GetDashboard(ctx, &query); err != nil { if _, err := s.dashboardStore.GetDashboard(ctx, &query); err != nil {
return toFolderError(err) return toFolderError(err)
} }
@ -223,40 +228,40 @@ func (f *FolderServiceImpl) UpdateFolder(ctx context.Context, user *user.SignedI
Overwrite: cmd.Overwrite, Overwrite: cmd.Overwrite,
} }
saveDashboardCmd, err := f.dashboardService.BuildSaveDashboardCommand(ctx, dto, false, false) saveDashboardCmd, err := s.dashboardService.BuildSaveDashboardCommand(ctx, dto, false, false)
if err != nil { if err != nil {
return toFolderError(err) return toFolderError(err)
} }
dash, err := f.dashboardStore.SaveDashboard(ctx, *saveDashboardCmd) dash, err := s.dashboardStore.SaveDashboard(ctx, *saveDashboardCmd)
if err != nil { if err != nil {
return toFolderError(err) return toFolderError(err)
} }
var folder *models.Folder var folder *models.Folder
folder, err = f.dashboardStore.GetFolderByID(ctx, orgID, dash.Id) folder, err = s.dashboardStore.GetFolderByID(ctx, orgID, dash.Id)
if err != nil { if err != nil {
return err return err
} }
cmd.Result = folder cmd.Result = folder
if currentTitle != folder.Title { if currentTitle != folder.Title {
if err := f.bus.Publish(ctx, &events.FolderTitleUpdated{ if err := s.bus.Publish(ctx, &events.FolderTitleUpdated{
Timestamp: folder.Updated, Timestamp: folder.Updated,
Title: folder.Title, Title: folder.Title,
ID: dash.Id, ID: dash.Id,
UID: dash.Uid, UID: dash.Uid,
OrgID: orgID, OrgID: orgID,
}); err != nil { }); err != nil {
f.log.Error("failed to publish FolderTitleUpdated event", "folder", folder.Title, "user", user.UserID, "error", err) s.log.Error("failed to publish FolderTitleUpdated event", "folder", folder.Title, "user", user.UserID, "error", err)
} }
} }
return nil return nil
} }
func (f *FolderServiceImpl) DeleteFolder(ctx context.Context, user *user.SignedInUser, orgID int64, uid string, forceDeleteRules bool) (*models.Folder, error) { func (s *Service) DeleteFolder(ctx context.Context, user *user.SignedInUser, orgID int64, uid string, forceDeleteRules bool) (*models.Folder, error) {
dashFolder, err := f.dashboardStore.GetFolderByUID(ctx, orgID, uid) dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, orgID, uid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -271,15 +276,15 @@ func (f *FolderServiceImpl) DeleteFolder(ctx context.Context, user *user.SignedI
deleteCmd := models.DeleteDashboardCommand{OrgId: orgID, Id: dashFolder.Id, ForceDeleteFolderRules: forceDeleteRules} deleteCmd := models.DeleteDashboardCommand{OrgId: orgID, Id: dashFolder.Id, ForceDeleteFolderRules: forceDeleteRules}
if err := f.dashboardStore.DeleteDashboard(ctx, &deleteCmd); err != nil { if err := s.dashboardStore.DeleteDashboard(ctx, &deleteCmd); err != nil {
return nil, toFolderError(err) return nil, toFolderError(err)
} }
return dashFolder, nil return dashFolder, nil
} }
func (f *FolderServiceImpl) MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error { func (s *Service) MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error {
return f.dashboardService.MakeUserAdmin(ctx, orgID, userID, folderID, setViewAndEditPermissions) return s.dashboardService.MakeUserAdmin(ctx, orgID, userID, folderID, setViewAndEditPermissions)
} }
func toFolderError(err error) error { func toFolderError(err error) error {

View File

@ -1,4 +1,4 @@
package service package folderimpl
import ( import (
"context" "context"
@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
dashboardsvc "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
@ -32,7 +33,7 @@ func TestIntegrationProvideFolderService(t *testing.T) {
cfg := setting.NewCfg() cfg := setting.NewCfg()
ac := acmock.New() ac := acmock.New()
ProvideFolderService(cfg, nil, nil, nil, nil, nil, ac, busmock.New()) ProvideService(ac, busmock.New(), cfg, nil, nil, nil, nil, nil)
require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 2) require.Len(t, ac.Calls.RegisterAttributeScopeResolver, 2)
}) })
@ -50,9 +51,9 @@ func TestIntegrationFolderService(t *testing.T) {
cfg.IsFeatureToggleEnabled = features.IsEnabled cfg.IsFeatureToggleEnabled = features.IsEnabled
folderPermissions := acmock.NewMockedPermissionsService() folderPermissions := acmock.NewMockedPermissionsService()
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
dashboardService := ProvideDashboardService(cfg, store, nil, features, folderPermissions, dashboardPermissions, acmock.New()) dashboardService := dashboardsvc.ProvideDashboardService(cfg, store, nil, features, folderPermissions, dashboardPermissions, acmock.New())
service := FolderServiceImpl{ service := &Service{
cfg: cfg, cfg: cfg,
log: log.New("test-folder-service"), log: log.New("test-folder-service"),
dashboardService: dashboardService, dashboardService: dashboardService,

View File

@ -0,0 +1,40 @@
package foldertest
import (
"context"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/user"
)
type FakeService struct {
ExpectedFolders []*models.Folder
ExpectedFolder *models.Folder
ExpectedError error
}
func (s *FakeService) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) {
return s.ExpectedFolders, s.ExpectedError
}
func (s *FakeService) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) {
return s.ExpectedFolder, s.ExpectedError
}
func (s *FakeService) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) {
return s.ExpectedFolder, s.ExpectedError
}
func (s *FakeService) GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) {
return s.ExpectedFolder, s.ExpectedError
}
func (s *FakeService) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) {
return s.ExpectedFolder, s.ExpectedError
}
func (s *FakeService) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error {
cmd.Result = s.ExpectedFolder
return s.ExpectedError
}
func (s *FakeService) DeleteFolder(ctx context.Context, user *user.SignedInUser, orgID int64, uid string, forceDeleteRules bool) (*models.Folder, error) {
return s.ExpectedFolder, s.ExpectedError
}
func (s *FakeService) MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error {
return s.ExpectedError
}

View File

@ -5,13 +5,13 @@ import (
"github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister routing.RouteRegister, folderService dashboards.FolderService) *LibraryElementService { func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister routing.RouteRegister, folderService folder.Service) *LibraryElementService {
l := &LibraryElementService{ l := &LibraryElementService{
Cfg: cfg, Cfg: cfg,
SQLStore: sqlStore, SQLStore: sqlStore,
@ -38,7 +38,7 @@ type LibraryElementService struct {
Cfg *setting.Cfg Cfg *setting.Cfg
SQLStore *sqlstore.SQLStore SQLStore *sqlstore.SQLStore
RouteRegister routing.RouteRegister RouteRegister routing.RouteRegister
folderService dashboards.FolderService folderService folder.Service
log log.Logger log log.Logger
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/dashboards/database"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
@ -308,10 +309,7 @@ func createFolderWithACL(t *testing.T, sqlStore *sqlstore.SQLStore, title string
cfg, dashboardStore, nil, cfg, dashboardStore, nil,
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
) )
s := dashboardservice.ProvideFolderService( s := folderimpl.ProvideService(ac, busmock.New(), cfg, d, dashboardStore, features, folderPermissions, nil)
cfg, d, dashboardStore, nil,
features, folderPermissions, ac, busmock.New(),
)
t.Logf("Creating folder with title and UID %q", title) t.Logf("Creating folder with title and UID %q", title)
folder, err := s.CreateFolder(context.Background(), &user, user.OrgID, title, title) folder, err := s.CreateFolder(context.Background(), &user, user.OrgID, title, title)
require.NoError(t, err) require.NoError(t, err)
@ -421,10 +419,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
service := LibraryElementService{ service := LibraryElementService{
Cfg: sqlStore.Cfg, Cfg: sqlStore.Cfg,
SQLStore: sqlStore, SQLStore: sqlStore,
folderService: dashboardservice.ProvideFolderService( folderService: folderimpl.ProvideService(ac, busmock.New(), sqlStore.Cfg, dashboardService, dashboardStore, features, folderPermissions, nil),
sqlStore.Cfg, dashboardService, dashboardStore, nil,
features, folderPermissions, ac, busmock.New(),
),
} }
usr := user.SignedInUser{ usr := user.SignedInUser{

View File

@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/dashboards/database"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/libraryelements"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
@ -1406,7 +1407,7 @@ func createFolderWithACL(t *testing.T, sqlStore *sqlstore.SQLStore, title string
dashboardPermissions := acmock.NewMockedPermissionsService() dashboardPermissions := acmock.NewMockedPermissionsService()
dashboardStore := database.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg)) dashboardStore := database.ProvideDashboardStore(sqlStore, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg))
d := dashboardservice.ProvideDashboardService(cfg, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac) d := dashboardservice.ProvideDashboardService(cfg, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac)
s := dashboardservice.ProvideFolderService(cfg, d, dashboardStore, nil, features, folderPermissions, ac, busmock.New()) s := folderimpl.ProvideService(ac, busmock.New(), cfg, d, dashboardStore, features, folderPermissions, nil)
t.Logf("Creating folder with title and UID %q", title) t.Logf("Creating folder with title and UID %q", title)
folder, err := s.CreateFolder(context.Background(), user, user.OrgID, title, title) folder, err := s.CreateFolder(context.Background(), user, user.OrgID, title, title)
@ -1508,10 +1509,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
cfg, dashboardStore, &alerting.DashAlertExtractorService{}, cfg, dashboardStore, &alerting.DashAlertExtractorService{},
features, folderPermissions, dashboardPermissions, ac, features, folderPermissions, dashboardPermissions, ac,
) )
folderService := dashboardservice.ProvideFolderService( folderService := folderimpl.ProvideService(ac, busmock.New(), cfg, dashboardService, dashboardStore, features, folderPermissions, nil)
cfg, dashboardService, dashboardStore, nil,
features, folderPermissions, ac, busmock.New(),
)
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService) elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService)
service := LibraryPanelService{ service := LibraryPanelService{

View File

@ -20,6 +20,7 @@ import (
"github.com/grafana/grafana/pkg/services/datasourceproxy" "github.com/grafana/grafana/pkg/services/datasourceproxy"
"github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/ngalert/api" "github.com/grafana/grafana/pkg/services/ngalert/api"
"github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/eval"
"github.com/grafana/grafana/pkg/services/ngalert/image" "github.com/grafana/grafana/pkg/services/ngalert/image"
@ -54,7 +55,7 @@ func ProvideService(
secretsService secrets.Service, secretsService secrets.Service,
notificationService notifications.Service, notificationService notifications.Service,
m *metrics.NGAlert, m *metrics.NGAlert,
folderService dashboards.FolderService, folderService folder.Service,
ac accesscontrol.AccessControl, ac accesscontrol.AccessControl,
dashboardService dashboards.DashboardService, dashboardService dashboards.DashboardService,
renderService rendering.Service, renderService rendering.Service,
@ -117,7 +118,7 @@ type AlertNG struct {
imageService image.ImageService imageService image.ImageService
schedule schedule.ScheduleService schedule schedule.ScheduleService
stateManager *state.Manager stateManager *state.Manager
folderService dashboards.FolderService folderService folder.Service
dashboardService dashboards.DashboardService dashboardService dashboards.DashboardService
// Alerting notification services // Alerting notification services

View File

@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"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/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -34,13 +35,13 @@ type DBstore struct {
FeatureToggles featuremgmt.FeatureToggles FeatureToggles featuremgmt.FeatureToggles
SQLStore *sqlstore.SQLStore SQLStore *sqlstore.SQLStore
Logger log.Logger Logger log.Logger
FolderService dashboards.FolderService FolderService folder.Service
AccessControl accesscontrol.AccessControl AccessControl accesscontrol.AccessControl
DashboardService dashboards.DashboardService DashboardService dashboards.DashboardService
} }
func ProvideDBStore( func ProvideDBStore(
cfg *setting.Cfg, featureToggles featuremgmt.FeatureToggles, sqlstore *sqlstore.SQLStore, folderService dashboards.FolderService, cfg *setting.Cfg, featureToggles featuremgmt.FeatureToggles, sqlstore *sqlstore.SQLStore, folderService folder.Service,
access accesscontrol.AccessControl, dashboards dashboards.DashboardService) *DBstore { access accesscontrol.AccessControl, dashboards dashboards.DashboardService) *DBstore {
return &DBstore{ return &DBstore{
Cfg: cfg.UnifiedAlerting, Cfg: cfg.UnifiedAlerting,

View File

@ -20,6 +20,7 @@ import (
databasestore "github.com/grafana/grafana/pkg/services/dashboards/database" databasestore "github.com/grafana/grafana/pkg/services/dashboards/database"
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/ngalert" "github.com/grafana/grafana/pkg/services/ngalert"
"github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/metrics"
@ -88,10 +89,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
) )
bus := busmock.New() bus := busmock.New()
folderService := dashboardservice.ProvideFolderService( folderService := folderimpl.ProvideService(ac, bus, cfg, dashboardService, dashboardStore, features, folderPermissions, nil)
cfg, dashboardService, dashboardStore, nil,
features, folderPermissions, ac, bus,
)
ng, err := ngalert.ProvideService( ng, err := ngalert.ProvideService(
cfg, &FakeFeatures{}, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, nil, cfg, &FakeFeatures{}, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, nil,

View File

@ -15,6 +15,7 @@ import (
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards" dashboardservice "github.com/grafana/grafana/pkg/services/dashboards"
datasourceservice "github.com/grafana/grafana/pkg/services/datasources" datasourceservice "github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/encryption" "github.com/grafana/grafana/pkg/services/encryption"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/ngalert/provisioning" "github.com/grafana/grafana/pkg/services/ngalert/provisioning"
"github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/notifications" "github.com/grafana/grafana/pkg/services/notifications"
@ -44,7 +45,7 @@ func ProvideService(
datasourceService datasourceservice.DataSourceService, datasourceService datasourceservice.DataSourceService,
correlationsService correlations.Service, correlationsService correlations.Service,
dashboardService dashboardservice.DashboardService, dashboardService dashboardservice.DashboardService,
folderService dashboardservice.FolderService, folderService folder.Service,
alertingService *alerting.AlertNotificationService, alertingService *alerting.AlertNotificationService,
pluginSettings pluginsettings.Service, pluginSettings pluginsettings.Service,
searchService searchV2.SearchService, searchService searchV2.SearchService,