diff --git a/pkg/services/folder/folderimpl/dashboard_folder_store.go b/pkg/services/folder/folderimpl/dashboard_folder_store.go index a7e5dbf2bb9..7241d3485d3 100644 --- a/pkg/services/folder/folderimpl/dashboard_folder_store.go +++ b/pkg/services/folder/folderimpl/dashboard_folder_store.go @@ -2,6 +2,7 @@ package folderimpl import ( "context" + "fmt" "strings" "github.com/grafana/grafana/pkg/infra/db" @@ -20,37 +21,6 @@ func ProvideDashboardFolderStore(sqlStore db.DB) *DashboardFolderStoreImpl { return &DashboardFolderStoreImpl{store: sqlStore} } -func (d *DashboardFolderStoreImpl) GetFolderByTitle(ctx context.Context, orgID int64, title string, folderUID *string) (*folder.Folder, error) { - if title == "" { - return nil, dashboards.ErrFolderTitleEmpty - } - - // there is a unique constraint on org_id, folder_uid, title - // there are no nested folders so the parent folder id is always 0 - metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() - // nolint:staticcheck - dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, Title: title} - err := d.store.WithDbSession(ctx, func(sess *db.Session) error { - s := sess.Table(&dashboards.Dashboard{}).Where("is_folder = " + d.store.GetDialect().BooleanStr(true)) - if folderUID != nil { - s = s.Where("folder_uid = ?", *folderUID) - } else { - s = s.Where("folder_uid IS NULL") - } - has, err := s.Get(&dashboard) - if err != nil { - return err - } - if !has { - return dashboards.ErrFolderNotFound - } - dashboard.SetID(dashboard.ID) - dashboard.SetUID(dashboard.UID) - return nil - }) - return dashboards.FromDashboard(&dashboard), err -} - func (d *DashboardFolderStoreImpl) GetFolderByID(ctx context.Context, orgID int64, id int64) (*folder.Folder, error) { metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() // nolint:staticcheck @@ -98,6 +68,74 @@ func (d *DashboardFolderStoreImpl) GetFolderByUID(ctx context.Context, orgID int return dashboards.FromDashboard(&dashboard), nil } +func (d *DashboardFolderStoreImpl) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.Folder, error) { + foldr := &folder.Folder{} + err := d.store.WithDbSession(ctx, func(sess *db.Session) error { + exists := false + var err error + s := strings.Builder{} + s.WriteString(`SELECT + d.id as id, + d.org_id as org_id, + d.uid as uid, + f0.parent_uid as parent_uid, + d.title as title, + f0.created as created, + f0.updated as updated, + f0.description as description, + d.version as version, + d.created_by as created_by, + d.updated_by as updated_by, + d.has_acl as has_acl`) + if q.WithFullpath { + s.WriteString(fmt.Sprintf(`, %s AS fullpath`, getFullpathSQL(d.store.GetDialect()))) + } + if q.WithFullpathUIDs { + s.WriteString(fmt.Sprintf(`, %s AS fullpath_uids`, getFullapathUIDsSQL(d.store.GetDialect()))) + } + s.WriteString(" FROM folder f0") + s.WriteString(" INNER JOIN dashboard d ON f0.uid = d.uid AND f0.org_id = d.org_id") + if q.WithFullpath || q.WithFullpathUIDs { + s.WriteString(getFullpathJoinsSQL()) + } + switch { + case q.UID != nil: + s.WriteString(" WHERE f0.uid = ? AND f0.org_id = ?") + exists, err = sess.SQL(s.String(), q.UID, q.OrgID).Get(foldr) + // nolint:staticcheck + case q.ID != nil: + // main difference from sqlstore.Get is that we join to use d.id instead of f0.id here + s.WriteString(" WHERE d.id = ?") + metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() + // nolint:staticcheck + exists, err = sess.SQL(s.String(), q.ID).Get(foldr) + case q.Title != nil: + s.WriteString(" WHERE f0.title = ? AND f0.org_id = ?") + args := []any{*q.Title, q.OrgID} + if q.ParentUID != nil { + s.WriteString(" AND f0.parent_uid = ?") + args = append(args, *q.ParentUID) + } else { + s.WriteString(" AND f0.parent_uid IS NULL") + } + exists, err = sess.SQL(s.String(), args...).Get(foldr) + default: + return folder.ErrBadRequest.Errorf("one of ID, UID, or Title must be included in the command") + } + if err != nil { + return folder.ErrDatabaseError.Errorf("failed to get folder: %w", err) + } + if !exists { + return dashboards.ErrFolderNotFound + } + return nil + }) + + foldr.Fullpath = strings.TrimLeft(foldr.Fullpath, "/") + foldr.FullpathUIDs = strings.TrimLeft(foldr.FullpathUIDs, "/") + return foldr.WithURL(), err +} + // GetFolders returns all folders for the given orgID and UIDs. // If no UIDs are provided then all folders for the org are returned. func (d *DashboardFolderStoreImpl) GetFolders(ctx context.Context, orgID int64, uids []string) (map[string]*folder.Folder, error) { diff --git a/pkg/services/folder/folderimpl/dashboard_folder_store_test.go b/pkg/services/folder/folderimpl/dashboard_folder_store_test.go index e23fc8a0a41..4a7089c2c0a 100644 --- a/pkg/services/folder/folderimpl/dashboard_folder_store_test.go +++ b/pkg/services/folder/folderimpl/dashboard_folder_store_test.go @@ -2,8 +2,10 @@ package folderimpl import ( "context" + "path" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/components/simplejson" @@ -11,6 +13,8 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/folder" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/tag/tagimpl" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tests/testsuite" @@ -32,31 +36,6 @@ func TestIntegrationDashboardFolderStore(t *testing.T) { dashboardStore, err = database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(featuremgmt.FlagPanelTitleSearch), tagimpl.ProvideService(sqlStore)) require.NoError(t, err) } - t.Run("Given dashboard and folder with the same title", func(t *testing.T) { - setup() - var orgId int64 = 1 - title := "Very Unique Name" - var sqlStore db.DB - var folder1, folder2 *dashboards.Dashboard - sqlStore, cfg = db.InitTestDBWithCfg(t) - folderStore := ProvideDashboardFolderStore(sqlStore) - folder2 = insertTestFolder(t, dashboardStore, "TEST", orgId, "", "prod") - _ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.ID, folder2.UID, "prod") - folder1 = insertTestFolder(t, dashboardStore, title, orgId, "", "prod") - - t.Run("GetFolderByTitle should find the folder", func(t *testing.T) { - result, err := folderStore.GetFolderByTitle(context.Background(), orgId, title, nil) - require.NoError(t, err) - require.Equal(t, folder1.UID, result.UID) - }) - - t.Run("GetFolderByTitle should find the folder by folderUID", func(t *testing.T) { - folder3 := insertTestFolder(t, dashboardStore, title, orgId, folder2.UID, "prod") - result, err := folderStore.GetFolderByTitle(context.Background(), orgId, title, &folder2.UID) - require.NoError(t, err) - require.Equal(t, folder3.UID, result.UID) - }) - }) t.Run("GetFolderByUID", func(t *testing.T) { setup() @@ -133,6 +112,160 @@ func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title st return dash } +func TestIntegrationGetDashFolderStore(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + db, cfg := sqlstore.InitTestDB(t) + folderStore := ProvideStore(db) + dashboardStore, err := database.ProvideDashboardStore(db, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(db)) + require.NoError(t, err) + dashFolderStore := ProvideDashboardFolderStore(db) + + orgID := CreateOrg(t, db, cfg) + + // create folder + d, err := dashboardStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{ + OrgID: orgID, + IsFolder: true, + Dashboard: simplejson.NewFromAny(map[string]any{ + "title": folderTitle, + }), + }) + require.NoError(t, err) + uid1 := d.UID + f, err := folderStore.Create(context.Background(), folder.CreateFolderCommand{ + Title: folderTitle, + Description: folderDsc, + OrgID: orgID, + UID: uid1, + }) + require.NoError(t, err) + d2, err := dashboardStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{ + OrgID: orgID, + FolderUID: uid1, + IsFolder: true, + Dashboard: simplejson.NewFromAny(map[string]any{ + "title": folderTitle, + }), + }) + require.NoError(t, err) + uid2 := d2.UID + subfolderWithSameName, err := folderStore.Create(context.Background(), folder.CreateFolderCommand{ + Title: folderTitle, + Description: folderDsc, + OrgID: orgID, + UID: uid2, + ParentUID: f.UID, + }) + require.NoError(t, err) + + t.Run("should gently fail in case of bad request", func(t *testing.T) { + _, err = dashFolderStore.Get(context.Background(), folder.GetFolderQuery{}) + require.Error(t, err) + }) + + t.Run("get folder by UID should succeed", func(t *testing.T) { + ff, err := dashFolderStore.Get(context.Background(), folder.GetFolderQuery{ + UID: &f.UID, + OrgID: orgID, + }) + require.NoError(t, err) + assert.Equal(t, f.UID, ff.UID) + assert.Equal(t, f.OrgID, ff.OrgID) + assert.Equal(t, f.Title, ff.Title) + assert.Equal(t, f.Description, ff.Description) + //assert.Equal(t, folder.GeneralFolderUID, ff.ParentUID) + assert.NotEmpty(t, ff.Created) + assert.NotEmpty(t, ff.Updated) + assert.NotEmpty(t, ff.URL) + }) + + t.Run("get folder by title should succeed", func(t *testing.T) { + ff, err := dashFolderStore.Get(context.Background(), folder.GetFolderQuery{ + Title: &f.Title, + OrgID: orgID, + }) + require.NoError(t, err) + assert.Equal(t, f.UID, ff.UID) + assert.Equal(t, f.OrgID, ff.OrgID) + assert.Equal(t, f.Title, ff.Title) + assert.Equal(t, f.Description, ff.Description) + assert.NotEmpty(t, ff.Created) + assert.NotEmpty(t, ff.Updated) + assert.NotEmpty(t, ff.URL) + }) + + t.Run("get folder by title and parent UID should succeed", func(t *testing.T) { + ff, err := dashFolderStore.Get(context.Background(), folder.GetFolderQuery{ + Title: &f.Title, + OrgID: orgID, + ParentUID: &uid1, + }) + require.NoError(t, err) + assert.Equal(t, subfolderWithSameName.UID, ff.UID) + assert.Equal(t, subfolderWithSameName.OrgID, ff.OrgID) + assert.Equal(t, subfolderWithSameName.Title, ff.Title) + assert.Equal(t, subfolderWithSameName.Description, ff.Description) + assert.Equal(t, subfolderWithSameName.ParentUID, ff.ParentUID) + assert.NotEmpty(t, ff.Created) + assert.NotEmpty(t, ff.Updated) + assert.NotEmpty(t, ff.URL) + }) + + t.Run("get folder by UID should succeed", func(t *testing.T) { + ff, err := dashFolderStore.Get(context.Background(), folder.GetFolderQuery{ + UID: &f.UID, + OrgID: orgID, + }) + require.NoError(t, err) + assert.Equal(t, f.UID, ff.UID) + assert.Equal(t, f.OrgID, ff.OrgID) + assert.Equal(t, f.Title, ff.Title) + assert.Equal(t, f.Description, ff.Description) + assert.NotEmpty(t, ff.Created) + assert.NotEmpty(t, ff.Updated) + assert.NotEmpty(t, ff.URL) + }) + + t.Run("get folder with fullpath should set fullpath as expected", func(t *testing.T) { + ff, err := dashFolderStore.Get(context.Background(), folder.GetFolderQuery{ + UID: &subfolderWithSameName.UID, + OrgID: orgID, + WithFullpath: true, + }) + require.NoError(t, err) + assert.Equal(t, subfolderWithSameName.UID, ff.UID) + assert.Equal(t, subfolderWithSameName.OrgID, ff.OrgID) + assert.Equal(t, subfolderWithSameName.Title, ff.Title) + assert.Equal(t, subfolderWithSameName.Description, ff.Description) + assert.Equal(t, path.Join(f.Title, subfolderWithSameName.Title), ff.Fullpath) + assert.Equal(t, f.UID, ff.ParentUID) + assert.NotEmpty(t, ff.Created) + assert.NotEmpty(t, ff.Updated) + assert.NotEmpty(t, ff.URL) + }) + + t.Run("get folder withFullpathUIDs should set fullpathUIDs as expected", func(t *testing.T) { + ff, err := dashFolderStore.Get(context.Background(), folder.GetFolderQuery{ + UID: &subfolderWithSameName.UID, + OrgID: orgID, + WithFullpathUIDs: true, + }) + require.NoError(t, err) + assert.Equal(t, subfolderWithSameName.UID, ff.UID) + assert.Equal(t, subfolderWithSameName.OrgID, ff.OrgID) + assert.Equal(t, subfolderWithSameName.Title, ff.Title) + assert.Equal(t, subfolderWithSameName.Description, ff.Description) + assert.Equal(t, path.Join(f.UID, subfolderWithSameName.UID), ff.FullpathUIDs) + assert.Equal(t, f.UID, ff.ParentUID) + assert.NotEmpty(t, ff.Created) + assert.NotEmpty(t, ff.Updated) + assert.NotEmpty(t, ff.URL) + }) +} + func insertTestFolder(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64, folderUID string, tags ...any) *dashboards.Dashboard { t.Helper() cmd := dashboards.SaveDashboardCommand{ diff --git a/pkg/services/folder/folderimpl/folder.go b/pkg/services/folder/folderimpl/folder.go index 7aa12fd9f21..39d30ec4ec4 100644 --- a/pkg/services/folder/folderimpl/folder.go +++ b/pkg/services/folder/folderimpl/folder.go @@ -300,41 +300,25 @@ func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*fol return folder.SharedWithMeFolder.WithURL(), nil } - var dashFolder *folder.Folder - var err error - switch { - case q.UID != nil: - if *q.UID == "" { - return &folder.GeneralFolder, nil - } - dashFolder, err = s.getFolderByUID(ctx, q.OrgID, *q.UID) - if err != nil { - return nil, err - } // nolint:staticcheck - case q.ID != nil: - metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() - dashFolder, err = s.getFolderByID(ctx, *q.ID, q.OrgID) - if err != nil { - return nil, err - } - case q.Title != nil: - dashFolder, err = s.getFolderByTitle(ctx, q.OrgID, *q.Title, q.ParentUID) - if err != nil { - return nil, err - } - default: + if q.ID == nil && q.Title == nil && q.UID == nil { return nil, folder.ErrBadRequest.Errorf("either on of UID, ID, Title fields must be present") } - if dashFolder.IsGeneral() { - return dashFolder, nil + // nolint:staticcheck + if (q.UID != nil && *q.UID == "") || (q.ID != nil && *q.ID == folder.GeneralFolder.ID) { + return &folder.GeneralFolder, nil + } + + f, err := s.dashboardFolderStore.Get(ctx, *q) + if err != nil { + return nil, err } // do not get guardian by the folder ID because it differs from the nested folder ID // and the legacy folder ID has been associated with the permissions: // use the folde UID instead that is the same for both - g, err := guardian.NewByFolder(ctx, dashFolder, dashFolder.OrgID, q.SignedInUser) + g, err := guardian.NewByFolder(ctx, f, f.OrgID, q.SignedInUser) if err != nil { return nil, err } @@ -346,38 +330,15 @@ func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*fol return nil, dashboards.ErrFolderAccessDenied } - if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) { - dashFolder.Fullpath = dashFolder.Title - dashFolder.FullpathUIDs = dashFolder.UID - return dashFolder, nil - } - - metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() - // nolint:staticcheck - if q.ID != nil { - q.ID = nil - q.UID = &dashFolder.UID - } - - f, err := s.store.Get(ctx, *q) - if err != nil { - return nil, err - } - // always expose the dashboard store sequential ID metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc() // nolint:staticcheck - f.ID = dashFolder.ID - f.Version = dashFolder.Version if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) { f.Fullpath = f.Title // set full path to the folder title (unescaped) f.FullpathUIDs = f.UID // set full path to the folder UID } - f.CreatedBy = dashFolder.CreatedBy - f.UpdatedBy = dashFolder.UpdatedBy - return f, err } @@ -711,22 +672,10 @@ func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery return s.store.GetParents(ctx, q) } -func (s *Service) getFolderByID(ctx context.Context, id int64, orgID int64) (*folder.Folder, error) { - if id == 0 { - return &folder.GeneralFolder, nil - } - - return s.dashboardFolderStore.GetFolderByID(ctx, orgID, id) -} - func (s *Service) getFolderByUID(ctx context.Context, orgID int64, uid string) (*folder.Folder, error) { return s.dashboardFolderStore.GetFolderByUID(ctx, orgID, uid) } -func (s *Service) getFolderByTitle(ctx context.Context, orgID int64, title string, parentUID *string) (*folder.Folder, error) { - return s.dashboardFolderStore.GetFolderByTitle(ctx, orgID, title, parentUID) -} - func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) { if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) { return s.createOnApiServer(ctx, cmd) diff --git a/pkg/services/folder/folderimpl/folder_test.go b/pkg/services/folder/folderimpl/folder_test.go index 959591db9da..9b545ab01ad 100644 --- a/pkg/services/folder/folderimpl/folder_test.go +++ b/pkg/services/folder/folderimpl/folder_test.go @@ -125,7 +125,9 @@ func TestIntegrationFolderService(t *testing.T) { f := folder.NewFolder("Folder", "") f.UID = folderUID - folderStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(f, nil) + folderStore.On("Get", mock.Anything, mock.MatchedBy(func(query folder.GetFolderQuery) bool { + return query.OrgID == orgID && *query.UID == folderUID + })).Return(f, nil) t.Run("When get folder by id should return access denied error", func(t *testing.T) { _, err := service.Get(context.Background(), &folder.GetFolderQuery{ @@ -174,7 +176,9 @@ func TestIntegrationFolderService(t *testing.T) { newFolder := folder.NewFolder("Folder", "") newFolder.UID = folderUID - folderStore.On("GetFolderByUID", mock.Anything, orgID, folderUID).Return(newFolder, nil) + folderStore.On("Get", mock.Anything, mock.MatchedBy(func(query folder.GetFolderQuery) bool { + return query.OrgID == orgID && *query.UID == f.UID + })).Return(newFolder, nil) err := service.Delete(context.Background(), &folder.DeleteFolderCommand{ UID: folderUID, @@ -272,7 +276,9 @@ func TestIntegrationFolderService(t *testing.T) { t.Run("When deleting folder by uid should not return access denied error", func(t *testing.T) { f := folder.NewFolder(util.GenerateShortUID(), "") f.UID = util.GenerateShortUID() - folderStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil) + folderStore.On("Get", mock.Anything, mock.MatchedBy(func(query folder.GetFolderQuery) bool { + return query.OrgID == orgID && *query.UID == f.UID + })).Return(f, nil) publicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() var actualCmd *dashboards.DeleteDashboardCommand @@ -297,8 +303,9 @@ func TestIntegrationFolderService(t *testing.T) { t.Run("When deleting folder by uid, expectedForceDeleteRules as false, and dashboard Restore turned on should not return access denied error", func(t *testing.T) { f := folder.NewFolder(util.GenerateShortUID(), "") f.UID = util.GenerateShortUID() - folderStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil) - + folderStore.On("Get", mock.Anything, mock.MatchedBy(func(query folder.GetFolderQuery) bool { + return query.OrgID == orgID && *query.UID == f.UID + })).Return(f, nil) var actualCmd *dashboards.DeleteDashboardCommand dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) @@ -321,8 +328,9 @@ func TestIntegrationFolderService(t *testing.T) { t.Run("When deleting folder by uid, expectedForceDeleteRules as true, and dashboard Restore turned on should not return access denied error", func(t *testing.T) { f := folder.NewFolder(util.GenerateShortUID(), "") f.UID = util.GenerateShortUID() - folderStore.On("GetFolderByUID", mock.Anything, orgID, f.UID).Return(f, nil) - + folderStore.On("Get", mock.Anything, mock.MatchedBy(func(query folder.GetFolderQuery) bool { + return query.OrgID == orgID && *query.UID == f.UID + })).Return(f, nil) var actualCmd *dashboards.DeleteDashboardCommand dashStore.On("DeleteDashboard", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { actualCmd = args.Get(1).(*dashboards.DeleteDashboardCommand) @@ -374,16 +382,6 @@ func TestIntegrationFolderService(t *testing.T) { require.NoError(t, err) }) - t.Run("When get folder by title should return folder", func(t *testing.T) { - expected := folder.NewFolder("TEST-"+util.GenerateShortUID(), "") - - folderStore.On("GetFolderByTitle", mock.Anything, orgID, expected.Title, mock.Anything).Return(expected, nil) - - actual, err := service.getFolderByTitle(context.Background(), orgID, expected.Title, nil) - require.Equal(t, expected, actual) - require.NoError(t, err) - }) - t.Cleanup(func() { guardian.New = origNewGuardian }) @@ -1471,11 +1469,8 @@ func TestNestedFolderService(t *testing.T) { // dashboard store commands that should be called. dashStore := &dashboards.FakeDashboardStore{} - - dashboardFolderStore := foldertest.NewFakeFolderStore(t) - nestedFolderStore := folder.NewFakeStore() - nestedFolderStore.ExpectedError = folder.ErrFolderNotFound + dashboardFolderStore := foldertest.NewFakeFolderStore(t) folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), actest.FakeAccessControl{ ExpectedEvaluate: true, diff --git a/pkg/services/folder/foldertest/folder_store_mock.go b/pkg/services/folder/foldertest/folder_store_mock.go index 1637fb57e06..816dda5a4bc 100644 --- a/pkg/services/folder/foldertest/folder_store_mock.go +++ b/pkg/services/folder/foldertest/folder_store_mock.go @@ -40,25 +40,25 @@ func (_m *FakeFolderStore) GetFolderByID(ctx context.Context, orgID int64, id in return r0, r1 } -// GetFolderByTitle provides a mock function with given fields: ctx, orgID, title, folderUID -func (_m *FakeFolderStore) GetFolderByTitle(ctx context.Context, orgID int64, title string, folderUID *string) (*folder.Folder, error) { - ret := _m.Called(ctx, orgID, title, folderUID) +// Get provides a mock function with given fields: ctx, query +func (_m *FakeFolderStore) Get(ctx context.Context, query folder.GetFolderQuery) (*folder.Folder, error) { + ret := _m.Called(ctx, query) var r0 *folder.Folder var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, string, *string) (*folder.Folder, error)); ok { - return rf(ctx, orgID, title, folderUID) + if rf, ok := ret.Get(0).(func(context.Context, folder.GetFolderQuery) (*folder.Folder, error)); ok { + return rf(ctx, query) } - if rf, ok := ret.Get(0).(func(context.Context, int64, string, *string) *folder.Folder); ok { - r0 = rf(ctx, orgID, title, folderUID) + if rf, ok := ret.Get(0).(func(context.Context, folder.GetFolderQuery) *folder.Folder); ok { + r0 = rf(ctx, query) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*folder.Folder) } } - if rf, ok := ret.Get(1).(func(context.Context, int64, string, *string) error); ok { - r1 = rf(ctx, orgID, title, folderUID) + if rf, ok := ret.Get(1).(func(context.Context, folder.GetFolderQuery) error); ok { + r1 = rf(ctx, query) } else { r1 = ret.Error(1) } diff --git a/pkg/services/folder/service.go b/pkg/services/folder/service.go index 2d72b38914d..bb00ed02a8c 100644 --- a/pkg/services/folder/service.go +++ b/pkg/services/folder/service.go @@ -64,11 +64,8 @@ type Service interface { // //go:generate mockery --name FolderStore --structname FakeFolderStore --outpkg foldertest --output foldertest --filename folder_store_mock.go type FolderStore interface { - // GetFolderByTitle retrieves a folder by its title - // It expects a parentUID as last argument. - // If parentUID is empty then the folder will be fetched from the root level - // otherwise it will be fetched from the subfolder under the folder with the given UID. - GetFolderByTitle(ctx context.Context, orgID int64, title string, parentUID *string) (*Folder, error) + // Get joins on the dashboard and folder table to return all information needed for a folder + Get(ctx context.Context, q GetFolderQuery) (*Folder, error) // GetFolderByUID retrieves a folder by its UID GetFolderByUID(ctx context.Context, orgID int64, uid string) (*Folder, error) // GetFolderByID retrieves a folder by its ID