From 080ea88af7b6036dd1fea0201a75e7b6fdff8fb3 Mon Sep 17 00:00:00 2001 From: idafurjes <36131195+idafurjes@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:28:24 +0100 Subject: [PATCH] =?UTF-8?q?Nested=20Folders:=20Support=20getting=20of=20ne?= =?UTF-8?q?sted=20folder=20in=20folder=20service=20wh=E2=80=A6=20(#58597)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Nested Folders: Support getting of nested folder in folder service when feature flag is set * Fix lint * Fix some tests * Fix ngalert test * ngalert fix * Fix API tests * Fix some tests and lint * Fix lint 2 * Fix library elements and panels * Add access control to get folder * Cleanup and minor test change --- pkg/api/dashboard.go | 8 +- pkg/api/dashboard_test.go | 9 +- pkg/api/folder.go | 51 ++--------- pkg/api/folder_permission.go | 21 +++-- pkg/api/folder_test.go | 45 ---------- .../dashboardimport/service/service.go | 11 ++- pkg/services/dashboards/accesscontrol.go | 6 +- pkg/services/dashboards/accesscontrol_test.go | 31 ++++--- pkg/services/dashboards/dashboard.go | 7 +- pkg/services/dashboards/database/database.go | 13 +-- .../database/database_folder_test.go | 6 +- .../dashboards/service/dashboard_service.go | 2 +- .../service/dashboard_service_test.go | 3 +- pkg/services/dashboards/store_mock.go | 25 +++--- pkg/services/folder/folderimpl/folder.go | 73 +++++++++++----- pkg/services/folder/folderimpl/folder_test.go | 86 ++++++++----------- pkg/services/folder/foldertest/foldertest.go | 24 +++--- pkg/services/folder/service.go | 19 ++-- pkg/services/libraryelements/api.go | 21 ++--- pkg/services/libraryelements/guard.go | 9 +- .../libraryelements_create_test.go | 10 +-- .../libraryelements_delete_test.go | 2 +- .../libraryelements_get_all_test.go | 66 +++++++------- .../libraryelements_get_test.go | 6 +- .../libraryelements_patch_test.go | 10 +-- .../libraryelements/libraryelements_test.go | 53 ++++++------ .../librarypanels/librarypanels_test.go | 6 +- pkg/services/ngalert/api/api_prometheus.go | 3 +- pkg/services/ngalert/api/api_ruler.go | 14 +-- pkg/services/ngalert/api/api_ruler_test.go | 19 ++-- .../ngalert/api/api_ruler_validation.go | 8 +- .../ngalert/api/api_ruler_validation_test.go | 28 +++--- pkg/services/ngalert/api/persist.go | 6 +- pkg/services/ngalert/models/testing.go | 6 +- pkg/services/ngalert/ngalert_test.go | 12 +-- pkg/services/ngalert/store/alert_rule.go | 21 ++--- pkg/services/ngalert/store/alert_rule_test.go | 6 +- pkg/services/ngalert/tests/fakes/rules.go | 28 +++--- pkg/services/ngalert/tests/util.go | 18 ++-- 39 files changed, 372 insertions(+), 420 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 926af720944..13e9b05968e 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -24,6 +24,7 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" dashver "github.com/grafana/grafana/pkg/services/dashboardversion" "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/org" pref "github.com/grafana/grafana/pkg/services/preference" @@ -394,14 +395,17 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa cmd.OrgId = c.OrgID cmd.UserId = c.UserID if cmd.FolderUid != "" { - folder, err := hs.folderService.GetFolderByUID(ctx, c.SignedInUser, c.OrgID, cmd.FolderUid) + folder, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{ + OrgID: c.OrgID, + UID: &cmd.FolderUid, + }) if err != nil { if errors.Is(err, dashboards.ErrFolderNotFound) { return response.Error(400, "Folder not found", err) } return response.Error(500, "Error while checking folder ID", err) } - cmd.FolderId = folder.Id + cmd.FolderId = folder.ID } dash := cmd.GetDashboardModel() diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index bd4a7f08b3d..3723ccd81a4 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -32,6 +32,7 @@ import ( "github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest" "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/libraryelements" "github.com/grafana/grafana/pkg/services/live" @@ -642,8 +643,8 @@ func TestDashboardAPIEndpoint(t *testing.T) { dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")). Return(&models.Dashboard{Id: dashID, Uid: "uid", Title: "Dash", Slug: "dash", Version: 2}, nil) - mockFolder := &fakeFolderService{ - GetFolderByUIDResult: &models.Folder{Id: 1, Uid: "folderUID", Title: "Folder"}, + mockFolder := &foldertest.FakeService{ + ExpectedFolder: &folder.Folder{ID: 1, UID: "folderUID", Title: "Folder"}, } postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) { @@ -673,8 +674,8 @@ func TestDashboardAPIEndpoint(t *testing.T) { dashboardService := dashboards.NewFakeDashboardService(t) - mockFolder := &fakeFolderService{ - GetFolderByUIDError: errors.New("Error while searching Folder ID"), + mockFolder := &foldertest.FakeService{ + ExpectedError: errors.New("Error while searching Folder ID"), } postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolder, func(sc *scenarioContext) { diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 4d4197e2d43..280fbb5a804 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -67,13 +67,14 @@ func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response { // 404: notFoundError // 500: internalServerError func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response { - folder, err := hs.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, web.Params(c.Req)[":uid"]) + uid := web.Params(c.Req)[":uid"] + folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: &uid}) if err != nil { return apierrors.ToFolderErrorResponse(err) } - g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser) - return response.JSON(http.StatusOK, hs.toFolderDto(c, g, folder)) + g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser) + return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder)) } // swagger:route GET /folders/id/{folder_id} folders getFolderByID @@ -93,13 +94,13 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response { if err != nil { return response.Error(http.StatusBadRequest, "id is invalid", err) } - folder, err := hs.folderService.GetFolderByID(c.Req.Context(), c.SignedInUser, id, c.OrgID) + folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{ID: &id, OrgID: c.OrgID}) if err != nil { return apierrors.ToFolderErrorResponse(err) } - g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser) - return response.JSON(http.StatusOK, hs.toFolderDto(c, g, folder)) + g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser) + return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, folder)) } // swagger:route POST /folders folders createFolder @@ -182,8 +183,8 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response { if err != nil { return apierrors.ToFolderErrorResponse(err) } - g := guardian.New(c.Req.Context(), result.Id, c.OrgID, c.SignedInUser) - return response.JSON(http.StatusOK, hs.toFolderDto(c, g, result)) + g := guardian.New(c.Req.Context(), result.ID, c.OrgID, c.SignedInUser) + return response.JSON(http.StatusOK, hs.newToFolderDto(c, g, result)) } // swagger:route DELETE /folders/{folder_uid} folders deleteFolder @@ -218,40 +219,6 @@ func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { // return response.JSON(http.StatusOK, "") } -func (hs *HTTPServer) toFolderDto(c *models.ReqContext, g guardian.DashboardGuardian, folder *models.Folder) dtos.Folder { - canEdit, _ := g.CanEdit() - canSave, _ := g.CanSave() - canAdmin, _ := g.CanAdmin() - canDelete, _ := g.CanDelete() - - // Finding creator and last updater of the folder - updater, creator := anonString, anonString - if folder.CreatedBy > 0 { - creator = hs.getUserLogin(c.Req.Context(), folder.CreatedBy) - } - if folder.UpdatedBy > 0 { - updater = hs.getUserLogin(c.Req.Context(), folder.UpdatedBy) - } - - return dtos.Folder{ - Id: folder.Id, - Uid: folder.Uid, - Title: folder.Title, - Url: folder.Url, - HasACL: folder.HasACL, - CanSave: canSave, - CanEdit: canEdit, - CanAdmin: canAdmin, - CanDelete: canDelete, - CreatedBy: creator, - Created: folder.Created, - UpdatedBy: updater, - Updated: folder.Updated, - Version: folder.Version, - AccessControl: hs.getAccessControlMetadata(c, c.OrgID, dashboards.ScopeFoldersPrefix, folder.Uid), - } -} - func (hs *HTTPServer) newToFolderDto(c *models.ReqContext, g guardian.DashboardGuardian, folder *folder.Folder) dtos.Folder { canEdit, _ := g.CanEdit() canSave, _ := g.CanSave() diff --git a/pkg/api/folder_permission.go b/pkg/api/folder_permission.go index 4ae2c81439f..555d1a67c8e 100644 --- a/pkg/api/folder_permission.go +++ b/pkg/api/folder_permission.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/web" @@ -26,13 +27,14 @@ import ( // 404: notFoundError // 500: internalServerError func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Response { - folder, err := hs.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, web.Params(c.Req)[":uid"]) + uid := web.Params(c.Req)[":uid"] + folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: &uid}) if err != nil { return apierrors.ToFolderErrorResponse(err) } - g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser) + g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser) if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin { return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied) @@ -49,7 +51,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Res continue } - perm.FolderId = folder.Id + perm.FolderId = folder.ID perm.DashboardId = 0 perm.UserAvatarUrl = dtos.GetGravatarUrl(perm.UserEmail) @@ -87,12 +89,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res return response.Error(400, err.Error(), err) } - folder, err := hs.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, web.Params(c.Req)[":uid"]) + uid := web.Params(c.Req)[":uid"] + folder, err := hs.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: &uid}) if err != nil { return apierrors.ToFolderErrorResponse(err) } - g := guardian.New(c.Req.Context(), folder.Id, c.OrgID, c.SignedInUser) + g := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser) canAdmin, err := g.CanAdmin() if err != nil { return apierrors.ToFolderErrorResponse(err) @@ -106,7 +109,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res for _, item := range apiCmd.Items { items = append(items, &models.DashboardACL{ OrgID: c.OrgID, - DashboardID: folder.Id, + DashboardID: folder.ID, UserID: item.UserID, TeamID: item.TeamID, Role: item.Role, @@ -140,13 +143,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res if err != nil { return response.Error(500, "Error while checking dashboard permissions", err) } - if err := hs.updateDashboardAccessControl(c.Req.Context(), c.OrgID, folder.Uid, true, items, old); err != nil { + if err := hs.updateDashboardAccessControl(c.Req.Context(), c.OrgID, folder.UID, true, items, old); err != nil { return response.Error(500, "Failed to create permission", err) } return response.Success("Dashboard permissions updated") } - if err := hs.DashboardService.UpdateDashboardACL(c.Req.Context(), folder.Id, items); err != nil { + if err := hs.DashboardService.UpdateDashboardACL(c.Req.Context(), folder.ID, items); err != nil { if errors.Is(err, models.ErrDashboardACLInfoMissing) { err = models.ErrFolderACLInfoMissing } @@ -163,7 +166,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res return response.JSON(http.StatusOK, util.DynMap{ "message": "Folder permissions updated", - "id": folder.Id, + "id": folder.ID, "title": folder.Title, }) } diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index d2f15ba5bb9..71c4136bae2 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -1,7 +1,6 @@ package api import ( - "context" "encoding/json" "fmt" "net/http" @@ -294,47 +293,3 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st fn(sc) }) } - -type fakeFolderService struct { - folder.Service - - GetFoldersResult []*models.Folder - GetFoldersError error - GetFolderByUIDResult *models.Folder - GetFolderByUIDError error - GetFolderByIDResult *models.Folder - GetFolderByIDError error - CreateFolderResult *models.Folder - CreateFolderError error - UpdateFolderResult *models.Folder - UpdateFolderError error - DeleteFolderResult *folder.Folder - DeleteFolderError error - DeletedFolderUids []string -} - -func (s *fakeFolderService) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) { - return s.GetFoldersResult, s.GetFoldersError -} - -func (s *fakeFolderService) GetFolderByID(ctx context.Context, user *user.SignedInUser, id int64, orgID int64) (*models.Folder, error) { - return s.GetFolderByIDResult, s.GetFolderByIDError -} - -func (s *fakeFolderService) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) { - return s.GetFolderByUIDResult, s.GetFolderByUIDError -} - -func (s *fakeFolderService) CreateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, title, uid string) (*models.Folder, error) { - return s.CreateFolderResult, s.CreateFolderError -} - -func (s *fakeFolderService) UpdateFolder(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) error { - cmd.Result = s.UpdateFolderResult - return s.UpdateFolderError -} - -func (s *fakeFolderService) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderCommand) error { - s.DeletedFolderUids = append(s.DeletedFolderUids, cmd.UID) - return s.DeleteFolderError -} diff --git a/pkg/services/dashboardimport/service/service.go b/pkg/services/dashboardimport/service/service.go index f4da623cc75..0b345d21886 100644 --- a/pkg/services/dashboardimport/service/service.go +++ b/pkg/services/dashboardimport/service/service.go @@ -85,17 +85,20 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb // here we need to get FolderId from FolderUID if it present in the request, if both exist, FolderUID would overwrite FolderID if req.FolderUid != "" { - folder, err := s.folderService.GetFolderByUID(ctx, req.User, req.User.OrgID, req.FolderUid) + folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{ + OrgID: req.User.OrgID, + UID: &req.FolderUid, + }) if err != nil { return nil, err } - req.FolderId = folder.Id + req.FolderId = folder.ID } else { - folder, err := s.folderService.GetFolderByID(ctx, req.User, req.FolderId, req.User.OrgID) + folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{ID: &req.FolderId, OrgID: req.User.OrgID}) if err != nil { return nil, err } - req.FolderUid = folder.Uid + req.FolderUid = folder.UID } saveCmd := models.SaveDashboardCommand{ diff --git a/pkg/services/dashboards/accesscontrol.go b/pkg/services/dashboards/accesscontrol.go index 3537574f630..49df72b9b45 100644 --- a/pkg/services/dashboards/accesscontrol.go +++ b/pkg/services/dashboards/accesscontrol.go @@ -53,7 +53,7 @@ func NewFolderNameScopeResolver(db Store) (string, ac.ScopeAttributeResolver) { if err != nil { return nil, err } - return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil + return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, nil }) } @@ -79,7 +79,7 @@ func NewFolderIDScopeResolver(db Store) (string, ac.ScopeAttributeResolver) { return nil, err } - return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.Uid)}, nil + return []string{ScopeFoldersProvider.GetResourceScopeUID(folder.UID)}, nil }) } @@ -142,7 +142,7 @@ func resolveDashboardScope(ctx context.Context, db Store, orgID int64, dashboard if err != nil { return nil, err } - folderUID = folder.Uid + folderUID = folder.UID } return []string{ diff --git a/pkg/services/dashboards/accesscontrol_test.go b/pkg/services/dashboards/accesscontrol_test.go index d9e57e44657..d59f739aec0 100644 --- a/pkg/services/dashboards/accesscontrol_test.go +++ b/pkg/services/dashboards/accesscontrol_test.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana/pkg/models" ac "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/util" ) @@ -29,9 +30,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) { orgId := rand.Int63() title := "Very complex :title with: and /" + util.GenerateShortUID() - db := models.NewFolder(title) - db.Id = rand.Int63() - db.Uid = util.GenerateShortUID() + db := &folder.Folder{Title: title, ID: rand.Int63(), UID: util.GenerateShortUID()} dashboardStore.On("GetFolderByTitle", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() scope := "folders:name:" + title @@ -40,7 +39,7 @@ func TestNewFolderNameScopeResolver(t *testing.T) { require.NoError(t, err) require.Len(t, resolvedScopes, 1) - require.Equal(t, fmt.Sprintf("folders:uid:%v", db.Uid), resolvedScopes[0]) + require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0]) dashboardStore.AssertCalled(t, "GetFolderByTitle", mock.Anything, orgId, title) }) @@ -88,17 +87,17 @@ func TestNewFolderIDScopeResolver(t *testing.T) { orgId := rand.Int63() uid := util.GenerateShortUID() - db := &models.Folder{Id: rand.Int63(), Uid: uid} + db := &folder.Folder{ID: rand.Int63(), UID: uid} dashboardStore.On("GetFolderByID", mock.Anything, mock.Anything, mock.Anything).Return(db, nil).Once() - scope := "folders:id:" + strconv.FormatInt(db.Id, 10) + scope := "folders:id:" + strconv.FormatInt(db.ID, 10) resolvedScopes, err := resolver.Resolve(context.Background(), orgId, scope) require.NoError(t, err) require.Len(t, resolvedScopes, 1) - require.Equal(t, fmt.Sprintf("folders:uid:%v", db.Uid), resolvedScopes[0]) + require.Equal(t, fmt.Sprintf("folders:uid:%v", db.UID), resolvedScopes[0]) - dashboardStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.Id) + dashboardStore.AssertCalled(t, "GetFolderByID", mock.Anything, orgId, db.ID) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { dashboardStore := &FakeDashboardStore{} @@ -157,18 +156,18 @@ func TestNewDashboardIDScopeResolver(t *testing.T) { _, resolver := NewDashboardIDScopeResolver(store) orgID := rand.Int63() - folder := &models.Folder{Id: 2, Uid: "2"} - dashboard := &models.Dashboard{Id: 1, FolderId: folder.Id, Uid: "1"} + folder := &folder.Folder{ID: 2, UID: "2"} + dashboard := &models.Dashboard{Id: 1, FolderId: folder.ID, Uid: "1"} store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() - store.On("GetFolderByID", mock.Anything, orgID, folder.Id).Return(folder, nil).Once() + store.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() scope := ac.Scope("dashboards", "id", strconv.FormatInt(dashboard.Id, 10)) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) require.NoError(t, err) require.Len(t, resolvedScopes, 2) require.Equal(t, fmt.Sprintf("dashboards:uid:%s", dashboard.Uid), resolvedScopes[0]) - require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.Uid), resolvedScopes[1]) + require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1]) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { @@ -203,18 +202,18 @@ func TestNewDashboardUIDScopeResolver(t *testing.T) { _, resolver := NewDashboardUIDScopeResolver(store) orgID := rand.Int63() - folder := &models.Folder{Id: 2, Uid: "2"} - dashboard := &models.Dashboard{Id: 1, FolderId: folder.Id, Uid: "1"} + folder := &folder.Folder{ID: 2, UID: "2"} + dashboard := &models.Dashboard{Id: 1, FolderId: folder.ID, Uid: "1"} store.On("GetDashboard", mock.Anything, mock.Anything).Return(dashboard, nil).Once() - store.On("GetFolderByID", mock.Anything, orgID, folder.Id).Return(folder, nil).Once() + store.On("GetFolderByID", mock.Anything, orgID, folder.ID).Return(folder, nil).Once() scope := ac.Scope("dashboards", "uid", dashboard.Uid) resolvedScopes, err := resolver.Resolve(context.Background(), orgID, scope) require.NoError(t, err) require.Len(t, resolvedScopes, 2) require.Equal(t, fmt.Sprintf("dashboards:uid:%s", dashboard.Uid), resolvedScopes[0]) - require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.Uid), resolvedScopes[1]) + require.Equal(t, fmt.Sprintf("folders:uid:%s", folder.UID), resolvedScopes[1]) }) t.Run("resolver should fail if input scope is not expected", func(t *testing.T) { diff --git a/pkg/services/dashboards/dashboard.go b/pkg/services/dashboards/dashboard.go index 82f4eaa0850..c8f80d0b7a1 100644 --- a/pkg/services/dashboards/dashboard.go +++ b/pkg/services/dashboards/dashboard.go @@ -4,6 +4,7 @@ import ( "context" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" ) // DashboardService is a service for operating on dashboards. @@ -89,9 +90,9 @@ type Store interface { //go:generate mockery --name FolderStore --structname FakeFolderStore --inpackage --filename folder_store_mock.go type FolderStore interface { // GetFolderByTitle retrieves a folder by its title - GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) + GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) // GetFolderByUID retrieves a folder by its UID - GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) + GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) // GetFolderByID retrieves a folder by its ID - GetFolderByID(ctx context.Context, orgID int64, id int64) (*models.Folder, error) + GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) } diff --git a/pkg/services/dashboards/database/database.go b/pkg/services/dashboards/database/database.go index 045d2597239..640274db692 100644 --- a/pkg/services/dashboards/database/database.go +++ b/pkg/services/dashboards/database/database.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" dashver "github.com/grafana/grafana/pkg/services/dashboardversion" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/permissions" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore" @@ -74,7 +75,7 @@ func (d *DashboardStore) ValidateDashboardBeforeSave(ctx context.Context, dashbo return isParentFolderChanged, nil } -func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) { +func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) { if title == "" { return nil, dashboards.ErrFolderTitleEmpty } @@ -94,10 +95,10 @@ func (d *DashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, titl dashboard.SetUid(dashboard.Uid) return nil }) - return models.DashboardToFolder(&dashboard), err + return folder.FromDashboard(&dashboard), err } -func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*models.Folder, error) { +func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) { dashboard := models.Dashboard{OrgId: orgID, FolderId: 0, Id: id} err := d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error { has, err := sess.Table(&models.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)).Where("folder_id=0").Get(&dashboard) @@ -114,10 +115,10 @@ func (d *DashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int6 if err != nil { return nil, err } - return models.DashboardToFolder(&dashboard), nil + return folder.FromDashboard(&dashboard), nil } -func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) { +func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) { if uid == "" { return nil, dashboards.ErrDashboardIdentifierNotSet } @@ -138,7 +139,7 @@ func (d *DashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid st if err != nil { return nil, err } - return models.DashboardToFolder(&dashboard), nil + return folder.FromDashboard(&dashboard), nil } func (d *DashboardStore) GetProvisionedDataByDashboardID(ctx context.Context, dashboardID int64) (*models.DashboardProvisioning, error) { diff --git a/pkg/services/dashboards/database/database_folder_test.go b/pkg/services/dashboards/database/database_folder_test.go index 8f104a670ff..b79508f1cd4 100644 --- a/pkg/services/dashboards/database/database_folder_test.go +++ b/pkg/services/dashboards/database/database_folder_test.go @@ -481,7 +481,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { t.Run("GetFolderByTitle should find the folder", func(t *testing.T) { result, err := dashboardStore.GetFolderByTitle(context.Background(), orgId, title) require.NoError(t, err) - require.Equal(t, folder1.Id, result.Id) + require.Equal(t, folder1.Id, result.ID) }) }) @@ -494,7 +494,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { t.Run("should return folder by UID", func(t *testing.T) { d, err := dashboardStore.GetFolderByUID(context.Background(), orgId, folder.Uid) - require.Equal(t, folder.Id, d.Id) + require.Equal(t, folder.Id, d.ID) require.NoError(t, err) }) t.Run("should not find dashboard", func(t *testing.T) { @@ -518,7 +518,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { t.Run("should return folder by ID", func(t *testing.T) { d, err := dashboardStore.GetFolderByID(context.Background(), orgId, folder.Id) - require.Equal(t, folder.Id, d.Id) + require.Equal(t, folder.Id, d.ID) require.NoError(t, err) }) t.Run("should not find dashboard", func(t *testing.T) { diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 671dc054888..881f2bc09ab 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -600,5 +600,5 @@ func (dr DashboardServiceImpl) CountDashboardsInFolder(ctx context.Context, quer return 0, err } - return dr.dashboardStore.CountDashboardsInFolder(ctx, &dashboards.CountDashboardsInFolderRequest{FolderID: folder.Id, OrgID: u.OrgID}) + return dr.dashboardStore.CountDashboardsInFolder(ctx, &dashboards.CountDashboardsInFolderRequest{FolderID: folder.ID, OrgID: u.OrgID}) } diff --git a/pkg/services/dashboards/service/dashboard_service_test.go b/pkg/services/dashboards/service/dashboard_service_test.go index fac1f73b8fa..e2ae5c80bcd 100644 --- a/pkg/services/dashboards/service/dashboard_service_test.go +++ b/pkg/services/dashboards/service/dashboard_service_test.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" @@ -226,7 +227,7 @@ func TestDashboardService(t *testing.T) { }) t.Run("Count dashboards in folder", func(t *testing.T) { - fakeStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&models.Folder{}, nil) + fakeStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil) fakeStore.On("CountDashboardsInFolder", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil) // set up a ctx with signed in user diff --git a/pkg/services/dashboards/store_mock.go b/pkg/services/dashboards/store_mock.go index 5824d5332db..309152a23ed 100644 --- a/pkg/services/dashboards/store_mock.go +++ b/pkg/services/dashboards/store_mock.go @@ -5,6 +5,7 @@ package dashboards import ( context "context" + folder "github.com/grafana/grafana/pkg/services/folder" models "github.com/grafana/grafana/pkg/models" mock "github.com/stretchr/testify/mock" ) @@ -194,15 +195,15 @@ func (_m *FakeDashboardStore) GetDashboardsByPluginID(ctx context.Context, query } // GetFolderByID provides a mock function with given fields: ctx, orgID, id -func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*models.Folder, error) { +func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) { ret := _m.Called(ctx, orgID, id) - var r0 *models.Folder - if rf, ok := ret.Get(0).(func(context.Context, int64, int64) *models.Folder); ok { + var r0 *folder.Folder + if rf, ok := ret.Get(0).(func(context.Context, int64, int64) *folder.Folder); ok { r0 = rf(ctx, orgID, id) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.Folder) + r0 = ret.Get(0).(*folder.Folder) } } @@ -217,15 +218,15 @@ func (_m *FakeDashboardStore) GetFolderByID(ctx context.Context, orgID int64, id } // GetFolderByTitle provides a mock function with given fields: ctx, orgID, title -func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*models.Folder, error) { +func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, title string) (*folder.Folder, error) { ret := _m.Called(ctx, orgID, title) - var r0 *models.Folder - if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.Folder); ok { + var r0 *folder.Folder + if rf, ok := ret.Get(0).(func(context.Context, int64, string) *folder.Folder); ok { r0 = rf(ctx, orgID, title) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.Folder) + r0 = ret.Get(0).(*folder.Folder) } } @@ -240,15 +241,15 @@ func (_m *FakeDashboardStore) GetFolderByTitle(ctx context.Context, orgID int64, } // GetFolderByUID provides a mock function with given fields: ctx, orgID, uid -func (_m *FakeDashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*models.Folder, error) { +func (_m *FakeDashboardStore) GetFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) { ret := _m.Called(ctx, orgID, uid) - var r0 *models.Folder - if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.Folder); ok { + var r0 *folder.Folder + if rf, ok := ret.Get(0).(func(context.Context, int64, string) *folder.Folder); ok { r0 = rf(ctx, orgID, uid) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*models.Folder) + r0 = ret.Get(0).(*folder.Folder) } } diff --git a/pkg/services/folder/folderimpl/folder.go b/pkg/services/folder/folderimpl/folder.go index 0de6fcf031e..6f3d1818b36 100644 --- a/pkg/services/folder/folderimpl/folder.go +++ b/pkg/services/folder/folderimpl/folder.go @@ -34,6 +34,7 @@ type Service struct { searchService *search.SearchService features *featuremgmt.FeatureManager permissions accesscontrol.FolderPermissionsService + accessControl accesscontrol.AccessControl // bus is currently used to publish events that cause scheduler to update rules. bus bus.Bus @@ -62,10 +63,41 @@ func ProvideService( searchService: searchService, features: features, permissions: folderPermissionsService, + accessControl: ac, bus: bus, } } +func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.Folder, error) { + user, err := appcontext.User(ctx) + if err != nil { + return nil, err + } + + if s.cfg.IsFeatureToggleEnabled(featuremgmt.FlagNestedFolders) { + if ok, err := s.accessControl.Evaluate(ctx, user, accesscontrol.EvalPermission( + dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(*cmd.UID), + )); !ok { + if err != nil { + return nil, toFolderError(err) + } + return nil, dashboards.ErrFolderAccessDenied + } + return s.store.Get(ctx, *cmd) + } + + switch { + case cmd.UID != nil: + return s.getFolderByUID(ctx, user, cmd.OrgID, *cmd.UID) + case cmd.ID != nil: + return s.getFolderByID(ctx, user, *cmd.ID, cmd.OrgID) + case cmd.Title != nil: + return s.getFolderByTitle(ctx, user, cmd.OrgID, *cmd.Title) + default: + return nil, folder.ErrBadRequest.Errorf("either on of UID, ID, Title fields must be present") + } +} + func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID int64, limit int64, page int64) ([]*models.Folder, error) { searchQuery := search.Query{ SignedInUser: user, @@ -95,9 +127,9 @@ func (s *Service) GetFolders(ctx context.Context, user *user.SignedInUser, orgID return folders, nil } -func (s *Service) 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) (*folder.Folder, error) { if id == 0 { - return &models.Folder{Id: id, Title: "General"}, nil + return &folder.Folder{ID: id, Title: "General"}, nil } dashFolder, err := s.dashboardStore.GetFolderByID(ctx, orgID, id) @@ -105,7 +137,7 @@ func (s *Service) GetFolderByID(ctx context.Context, user *user.SignedInUser, id return nil, err } - g := guardian.New(ctx, dashFolder.Id, orgID, user) + g := guardian.New(ctx, dashFolder.ID, orgID, user) if canView, err := g.CanView(); err != nil || !canView { if err != nil { return nil, toFolderError(err) @@ -116,13 +148,13 @@ func (s *Service) GetFolderByID(ctx context.Context, user *user.SignedInUser, id return dashFolder, nil } -func (s *Service) 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) (*folder.Folder, error) { dashFolder, err := s.dashboardStore.GetFolderByUID(ctx, orgID, uid) if err != nil { return nil, err } - g := guardian.New(ctx, dashFolder.Id, orgID, user) + g := guardian.New(ctx, dashFolder.ID, orgID, user) if canView, err := g.CanView(); err != nil || !canView { if err != nil { return nil, toFolderError(err) @@ -133,13 +165,13 @@ func (s *Service) GetFolderByUID(ctx context.Context, user *user.SignedInUser, o return dashFolder, nil } -func (s *Service) 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) (*folder.Folder, error) { dashFolder, err := s.dashboardStore.GetFolderByTitle(ctx, orgID, title) if err != nil { return nil, err } - g := guardian.New(ctx, dashFolder.Id, orgID, user) + g := guardian.New(ctx, dashFolder.ID, orgID, user) if canView, err := g.CanView(); err != nil || !canView { if err != nil { return nil, toFolderError(err) @@ -189,7 +221,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) ( return nil, toFolderError(err) } - var createdFolder *models.Folder + var createdFolder *folder.Folder createdFolder, err = s.dashboardStore.GetFolderByID(ctx, cmd.OrgID, dash.Id) if err != nil { return nil, err @@ -209,9 +241,9 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) ( {BuiltinRole: string(org.RoleViewer), Permission: models.PERMISSION_VIEW.String()}, }...) - _, permissionErr = s.permissions.SetPermissions(ctx, cmd.OrgID, createdFolder.Uid, permissions...) + _, permissionErr = s.permissions.SetPermissions(ctx, cmd.OrgID, createdFolder.UID, permissions...) } else if s.cfg.EditorsCanAdmin && user.IsRealUser() && !user.IsAnonymous { - permissionErr = s.MakeUserAdmin(ctx, cmd.OrgID, userID, createdFolder.Id, true) + permissionErr = s.MakeUserAdmin(ctx, cmd.OrgID, userID, createdFolder.ID, true) } if permissionErr != nil { @@ -242,7 +274,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) ( // We'll log the error and also roll back the previously-created // (legacy) folder. s.log.Error("error saving folder to nested folder store", err) - err = s.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: createdFolder.Uid, OrgID: cmd.OrgID, ForceDeleteRules: true}) + err = s.DeleteFolder(ctx, &folder.DeleteFolderCommand{UID: createdFolder.UID, OrgID: cmd.OrgID, ForceDeleteRules: true}) if err != nil { s.log.Error("error deleting folder after failed save to nested folder store", err) } @@ -254,7 +286,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) ( return folder.FromDashboard(dash), nil } -func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) { +func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*folder.Folder, error) { foldr, err := s.legacyUpdate(ctx, user, orgID, existingUid, cmd) if err != nil { return nil, err @@ -276,7 +308,7 @@ func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int if err != nil { return nil, err } - _, err = s.store.Update(ctx, folder.UpdateFolderCommand{ + foldr, err := s.store.Update(ctx, folder.UpdateFolderCommand{ Folder: getFolder, NewUID: &cmd.Uid, NewTitle: &cmd.Title, @@ -285,11 +317,12 @@ func (s *Service) Update(ctx context.Context, user *user.SignedInUser, orgID int if err != nil { return nil, err } + return foldr, nil } return foldr, nil } -func (s *Service) legacyUpdate(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) { +func (s *Service) legacyUpdate(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*folder.Folder, error) { query := models.GetDashboardQuery{OrgId: orgID, Uid: existingUid} _, err := s.dashboardStore.GetDashboard(ctx, &query) if err != nil { @@ -322,7 +355,7 @@ func (s *Service) legacyUpdate(ctx context.Context, user *user.SignedInUser, org return nil, toFolderError(err) } - var foldr *models.Folder + var foldr *folder.Folder foldr, err = s.dashboardStore.GetFolderByID(ctx, orgID, dash.Id) if err != nil { return nil, err @@ -359,7 +392,7 @@ func (s *Service) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderComm return err } - guard := guardian.New(ctx, dashFolder.Id, cmd.OrgID, user) + guard := guardian.New(ctx, dashFolder.ID, cmd.OrgID, user) if canSave, err := guard.CanDelete(); err != nil || !canSave { if err != nil { return toFolderError(err) @@ -367,7 +400,7 @@ func (s *Service) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderComm return dashboards.ErrFolderAccessDenied } - deleteCmd := models.DeleteDashboardCommand{OrgId: cmd.OrgID, Id: dashFolder.Id, ForceDeleteFolderRules: cmd.ForceDeleteRules} + deleteCmd := models.DeleteDashboardCommand{OrgId: cmd.OrgID, Id: dashFolder.ID, ForceDeleteFolderRules: cmd.ForceDeleteRules} if err := s.dashboardStore.DeleteDashboard(ctx, &deleteCmd); err != nil { return toFolderError(err) @@ -416,12 +449,6 @@ func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) e return nil } -func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.Folder, error) { - // check the flag, if old - do whatever did before - // for new only the store - return s.store.Get(ctx, *cmd) -} - func (s *Service) GetParents(ctx context.Context, cmd *folder.GetParentsQuery) ([]*folder.Folder, error) { // check the flag, if old - do whatever did before // for new only the store diff --git a/pkg/services/folder/folderimpl/folder_test.go b/pkg/services/folder/folderimpl/folder_test.go index 5b8a142ea90..19ca8e8ddba 100644 --- a/pkg/services/folder/folderimpl/folder_test.go +++ b/pkg/services/folder/folderimpl/folder_test.go @@ -15,6 +15,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/accesscontrol/actest" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/dashboards" dashboardsvc "github.com/grafana/grafana/pkg/services/dashboards/service" @@ -79,26 +80,26 @@ func TestIntegrationFolderService(t *testing.T) { folderId := rand.Int63() folderUID := util.GenerateShortUID() - f := models.NewFolder("Folder") - f.Id = folderId - f.Uid = folderUID + f := folder.NewFolder("Folder", "") + f.ID = folderId + f.UID = folderUID dashStore.On("GetFolderByID", mock.Anything, orgID, folderId).Return(f, nil) dashStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(f, nil) t.Run("When get folder by id should return access denied error", func(t *testing.T) { - _, err := service.GetFolderByID(context.Background(), usr, folderId, orgID) + _, err := service.getFolderByID(context.Background(), usr, folderId, orgID) require.Equal(t, err, dashboards.ErrFolderAccessDenied) }) t.Run("When get folder by id, with id = 0 should return default folder", func(t *testing.T) { - folder, err := service.GetFolderByID(context.Background(), usr, 0, orgID) + foldr, err := service.getFolderByID(context.Background(), usr, 0, orgID) require.NoError(t, err) - require.Equal(t, folder, &models.Folder{Id: 0, Title: "General"}) + require.Equal(t, foldr, &folder.Folder{ID: 0, Title: "General"}) }) t.Run("When get folder by uid should return access denied error", func(t *testing.T) { - _, err := service.GetFolderByUID(context.Background(), usr, orgID, folderUID) + _, err := service.getFolderByUID(context.Background(), usr, orgID, folderUID) require.Equal(t, err, dashboards.ErrFolderAccessDenied) }) @@ -157,7 +158,7 @@ func TestIntegrationFolderService(t *testing.T) { t.Run("When creating folder should not return access denied error", func(t *testing.T) { dash := models.NewDashboardFolder("Test-Folder") dash.Id = rand.Int63() - f := models.DashboardToFolder(dash) + f := folder.FromDashboard(dash) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dash, nil).Once() @@ -170,7 +171,7 @@ func TestIntegrationFolderService(t *testing.T) { UID: "someuid", }) require.NoError(t, err) - require.Equal(t, f, actualFolder.ToLegacyModel()) + require.Equal(t, f, actualFolder) }) t.Run("When creating folder should return error if uid is general", func(t *testing.T) { @@ -190,7 +191,7 @@ func TestIntegrationFolderService(t *testing.T) { dashboardFolder := models.NewDashboardFolder("Folder") dashboardFolder.Id = rand.Int63() dashboardFolder.Uid = util.GenerateShortUID() - f := models.DashboardToFolder(dashboardFolder) + f := folder.FromDashboard(dashboardFolder) dashStore.On("ValidateDashboardBeforeSave", mock.Anything, mock.AnythingOfType("*models.Dashboard"), mock.AnythingOfType("bool")).Return(true, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(dashboardFolder, nil) @@ -207,10 +208,10 @@ func TestIntegrationFolderService(t *testing.T) { }) t.Run("When deleting folder by uid should not return access denied error", func(t *testing.T) { - f := models.NewFolder(util.GenerateShortUID()) - f.Id = rand.Int63() - f.Uid = util.GenerateShortUID() - dashStore.On("GetFolderByUID", mock.Anything, orgID, f.Uid).Return(f, nil) + f := folder.NewFolder(util.GenerateShortUID(), "") + f.ID = rand.Int63() + f.UID = util.GenerateShortUID() + dashStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil) var actualCmd *models.DeleteDashboardCommand dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { @@ -221,13 +222,13 @@ func TestIntegrationFolderService(t *testing.T) { ctx := context.Background() ctx = appcontext.WithUser(ctx, usr) err := service.DeleteFolder(ctx, &folder.DeleteFolderCommand{ - UID: f.Uid, + UID: f.UID, OrgID: orgID, ForceDeleteRules: expectedForceDeleteRules, }) require.NoError(t, err) require.NotNil(t, actualCmd) - require.Equal(t, f.Id, actualCmd.Id) + require.Equal(t, f.ID, actualCmd.Id) require.Equal(t, orgID, actualCmd.OrgId) require.Equal(t, expectedForceDeleteRules, actualCmd.ForceDeleteFolderRules) }) @@ -242,33 +243,33 @@ func TestIntegrationFolderService(t *testing.T) { guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true}) t.Run("When get folder by id should return folder", func(t *testing.T) { - expected := models.NewFolder(util.GenerateShortUID()) - expected.Id = rand.Int63() + expected := folder.NewFolder(util.GenerateShortUID(), "") + expected.ID = rand.Int63() - dashStore.On("GetFolderByID", mock.Anything, orgID, expected.Id).Return(expected, nil) + dashStore.On("GetFolderByID", mock.Anything, orgID, expected.ID).Return(expected, nil) - actual, err := service.GetFolderByID(context.Background(), usr, expected.Id, orgID) + actual, err := service.getFolderByID(context.Background(), usr, expected.ID, orgID) require.Equal(t, expected, actual) require.NoError(t, err) }) t.Run("When get folder by uid should return folder", func(t *testing.T) { - expected := models.NewFolder(util.GenerateShortUID()) - expected.Uid = util.GenerateShortUID() + expected := folder.NewFolder(util.GenerateShortUID(), "") + expected.UID = util.GenerateShortUID() - dashStore.On("GetFolderByUID", mock.Anything, orgID, expected.Uid).Return(expected, nil) + dashStore.On("GetFolderByUID", mock.Anything, orgID, expected.UID).Return(expected, nil) - actual, err := service.GetFolderByUID(context.Background(), usr, orgID, expected.Uid) + actual, err := service.getFolderByUID(context.Background(), usr, orgID, expected.UID) require.Equal(t, expected, actual) require.NoError(t, err) }) t.Run("When get folder by title should return folder", func(t *testing.T) { - expected := models.NewFolder("TEST-" + util.GenerateShortUID()) + expected := folder.NewFolder("TEST-"+util.GenerateShortUID(), "") dashStore.On("GetFolderByTitle", mock.Anything, orgID, expected.Title).Return(expected, nil) - actual, err := service.GetFolderByTitle(context.Background(), usr, orgID, expected.Title) + actual, err := service.getFolderByTitle(context.Background(), usr, orgID, expected.Title) require.Equal(t, expected, actual) require.NoError(t, err) }) @@ -311,7 +312,7 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) { mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil) dashStore := dashboards.FakeDashboardStore{} dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil) - dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil) + dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil) cfg := setting.NewCfg() cfg.RBACEnabled = false nestedFoldersEnabled := true @@ -338,18 +339,6 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) { require.NotNil(t, res.UID) }) - t.Run("delete folder", func(t *testing.T) { - folderStore.ExpectedFolder = &folder.Folder{} - err := folderService.Delete(context.Background(), &folder.DeleteFolderCommand{}) - require.NoError(t, err) - }) - - t.Run("get folder", func(t *testing.T) { - folderStore.ExpectedFolder = &folder.Folder{} - _, err := folderService.Get(context.Background(), &folder.GetFolderQuery{}) - require.NoError(t, err) - }) - t.Run("get parents folder", func(t *testing.T) { folderStore.ExpectedFolder = &folder.Folder{} _, err := folderService.GetParents(context.Background(), &folder.GetParentsQuery{}) @@ -378,12 +367,6 @@ func TestNestedFolderServiceFeatureToggle(t *testing.T) { require.NoError(t, err) require.Equal(t, 4, len(res)) }) - - t.Run("move folder", func(t *testing.T) { - folderStore.ExpectedFolder = &folder.Folder{} - _, err := folderService.Move(context.Background(), &folder.MoveFolderCommand{}) - require.NoError(t, err) - }) } func TestNestedFolderService(t *testing.T) { @@ -412,7 +395,7 @@ func TestNestedFolderService(t *testing.T) { mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil) - dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil) + dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil) ctx = appcontext.WithUser(ctx, usr) _, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{ @@ -430,7 +413,7 @@ func TestNestedFolderService(t *testing.T) { dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { actualCmd = args.Get(1).(*models.DeleteDashboardCommand) }).Return(nil).Once() - dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&models.Folder{}, nil) + dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil) g := guardian.New guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true}) @@ -463,6 +446,9 @@ func TestNestedFolderService(t *testing.T) { dashboardStore: dashStore, store: store, features: features, + accessControl: actest.FakeAccessControl{ + ExpectedEvaluate: true, + }, } t.Run("create, no error", func(t *testing.T) { @@ -471,7 +457,7 @@ func TestNestedFolderService(t *testing.T) { mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil) - dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil) + dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil) ctx = appcontext.WithUser(ctx, usr) _, err := foldersvc.Create(ctx, &folder.CreateFolderCommand{ OrgID: orgID, @@ -493,8 +479,8 @@ func TestNestedFolderService(t *testing.T) { mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool"), mock.AnythingOfType("bool")).Return(&models.SaveDashboardCommand{}, nil) dashStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("models.SaveDashboardCommand")).Return(&models.Dashboard{}, nil) - dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&models.Folder{}, nil) - dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&models.Folder{}, nil) + dashStore.On("GetFolderByID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(&folder.Folder{}, nil) + dashStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(&folder.Folder{}, nil) // return an error from the folder store store.ExpectedError = errors.New("FAILED") diff --git a/pkg/services/folder/foldertest/foldertest.go b/pkg/services/folder/foldertest/foldertest.go index 20c81534d78..ff62be316eb 100644 --- a/pkg/services/folder/foldertest/foldertest.go +++ b/pkg/services/folder/foldertest/foldertest.go @@ -14,29 +14,25 @@ type FakeService struct { ExpectedError error } +func NewFakeService() *FakeService { + return &FakeService{} +} + var _ folder.Service = (*FakeService)(nil) 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.ToLegacyModel(), s.ExpectedError -} -func (s *FakeService) GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) { - if s.ExpectedFolder == nil { - return nil, s.ExpectedError - } - return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError -} -func (s *FakeService) GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) { - return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError -} + func (s *FakeService) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) { return s.ExpectedFolder, s.ExpectedError } -func (s *FakeService) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) { +func (s *FakeService) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.Folder, error) { + return s.ExpectedFolder, s.ExpectedError +} +func (s *FakeService) Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*folder.Folder, error) { cmd.Result = s.ExpectedFolder.ToLegacyModel() - return s.ExpectedFolder.ToLegacyModel(), s.ExpectedError + return s.ExpectedFolder, s.ExpectedError } func (s *FakeService) DeleteFolder(ctx context.Context, cmd *folder.DeleteFolderCommand) error { return s.ExpectedError diff --git a/pkg/services/folder/service.go b/pkg/services/folder/service.go index d91ba445066..05c20196ff5 100644 --- a/pkg/services/folder/service.go +++ b/pkg/services/folder/service.go @@ -9,13 +9,18 @@ import ( type Service interface { 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) - GetFolderByUID(ctx context.Context, user *user.SignedInUser, orgID int64, uid string) (*models.Folder, error) - GetFolderByTitle(ctx context.Context, user *user.SignedInUser, orgID int64, title string) (*models.Folder, error) + Create(ctx context.Context, cmd *CreateFolderCommand) (*Folder, error) + + // GetFolder takes a GetFolderCommand and returns a folder matching the + // request. One of ID, UID, or Title must be included. If multiple values + // are included in the request, Grafana will select one in order of + // specificity (ID, UID, Title). + Get(ctx context.Context, cmd *GetFolderQuery) (*Folder, error) + // Update is used to update a folder's UID, Title and Description. To change // a folder's parent folder, use Move. - Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*models.Folder, error) + Update(ctx context.Context, user *user.SignedInUser, orgID int64, existingUid string, cmd *models.UpdateFolderCommand) (*Folder, error) DeleteFolder(ctx context.Context, cmd *DeleteFolderCommand) error MakeUserAdmin(ctx context.Context, orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error // Move changes a folder's parent folder to the requested new parent. @@ -35,12 +40,6 @@ type NestedFolderService interface { // dashboards in the folder. Delete(ctx context.Context, cmd *DeleteFolderCommand) (*Folder, error) - // GetFolder takes a GetFolderCommand and returns a folder matching the - // request. One of ID, UID, or Title must be included. If multiple values - // are included in the request, Grafana will select one in order of - // specificity (ID, UID, Title). - Get(ctx context.Context, cmd *GetFolderQuery) (*Folder, error) - // GetParents returns an ordered list of parent folders for the given // folder, starting with the root node and ending with the requested child // node. diff --git a/pkg/services/libraryelements/api.go b/pkg/services/libraryelements/api.go index 3cb3ef4b5cc..d499390b593 100644 --- a/pkg/services/libraryelements/api.go +++ b/pkg/services/libraryelements/api.go @@ -9,6 +9,7 @@ import ( "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/web" ) @@ -47,11 +48,11 @@ func (l *LibraryElementService) createHandler(c *models.ReqContext) response.Res if *cmd.FolderUID == "" { cmd.FolderID = 0 } else { - folder, err := l.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, *cmd.FolderUID) + folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: cmd.FolderUID}) if err != nil || folder == nil { return response.Error(http.StatusBadRequest, "failed to get folder", err) } - cmd.FolderID = folder.Id + cmd.FolderID = folder.ID } } @@ -61,12 +62,12 @@ func (l *LibraryElementService) createHandler(c *models.ReqContext) response.Res } if element.FolderID != 0 { - folder, err := l.folderService.GetFolderByID(c.Req.Context(), c.SignedInUser, element.FolderID, c.OrgID) + folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, ID: &element.FolderID}) if err != nil { return response.Error(http.StatusInternalServerError, "failed to get folder", err) } - element.FolderUID = folder.Uid - element.Meta.FolderUID = folder.Uid + element.FolderUID = folder.UID + element.Meta.FolderUID = folder.UID element.Meta.FolderName = folder.Title } @@ -175,11 +176,11 @@ func (l *LibraryElementService) patchHandler(c *models.ReqContext) response.Resp if *cmd.FolderUID == "" { cmd.FolderID = 0 } else { - folder, err := l.folderService.GetFolderByUID(c.Req.Context(), c.SignedInUser, c.OrgID, *cmd.FolderUID) + folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, UID: cmd.FolderUID}) if err != nil || folder == nil { return response.Error(http.StatusBadRequest, "failed to get folder", err) } - cmd.FolderID = folder.Id + cmd.FolderID = folder.ID } } @@ -189,12 +190,12 @@ func (l *LibraryElementService) patchHandler(c *models.ReqContext) response.Resp } if element.FolderID != 0 { - folder, err := l.folderService.GetFolderByID(c.Req.Context(), c.SignedInUser, element.FolderID, c.OrgID) + folder, err := l.folderService.Get(c.Req.Context(), &folder.GetFolderQuery{OrgID: c.OrgID, ID: &element.FolderID}) if err != nil { return response.Error(http.StatusInternalServerError, "failed to get folder", err) } - element.FolderUID = folder.Uid - element.Meta.FolderUID = folder.Uid + element.FolderUID = folder.UID + element.Meta.FolderUID = folder.UID element.Meta.FolderName = folder.Title } diff --git a/pkg/services/libraryelements/guard.go b/pkg/services/libraryelements/guard.go index ed697a4bf33..958ded662d6 100644 --- a/pkg/services/libraryelements/guard.go +++ b/pkg/services/libraryelements/guard.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" @@ -39,12 +40,12 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte if isGeneralFolder(folderID) && user.HasRole(org.RoleViewer) { return dashboards.ErrFolderAccessDenied } - folder, err := l.folderService.GetFolderByID(ctx, user, folderID, user.OrgID) + folder, err := l.folderService.Get(ctx, &folder.GetFolderQuery{ID: &folderID, OrgID: user.OrgID}) if err != nil { return err } - g := guardian.New(ctx, folder.Id, user.OrgID, user) + g := guardian.New(ctx, folder.ID, user.OrgID, user) canEdit, err := g.CanEdit() if err != nil { @@ -62,12 +63,12 @@ func (l *LibraryElementService) requireViewPermissionsOnFolder(ctx context.Conte return nil } - folder, err := l.folderService.GetFolderByID(ctx, user, folderID, user.OrgID) + folder, err := l.folderService.Get(ctx, &folder.GetFolderQuery{ID: &folderID, OrgID: user.OrgID}) if err != nil { return err } - g := guardian.New(ctx, folder.Id, user.OrgID, user) + g := guardian.New(ctx, folder.ID, user.OrgID, user) canView, err := g.CanView() if err != nil { diff --git a/pkg/services/libraryelements/libraryelements_create_test.go b/pkg/services/libraryelements/libraryelements_create_test.go index afde9ceaf09..ecc54aca467 100644 --- a/pkg/services/libraryelements/libraryelements_create_test.go +++ b/pkg/services/libraryelements/libraryelements_create_test.go @@ -13,7 +13,7 @@ import ( func TestCreateLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to create a library panel that already exists, it should fail", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 400, resp.Status()) @@ -65,7 +65,7 @@ func TestCreateLibraryElement(t *testing.T) { testScenario(t, "When an admin tries to create a library panel that does not exists using an nonexistent UID, it should succeed", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Nonexistent UID") + command := getCreatePanelCommand(sc.folder.ID, "Nonexistent UID") command.UID = util.GenerateShortUID() sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) @@ -114,7 +114,7 @@ func TestCreateLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an existent UID, it should fail", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Existing UID") + command := getCreatePanelCommand(sc.folder.ID, "Existing UID") command.UID = sc.initialResult.Result.UID sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) @@ -123,7 +123,7 @@ func TestCreateLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an invalid UID, it should fail", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Invalid UID") + command := getCreatePanelCommand(sc.folder.ID, "Invalid UID") command.UID = "Testing an invalid UID" sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) @@ -132,7 +132,7 @@ func TestCreateLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to create a library panel that does not exists using an UID that is too long, it should fail", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Invalid UID") + command := getCreatePanelCommand(sc.folder.ID, "Invalid UID") command.UID = "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K" sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) diff --git a/pkg/services/libraryelements/libraryelements_delete_test.go b/pkg/services/libraryelements/libraryelements_delete_test.go index 5f3065ebf50..0bd640e646d 100644 --- a/pkg/services/libraryelements/libraryelements_delete_test.go +++ b/pkg/services/libraryelements/libraryelements_delete_test.go @@ -73,7 +73,7 @@ func TestDeleteLibraryElement(t *testing.T) { Title: "Testing deleteHandler ", Data: simplejson.NewFromAny(dashJSON), } - dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id) + dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID) err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id) require.NoError(t, err) diff --git a/pkg/services/libraryelements/libraryelements_get_all_test.go b/pkg/services/libraryelements/libraryelements_get_all_test.go index 9d0970a8d3a..bac68141c57 100644 --- a/pkg/services/libraryelements/libraryelements_get_all_test.go +++ b/pkg/services/libraryelements/libraryelements_get_all_test.go @@ -37,7 +37,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all panel elements and both panels and variables exist, it should only return panels", func(t *testing.T, sc scenarioContext) { - command := getCreateVariableCommand(sc.folder.Id, "query0") + command := getCreateVariableCommand(sc.folder.ID, "query0") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -77,7 +77,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -103,7 +103,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all variable elements and both panels and variables exist, it should only return panels", func(t *testing.T, sc scenarioContext) { - command := getCreateVariableCommand(sc.folder.Id, "query0") + command := getCreateVariableCommand(sc.folder.ID, "query0") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -142,7 +142,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -168,7 +168,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist, it should succeed", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -204,7 +204,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -239,7 +239,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[1].Meta.Created, Updated: result.Result.Elements[1].Meta.Updated, @@ -265,7 +265,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and sort desc is set, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -304,7 +304,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -339,7 +339,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[1].Meta.Created, Updated: result.Result.Elements[1].Meta.Updated, @@ -365,7 +365,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to existing types, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", models.PanelElement, []byte(` + command := getCreateCommandWithModel(sc.folder.ID, "Gauge - Library Panel", models.PanelElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "id": 1, @@ -378,7 +378,7 @@ func TestGetAllLibraryElements(t *testing.T) { resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) - command = getCreateCommandWithModel(sc.folder.Id, "BarGauge - Library Panel", models.PanelElement, []byte(` + command = getCreateCommandWithModel(sc.folder.ID, "BarGauge - Library Panel", models.PanelElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "id": 1, @@ -425,7 +425,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -460,7 +460,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[1].Meta.Created, Updated: result.Result.Elements[1].Meta.Updated, @@ -486,7 +486,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and typeFilter is set to a nonexistent type, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreateCommandWithModel(sc.folder.Id, "Gauge - Library Panel", models.PanelElement, []byte(` + command := getCreateCommandWithModel(sc.folder.ID, "Gauge - Library Panel", models.PanelElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "id": 1, @@ -621,7 +621,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to General folder, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -661,7 +661,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -696,7 +696,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[1].Meta.Created, Updated: result.Result.Elements[1].Meta.Updated, @@ -722,7 +722,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and excludeUID is set, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -761,7 +761,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -787,7 +787,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -826,7 +826,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -852,7 +852,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 2, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -892,7 +892,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -918,7 +918,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in the description, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreateCommandWithModel(sc.folder.Id, "Text - Library Panel2", models.PanelElement, []byte(` + command := getCreateCommandWithModel(sc.folder.ID, "Text - Library Panel2", models.PanelElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "id": 1, @@ -967,7 +967,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -993,7 +993,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and searchString exists in both name and description, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreateCommandWithModel(sc.folder.Id, "Some Other", models.PanelElement, []byte(` + command := getCreateCommandWithModel(sc.folder.ID, "Some Other", models.PanelElement, []byte(` { "datasource": "${DS_GDEV-TESTDATA}", "id": 1, @@ -1040,7 +1040,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -1075,7 +1075,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[1].Meta.Created, Updated: result.Result.Elements[1].Meta.Updated, @@ -1101,7 +1101,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 1 and searchString is panel2, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -1142,7 +1142,7 @@ func TestGetAllLibraryElements(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: result.Result.Elements[0].Meta.Created, Updated: result.Result.Elements[0].Meta.Updated, @@ -1168,7 +1168,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString is panel, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -1199,7 +1199,7 @@ func TestGetAllLibraryElements(t *testing.T) { scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString does not exist, it should succeed and the result should be correct", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel2") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) diff --git a/pkg/services/libraryelements/libraryelements_get_test.go b/pkg/services/libraryelements/libraryelements_get_test.go index 96e9d2a180e..a1ae8276351 100644 --- a/pkg/services/libraryelements/libraryelements_get_test.go +++ b/pkg/services/libraryelements/libraryelements_get_test.go @@ -49,7 +49,7 @@ func TestGetLibraryElement(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 0, Created: res.Result.Meta.Created, Updated: res.Result.Meta.Updated, @@ -119,7 +119,7 @@ func TestGetLibraryElement(t *testing.T) { Title: "Testing getHandler", Data: simplejson.NewFromAny(dashJSON), } - dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id) + dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID) err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id) require.NoError(t, err) @@ -144,7 +144,7 @@ func TestGetLibraryElement(t *testing.T) { Version: 1, Meta: LibraryElementDTOMeta{ FolderName: "ScenarioFolder", - FolderUID: sc.folder.Uid, + FolderUID: sc.folder.UID, ConnectedDashboards: 1, Created: res.Result.Meta.Created, Updated: res.Result.Meta.Updated, diff --git a/pkg/services/libraryelements/libraryelements_patch_test.go b/pkg/services/libraryelements/libraryelements_patch_test.go index 998a5c59ab8..51d1f06e822 100644 --- a/pkg/services/libraryelements/libraryelements_patch_test.go +++ b/pkg/services/libraryelements/libraryelements_patch_test.go @@ -187,7 +187,7 @@ func TestPatchLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to patch a library panel with an existing UID, it should fail", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Existing UID") + command := getCreatePanelCommand(sc.folder.ID, "Existing UID") command.UID = util.GenerateShortUID() sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) @@ -307,7 +307,7 @@ func TestPatchLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to patch a library panel with a name that already exists, it should fail", func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Another Panel") + command := getCreatePanelCommand(sc.folder.ID, "Another Panel") sc.ctx.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) var result = validateAndUnMarshalResponse(t, resp) @@ -343,7 +343,7 @@ func TestPatchLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail", func(t *testing.T, sc scenarioContext) { cmd := PatchLibraryElementCommand{ - FolderID: sc.folder.Id, + FolderID: sc.folder.ID, Version: 1, Kind: int64(models.PanelElement), } @@ -357,7 +357,7 @@ func TestPatchLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to patch a library panel with an old version number, it should fail", func(t *testing.T, sc scenarioContext) { cmd := PatchLibraryElementCommand{ - FolderID: sc.folder.Id, + FolderID: sc.folder.ID, Version: 1, Kind: int64(models.PanelElement), } @@ -373,7 +373,7 @@ func TestPatchLibraryElement(t *testing.T) { scenarioWithPanel(t, "When an admin tries to patch a library panel with an other kind, it should succeed but panel should not change", func(t *testing.T, sc scenarioContext) { cmd := PatchLibraryElementCommand{ - FolderID: sc.folder.Id, + FolderID: sc.folder.ID, Version: 1, Kind: int64(models.VariableElement), } diff --git a/pkg/services/libraryelements/libraryelements_test.go b/pkg/services/libraryelements/libraryelements_test.go index a497c0dfbfe..944dfeb650e 100644 --- a/pkg/services/libraryelements/libraryelements_test.go +++ b/pkg/services/libraryelements/libraryelements_test.go @@ -73,23 +73,23 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) { Title: "Testing DeleteLibraryElementsInFolder", Data: simplejson.NewFromAny(dashJSON), } - dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id) + dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID) err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id) require.NoError(t, err) - err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid) + err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID) require.EqualError(t, err, ErrFolderHasConnectedLibraryElements.Error()) }) scenarioWithPanel(t, "When an admin tries to delete a folder uid that doesn't exist, it should fail", func(t *testing.T, sc scenarioContext) { - err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid+"xxxx") + err := sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID+"xxxx") require.EqualError(t, err, dashboards.ErrFolderNotFound.Error()) }) scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too", func(t *testing.T, sc scenarioContext) { - command := getCreateVariableCommand(sc.folder.Id, "query0") + command := getCreateVariableCommand(sc.folder.ID, "query0") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -102,7 +102,7 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) { require.NotNil(t, result.Result) require.Equal(t, 2, len(result.Result.Elements)) - err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.Uid) + err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID) require.NoError(t, err) resp = sc.service.getAllHandler(sc.reqContext) require.Equal(t, 200, resp.Status()) @@ -146,7 +146,7 @@ func TestGetLibraryPanelConnections(t *testing.T) { Title: "Testing GetLibraryPanelConnections", Data: simplejson.NewFromAny(dashJSON), } - dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.Id) + dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID) err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.Id) require.NoError(t, err) @@ -256,7 +256,7 @@ type scenarioContext struct { service *LibraryElementService reqContext *models.ReqContext user user.SignedInUser - folder *models.Folder + folder *folder.Folder initialResult libraryElementResult sqlStore db.DB } @@ -387,7 +387,7 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena guardian.InitLegacyGuardian(store, &dashboards.FakeDashboardService{}, &teamtest.FakeService{}) testScenario(t, desc, func(t *testing.T, sc scenarioContext) { - command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel") + command := getCreatePanelCommand(sc.folder.ID, "Text - Library Panel") sc.reqContext.Req.Body = mockRequestBody(command) resp := sc.service.createHandler(sc.reqContext) sc.initialResult = validateAndUnMarshalResponse(t, resp) @@ -402,13 +402,26 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo t.Helper() t.Run(desc, func(t *testing.T) { - ctx := web.Context{Req: &http.Request{ + orgID := int64(1) + role := org.RoleAdmin + usr := user.SignedInUser{ + UserID: 1, + Name: "Signed In User", + Login: "signed_in_user", + Email: "signed.in.user@test.com", + OrgID: orgID, + OrgRole: role, + LastSeenAt: time.Now(), + } + req := &http.Request{ Header: http.Header{ "Content-Type": []string{"application/json"}, }, - }} - orgID := int64(1) - role := org.RoleAdmin + } + ctx := appcontext.WithUser(context.Background(), &usr) + req = req.WithContext(ctx) + webCtx := web.Context{Req: req} + sqlStore := db.InitTestDB(t) dashboardStore := database.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg)) features := featuremgmt.WithFeatures() @@ -428,16 +441,6 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo folderService: folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardService, dashboardStore, nil, features, folderPermissions, nil), } - usr := user.SignedInUser{ - UserID: 1, - Name: "Signed In User", - Login: "signed_in_user", - Email: "signed.in.user@test.com", - OrgID: orgID, - OrgRole: role, - LastSeenAt: time.Now(), - } - // deliberate difference between signed in user and user in db to make it crystal clear // what to expect in the tests // In the real world these are identical @@ -452,16 +455,16 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo sc := scenarioContext{ user: usr, - ctx: &ctx, + ctx: &webCtx, service: &service, sqlStore: sqlStore, reqContext: &models.ReqContext{ - Context: &ctx, + Context: &webCtx, SignedInUser: &usr, }, } - sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{}).ToLegacyModel() + sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{}) fn(t, sc) }) diff --git a/pkg/services/librarypanels/librarypanels_test.go b/pkg/services/librarypanels/librarypanels_test.go index 007a7ed33e5..7f7bf97bebd 100644 --- a/pkg/services/librarypanels/librarypanels_test.go +++ b/pkg/services/librarypanels/librarypanels_test.go @@ -850,12 +850,14 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo Login: userInDbName, } - _, err := sqlStore.CreateUser(context.Background(), cmd) + ctx := appcontext.WithUser(context.Background(), usr) + + _, err := sqlStore.CreateUser(ctx, cmd) require.NoError(t, err) sc := scenarioContext{ user: usr, - ctx: context.Background(), + ctx: ctx, service: &service, elementService: elementService, sqlStore: sqlStore, diff --git a/pkg/services/ngalert/api/api_prometheus.go b/pkg/services/ngalert/api/api_prometheus.go index d263a340eda..c894258bf20 100644 --- a/pkg/services/ngalert/api/api_prometheus.go +++ b/pkg/services/ngalert/api/api_prometheus.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/folder" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/eval" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" @@ -181,7 +182,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res return response.JSON(http.StatusOK, ruleResponse) } -func (srv PrometheusSrv) toRuleGroup(groupName string, folder *models.Folder, rules []*ngmodels.AlertRule, labelOptions []ngmodels.LabelOption) *apimodels.RuleGroup { +func (srv PrometheusSrv) toRuleGroup(groupName string, folder *folder.Folder, rules []*ngmodels.AlertRule, labelOptions []ngmodels.LabelOption) *apimodels.RuleGroup { newGroup := &apimodels.RuleGroup{ Name: groupName, File: folder.Title, // file is what Prometheus uses for provisioning, we replace it with namespace. diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index 9229bd40f78..6161bce3bb4 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -83,7 +83,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *models.ReqContext, namespaceTitle s unauthz, provisioned := false, false q := ngmodels.ListAlertRulesQuery{ OrgID: c.SignedInUser.OrgID, - NamespaceUIDs: []string{namespace.Uid}, + NamespaceUIDs: []string{namespace.UID}, RuleGroup: ruleGroup, } if err = srv.store.ListAlertRules(ctx, &q); err != nil { @@ -163,7 +163,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext, namespace q := ngmodels.ListAlertRulesQuery{ OrgID: c.SignedInUser.OrgID, - NamespaceUIDs: []string{namespace.Uid}, + NamespaceUIDs: []string{namespace.UID}, } if err := srv.store.ListAlertRules(c.Req.Context(), &q); err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to update rule group") @@ -189,7 +189,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext, namespace if !authorizeAccessToRuleGroup(rules, hasAccess) { continue } - result[namespaceTitle] = append(result[namespaceTitle], toGettableRuleGroupConfig(groupName, rules, namespace.Id, provenanceRecords)) + result[namespaceTitle] = append(result[namespaceTitle], toGettableRuleGroupConfig(groupName, rules, namespace.ID, provenanceRecords)) } return response.JSON(http.StatusAccepted, result) @@ -205,7 +205,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext, namespaceTitl q := ngmodels.ListAlertRulesQuery{ OrgID: c.SignedInUser.OrgID, - NamespaceUIDs: []string{namespace.Uid}, + NamespaceUIDs: []string{namespace.UID}, RuleGroup: ruleGroup, } if err := srv.store.ListAlertRules(c.Req.Context(), &q); err != nil { @@ -226,7 +226,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext, namespaceTitl } result := apimodels.RuleGroupConfigResponse{ - GettableRuleGroupConfig: toGettableRuleGroupConfig(ruleGroup, q.Result, namespace.Id, provenanceRecords), + GettableRuleGroupConfig: toGettableRuleGroupConfig(ruleGroup, q.Result, namespace.ID, provenanceRecords), } return response.JSON(http.StatusAccepted, result) } @@ -296,7 +296,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response continue } namespace := folder.Title - result[namespace] = append(result[namespace], toGettableRuleGroupConfig(groupKey.RuleGroup, rules, folder.Id, provenanceRecords)) + result[namespace] = append(result[namespace], toGettableRuleGroupConfig(groupKey.RuleGroup, rules, folder.ID, provenanceRecords)) } return response.JSON(http.StatusOK, result) } @@ -316,7 +316,7 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConf groupKey := ngmodels.AlertRuleGroupKey{ OrgID: c.SignedInUser.OrgID, - NamespaceUID: namespace.Uid, + NamespaceUID: namespace.UID, RuleGroup: ruleGroupConfig.Name, } diff --git a/pkg/services/ngalert/api/api_ruler_test.go b/pkg/services/ngalert/api/api_ruler_test.go index 4a7b2a4cc64..9f7d07103a2 100644 --- a/pkg/services/ngalert/api/api_ruler_test.go +++ b/pkg/services/ngalert/api/api_ruler_test.go @@ -18,6 +18,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" acMock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/folder" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/provisioning" @@ -382,7 +383,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) { ruleStore := fakes.NewRuleStore(t) ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder) groupKey := models.GenerateGroupKey(orgID) - groupKey.NamespaceUID = folder.Uid + groupKey.NamespaceUID = folder.UID expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex())) ruleStore.PutRule(context.Background(), expectedRules...) @@ -426,12 +427,12 @@ func TestRouteGetRulesConfig(t *testing.T) { ruleStore := fakes.NewRuleStore(t) folder1 := randFolder() folder2 := randFolder() - ruleStore.Folders[orgID] = []*models2.Folder{folder1, folder2} + ruleStore.Folders[orgID] = []*folder.Folder{folder1, folder2} group1Key := models.GenerateGroupKey(orgID) - group1Key.NamespaceUID = folder1.Uid + group1Key.NamespaceUID = folder1.UID group2Key := models.GenerateGroupKey(orgID) - group2Key.NamespaceUID = folder2.Uid + group2Key.NamespaceUID = folder2.UID group1 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group1Key))) group2 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group2Key))) @@ -464,7 +465,7 @@ func TestRouteGetRulesConfig(t *testing.T) { ruleStore := fakes.NewRuleStore(t) ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder) groupKey := models.GenerateGroupKey(orgID) - groupKey.NamespaceUID = folder.Uid + groupKey.NamespaceUID = folder.UID expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex())) ruleStore.PutRule(context.Background(), expectedRules...) @@ -509,7 +510,7 @@ func TestRouteGetRulesGroupConfig(t *testing.T) { ruleStore := fakes.NewRuleStore(t) ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder) groupKey := models.GenerateGroupKey(orgID) - groupKey.NamespaceUID = folder.Uid + groupKey.NamespaceUID = folder.UID expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(groupKey))) ruleStore.PutRule(context.Background(), expectedRules...) @@ -544,7 +545,7 @@ func TestRouteGetRulesGroupConfig(t *testing.T) { ruleStore := fakes.NewRuleStore(t) ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder) groupKey := models.GenerateGroupKey(orgID) - groupKey.NamespaceUID = folder.Uid + groupKey.NamespaceUID = folder.UID expectedRules := models.GenerateAlertRules(rand.Intn(5)+5, models.AlertRuleGen(withGroupKey(groupKey), models.WithUniqueGroupIndex())) ruleStore.PutRule(context.Background(), expectedRules...) @@ -699,9 +700,9 @@ func withGroup(groupName string) func(rule *models.AlertRule) { } } -func withNamespace(namespace *models2.Folder) func(rule *models.AlertRule) { +func withNamespace(namespace *folder.Folder) func(rule *models.AlertRule) { return func(rule *models.AlertRule) { - rule.NamespaceUID = namespace.Uid + rule.NamespaceUID = namespace.UID } } diff --git a/pkg/services/ngalert/api/api_ruler_validation.go b/pkg/services/ngalert/api/api_ruler_validation.go index 395beaa0291..903188c496b 100644 --- a/pkg/services/ngalert/api/api_ruler_validation.go +++ b/pkg/services/ngalert/api/api_ruler_validation.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" @@ -18,7 +18,7 @@ func validateRuleNode( groupName string, interval time.Duration, orgId int64, - namespace *models.Folder, + namespace *folder.Folder, conditionValidator func(ngmodels.Condition) error, cfg *setting.UnifiedAlertingSettings) (*ngmodels.AlertRule, error) { intervalSeconds, err := validateInterval(cfg, interval) @@ -93,7 +93,7 @@ func validateRuleNode( Data: ruleNode.GrafanaManagedAlert.Data, UID: ruleNode.GrafanaManagedAlert.UID, IntervalSeconds: intervalSeconds, - NamespaceUID: namespace.Uid, + NamespaceUID: namespace.UID, RuleGroup: groupName, NoDataState: noDataState, ExecErrState: errorState, @@ -152,7 +152,7 @@ func validateForInterval(ruleNode *apimodels.PostableExtendedRuleNode) (time.Dur func validateRuleGroup( ruleGroupConfig *apimodels.PostableRuleGroupConfig, orgId int64, - namespace *models.Folder, + namespace *folder.Folder, conditionValidator func(ngmodels.Condition) error, cfg *setting.UnifiedAlertingSettings) ([]*ngmodels.AlertRule, error) { if ruleGroupConfig.Name == "" { diff --git a/pkg/services/ngalert/api/api_ruler_validation_test.go b/pkg/services/ngalert/api/api_ruler_validation_test.go index bd4072a59c0..b5098d167f4 100644 --- a/pkg/services/ngalert/api/api_ruler_validation_test.go +++ b/pkg/services/ngalert/api/api_ruler_validation_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/exp/rand" - models2 "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/store" @@ -83,18 +83,18 @@ func validGroup(cfg *setting.UnifiedAlertingSettings, rules ...apimodels.Postabl } } -func randFolder() *models2.Folder { - return &models2.Folder{ - Id: rand.Int63(), - Uid: util.GenerateShortUID(), - Title: "TEST-FOLDER-" + util.GenerateShortUID(), - Url: "", - Version: 0, - Created: time.Time{}, - Updated: time.Time{}, - UpdatedBy: 0, - CreatedBy: 0, - HasACL: false, +func randFolder() *folder.Folder { + return &folder.Folder{ + ID: rand.Int63(), + UID: util.GenerateShortUID(), + Title: "TEST-FOLDER-" + util.GenerateShortUID(), + // URL: "", + // Version: 0, + Created: time.Time{}, + Updated: time.Time{}, + // UpdatedBy: 0, + // CreatedBy: 0, + // HasACL: false, } } @@ -235,7 +235,7 @@ func TestValidateRuleNode_NoUID(t *testing.T) { require.Equal(t, int64(interval.Seconds()), alert.IntervalSeconds) require.Equal(t, int64(0), alert.Version) require.Equal(t, api.GrafanaManagedAlert.UID, alert.UID) - require.Equal(t, folder.Uid, alert.NamespaceUID) + require.Equal(t, folder.UID, alert.NamespaceUID) require.Nil(t, alert.DashboardUID) require.Nil(t, alert.PanelID) require.Equal(t, name, alert.RuleGroup) diff --git a/pkg/services/ngalert/api/persist.go b/pkg/services/ngalert/api/persist.go index bb8f59c7412..6c3b03576c3 100644 --- a/pkg/services/ngalert/api/persist.go +++ b/pkg/services/ngalert/api/persist.go @@ -3,15 +3,15 @@ package api import ( "context" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/user" ) // RuleStore is the interface for persisting alert rules and instances type RuleStore interface { - GetUserVisibleNamespaces(context.Context, int64, *user.SignedInUser) (map[string]*models.Folder, error) - GetNamespaceByTitle(context.Context, string, int64, *user.SignedInUser, bool) (*models.Folder, error) + GetUserVisibleNamespaces(context.Context, int64, *user.SignedInUser) (map[string]*folder.Folder, error) + GetNamespaceByTitle(context.Context, string, int64, *user.SignedInUser, bool) (*folder.Folder, error) GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) error ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error diff --git a/pkg/services/ngalert/models/testing.go b/pkg/services/ngalert/models/testing.go index 782f76d9b58..f1c343a75bc 100644 --- a/pkg/services/ngalert/models/testing.go +++ b/pkg/services/ngalert/models/testing.go @@ -9,7 +9,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/expr" - models2 "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/util" ) @@ -140,9 +140,9 @@ func WithOrgID(orgId int64) AlertRuleMutator { } } -func WithNamespace(namespace *models2.Folder) AlertRuleMutator { +func WithNamespace(namespace *folder.Folder) AlertRuleMutator { return func(rule *AlertRule) { - rule.NamespaceUID = namespace.Uid + rule.NamespaceUID = namespace.UID } } diff --git a/pkg/services/ngalert/ngalert_test.go b/pkg/services/ngalert/ngalert_test.go index 7004198e1eb..2d70e25c5ce 100644 --- a/pkg/services/ngalert/ngalert_test.go +++ b/pkg/services/ngalert/ngalert_test.go @@ -13,7 +13,7 @@ import ( "github.com/grafana/grafana/pkg/events" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" - models2 "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/schedule" "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes" @@ -22,9 +22,9 @@ import ( func Test_subscribeToFolderChanges(t *testing.T) { orgID := rand.Int63() - folder := &models2.Folder{ - Id: 0, - Uid: util.GenerateShortUID(), + folder := &folder.Folder{ + ID: 0, + UID: util.GenerateShortUID(), Title: "Folder" + util.GenerateShortUID(), } rules := models.GenerateAlertRules(5, models.AlertRuleGen(models.WithOrgID(orgID), models.WithNamespace(folder))) @@ -42,8 +42,8 @@ func Test_subscribeToFolderChanges(t *testing.T) { err := bus.Publish(context.Background(), &events.FolderTitleUpdated{ Timestamp: time.Now(), Title: "Folder" + util.GenerateShortUID(), - ID: folder.Id, - UID: folder.Uid, + ID: folder.ID, + UID: folder.UID, OrgID: orgID, }) require.NoError(t, err) diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 592a50689b6..1cb125de37c 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/guardian" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore" @@ -298,8 +299,8 @@ func (st DBstore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespa } // GetUserVisibleNamespaces returns the folders that are visible to the user and have at least one alert in it -func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, user *user.SignedInUser) (map[string]*models.Folder, error) { - namespaceMap := make(map[string]*models.Folder) +func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, user *user.SignedInUser) (map[string]*folder.Folder, error) { + namespaceMap := make(map[string]*folder.Folder) searchQuery := models.FindPersistedDashboardsQuery{ OrgId: orgID, @@ -330,9 +331,9 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use if !hit.IsFolder { continue } - namespaceMap[hit.UID] = &models.Folder{ - Id: hit.ID, - Uid: hit.UID, + namespaceMap[hit.UID] = &folder.Folder{ + ID: hit.ID, + UID: hit.UID, Title: hit.Title, } } @@ -342,15 +343,15 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use } // GetNamespaceByTitle is a handler for retrieving a namespace by its title. Alerting rules follow a Grafana folder-like structure which we call namespaces. -func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, orgID int64, user *user.SignedInUser, withCanSave bool) (*models.Folder, error) { - folder, err := st.FolderService.GetFolderByTitle(ctx, user, orgID, namespace) +func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, orgID int64, user *user.SignedInUser, withCanSave bool) (*folder.Folder, error) { + folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, Title: &namespace}) if err != nil { return nil, err } // if access control is disabled, check that the user is allowed to save in the folder. if withCanSave && st.AccessControl.IsDisabled() { - g := guardian.New(ctx, folder.Id, orgID, user) + g := guardian.New(ctx, folder.ID, orgID, user) if canSave, err := g.CanSave(); err != nil || !canSave { if err != nil { st.Logger.Error("checking can save permission has failed", "userId", user.UserID, "username", user.Login, "namespace", namespace, "orgId", orgID, "error", err) @@ -363,8 +364,8 @@ func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, org } // GetNamespaceByUID is a handler for retrieving a namespace by its UID. Alerting rules follow a Grafana folder-like structure which we call namespaces. -func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*models.Folder, error) { - folder, err := st.FolderService.GetFolderByUID(ctx, user, orgID, uid) +func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*folder.Folder, error) { + folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, Title: &uid}) if err != nil { return nil, err } diff --git a/pkg/services/ngalert/store/alert_rule_test.go b/pkg/services/ngalert/store/alert_rule_test.go index a4d4be01b67..1471109ba4a 100644 --- a/pkg/services/ngalert/store/alert_rule_test.go +++ b/pkg/services/ngalert/store/alert_rule_test.go @@ -22,7 +22,7 @@ func TestIntegrationUpdateAlertRules(t *testing.T) { t.Skip("skipping integration test") } sqlStore := db.InitTestDB(t) - store := DBstore{ + store := &DBstore{ SQLStore: sqlStore, Cfg: setting.UnifiedAlertingSettings{ BaseInterval: time.Duration(rand.Int63n(100)) * time.Second, @@ -136,7 +136,7 @@ func TestIntegration_CountAlertRules(t *testing.T) { } sqlStore := db.InitTestDB(t) - store := DBstore{SQLStore: sqlStore} + store := &DBstore{SQLStore: sqlStore} rule := createRule(t, store) tests := map[string]struct { @@ -175,7 +175,7 @@ func TestIntegration_CountAlertRules(t *testing.T) { } } -func createRule(t *testing.T, store DBstore) *models.AlertRule { +func createRule(t *testing.T, store *DBstore) *models.AlertRule { rule := models.AlertRuleGen(withIntervalMatching(store.Cfg.BaseInterval))() err := store.SQLStore.WithDbSession(context.Background(), func(sess *db.Session) error { _, err := sess.Table(models.AlertRule{}).InsertOne(rule) diff --git a/pkg/services/ngalert/tests/fakes/rules.go b/pkg/services/ngalert/tests/fakes/rules.go index 3cb2f1b7210..3775f4a28d3 100644 --- a/pkg/services/ngalert/tests/fakes/rules.go +++ b/pkg/services/ngalert/tests/fakes/rules.go @@ -9,7 +9,7 @@ import ( "testing" "time" - models2 "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/util" @@ -23,7 +23,7 @@ type RuleStore struct { Rules map[int64][]*models.AlertRule Hook func(cmd interface{}) error // use Hook if you need to intercept some query and return an error RecordedOps []interface{} - Folders map[int64][]*models2.Folder + Folders map[int64][]*folder.Folder } type GenericRecordedQuery struct { @@ -38,7 +38,7 @@ func NewRuleStore(t *testing.T) *RuleStore { Hook: func(interface{}) error { return nil }, - Folders: map[int64][]*models2.Folder{}, + Folders: map[int64][]*folder.Folder{}, } } @@ -58,18 +58,18 @@ mainloop: rgs = append(rgs, r) f.Rules[r.OrgID] = rgs - var existing *models2.Folder + var existing *folder.Folder folders := f.Folders[r.OrgID] for _, folder := range folders { - if folder.Uid == r.NamespaceUID { + if folder.UID == r.NamespaceUID { existing = folder break } } if existing == nil { - folders = append(folders, &models2.Folder{ - Id: rand.Int63(), - Uid: r.NamespaceUID, + folders = append(folders, &folder.Folder{ + ID: rand.Int63(), + UID: r.NamespaceUID, Title: "TEST-FOLDER-" + util.GenerateShortUID(), }) f.Folders[r.OrgID] = folders @@ -227,11 +227,11 @@ func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQu return nil } -func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*models2.Folder, error) { +func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*folder.Folder, error) { f.mtx.Lock() defer f.mtx.Unlock() - namespacesMap := map[string]*models2.Folder{} + namespacesMap := map[string]*folder.Folder{} _, ok := f.Rules[orgID] if !ok { @@ -239,12 +239,12 @@ func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ * } for _, folder := range f.Folders[orgID] { - namespacesMap[folder.Uid] = folder + namespacesMap[folder.UID] = folder } return namespacesMap, nil } -func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser, _ bool) (*models2.Folder, error) { +func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser, _ bool) (*folder.Folder, error) { folders := f.Folders[orgID] for _, folder := range folders { if folder.Title == title { @@ -254,7 +254,7 @@ func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID i return nil, fmt.Errorf("not found") } -func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*models2.Folder, error) { +func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*folder.Folder, error) { f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{ Name: "GetNamespaceByUID", Params: []interface{}{orgID, uid}, @@ -262,7 +262,7 @@ func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64 folders := f.Folders[orgID] for _, folder := range folders { - if folder.Uid == uid { + if folder.UID == uid { return folder, nil } } diff --git a/pkg/services/ngalert/tests/util.go b/pkg/services/ngalert/tests/util.go index 921cd630703..287dad05e3b 100644 --- a/pkg/services/ngalert/tests/util.go +++ b/pkg/services/ngalert/tests/util.go @@ -18,7 +18,6 @@ import ( "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" - gfmodels "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/annotations/annotationstest" @@ -68,6 +67,7 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG, }) cfg := setting.NewCfg() + cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{ BaseInterval: setting.SchedulerBaseInterval, } @@ -127,12 +127,10 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s } ctx = appcontext.WithUser(ctx, user) - f, err := dbstore.FolderService.Create(ctx, &folder.CreateFolderCommand{OrgID: orgID, Title: "FOLDER-" + util.GenerateShortUID(), UID: folderUID}) - var folder *gfmodels.Folder - if err == nil { - folder = f.ToLegacyModel() - } else if errors.Is(err, dashboards.ErrFolderWithSameUIDExists) || errors.Is(err, dashboards.ErrFolderVersionMismatch) { - folder, err = dbstore.FolderService.GetFolderByUID(ctx, user, orgID, folderUID) + _, err := dbstore.FolderService.Create(ctx, &folder.CreateFolderCommand{OrgID: orgID, Title: "FOLDER-" + util.GenerateShortUID(), UID: folderUID}) + // var foldr *folder.Folder + if errors.Is(err, dashboards.ErrFolderWithSameUIDExists) || errors.Is(err, dashboards.ErrFolderVersionMismatch) { + _, err = dbstore.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, UID: &folderUID}) } require.NoError(t, err) @@ -160,7 +158,7 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s Labels: labels, Annotations: map[string]string{"testAnnoKey": "testAnnoValue"}, IntervalSeconds: intervalSeconds, - NamespaceUID: folder.Uid, + NamespaceUID: folderUID, RuleGroup: ruleGroup, NoDataState: models.NoData, ExecErrState: models.AlertingErrState, @@ -170,7 +168,7 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s q := models.ListAlertRulesQuery{ OrgID: orgID, - NamespaceUIDs: []string{folder.Uid}, + NamespaceUIDs: []string{folderUID}, RuleGroup: ruleGroup, } err = dbstore.ListAlertRules(ctx, &q) @@ -178,6 +176,6 @@ func CreateTestAlertRuleWithLabels(t testing.TB, ctx context.Context, dbstore *s require.NotEmpty(t, q.Result) rule := q.Result[0] - t.Logf("alert definition: %v with title: %q interval: %d folder: %s created", rule.GetKey(), rule.Title, rule.IntervalSeconds, folder.Uid) + t.Logf("alert definition: %v with title: %q interval: %d folder: %s created", rule.GetKey(), rule.Title, rule.IntervalSeconds, folderUID) return rule }