Folders: Change query to an inner join on dashboards rather than 2 gets (#103183)

pull/103239/head
Stephanie Hingtgen 2 months ago committed by GitHub
parent ac39141021
commit 5535447587
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 100
      pkg/services/folder/folderimpl/dashboard_folder_store.go
  2. 183
      pkg/services/folder/folderimpl/dashboard_folder_store_test.go
  3. 71
      pkg/services/folder/folderimpl/folder.go
  4. 37
      pkg/services/folder/folderimpl/folder_test.go
  5. 18
      pkg/services/folder/foldertest/folder_store_mock.go
  6. 7
      pkg/services/folder/service.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) {

@ -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{

@ -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)

@ -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,

@ -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)
}

@ -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

Loading…
Cancel
Save