Folders: Change query to an inner join on dashboards rather than 2 gets (#103183)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user