mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Nested Folders: Support getting of nested folder in folder service wh… (#58597)
* 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
This commit is contained in:
@@ -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{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user