mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: Fix empty folder details for nested folder items (#76504)
* Introduce dashboard.folder_uid column * Add data migration * Search: Fix empty folder details for nested folders * Set `dashboard.folder_uid` and update tests * Add unique index * lint Ignore cyclomatic complexity of func `(*DashboardServiceImpl).BuildSaveDashboardCommand * Fix search by folder UID
This commit is contained in:
parent
442e533803
commit
03a626f1d6
@ -412,10 +412,13 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
|
|||||||
|
|
||||||
cmd.OrgID = c.SignedInUser.GetOrgID()
|
cmd.OrgID = c.SignedInUser.GetOrgID()
|
||||||
cmd.UserID = userID
|
cmd.UserID = userID
|
||||||
if cmd.FolderUID != "" {
|
// nolint:staticcheck
|
||||||
|
if cmd.FolderUID != "" || cmd.FolderID != 0 {
|
||||||
folder, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
folder, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||||
OrgID: c.SignedInUser.GetOrgID(),
|
OrgID: c.SignedInUser.GetOrgID(),
|
||||||
UID: &cmd.FolderUID,
|
UID: &cmd.FolderUID,
|
||||||
|
// nolint:staticcheck
|
||||||
|
ID: &cmd.FolderID,
|
||||||
SignedInUser: c.SignedInUser,
|
SignedInUser: c.SignedInUser,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -424,7 +427,9 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
|
|||||||
}
|
}
|
||||||
return response.Error(http.StatusInternalServerError, "Error while checking folder ID", err)
|
return response.Error(http.StatusInternalServerError, "Error while checking folder ID", err)
|
||||||
}
|
}
|
||||||
|
// nolint:staticcheck
|
||||||
cmd.FolderID = folder.ID
|
cmd.FolderID = folder.ID
|
||||||
|
cmd.FolderUID = folder.UID
|
||||||
}
|
}
|
||||||
|
|
||||||
dash := cmd.GetDashboardModel()
|
dash := cmd.GetDashboardModel()
|
||||||
@ -1073,7 +1078,9 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *contextmodel.ReqContext) respon
|
|||||||
saveCmd.Dashboard.Set("version", dash.Version)
|
saveCmd.Dashboard.Set("version", dash.Version)
|
||||||
saveCmd.Dashboard.Set("uid", dash.UID)
|
saveCmd.Dashboard.Set("uid", dash.UID)
|
||||||
saveCmd.Message = fmt.Sprintf("Restored from version %d", version.Version)
|
saveCmd.Message = fmt.Sprintf("Restored from version %d", version.Version)
|
||||||
|
// nolint:staticcheck
|
||||||
saveCmd.FolderID = dash.FolderID
|
saveCmd.FolderID = dash.FolderID
|
||||||
|
saveCmd.FolderUID = dash.FolderUID
|
||||||
|
|
||||||
return hs.postDashboard(c, saveCmd)
|
return hs.postDashboard(c, saveCmd)
|
||||||
}
|
}
|
||||||
|
@ -394,6 +394,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
// This tests that a valid request returns correct response
|
// This tests that a valid request returns correct response
|
||||||
t.Run("Given a correct request for creating a dashboard", func(t *testing.T) {
|
t.Run("Given a correct request for creating a dashboard", func(t *testing.T) {
|
||||||
const folderID int64 = 3
|
const folderID int64 = 3
|
||||||
|
folderUID := "Folder"
|
||||||
const dashID int64 = 2
|
const dashID int64 = 2
|
||||||
|
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
@ -404,15 +405,19 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
FolderID: folderID,
|
FolderID: folderID,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
Message: "msg",
|
Message: "msg",
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).
|
dashboardService.On("SaveDashboard", mock.Anything, mock.AnythingOfType("*dashboards.SaveDashboardDTO"), mock.AnythingOfType("bool")).
|
||||||
Return(&dashboards.Dashboard{ID: dashID, UID: "uid", Title: "Dash", Slug: "dash", Version: 2}, nil)
|
Return(&dashboards.Dashboard{ID: dashID, UID: "uid", Title: "Dash", Slug: "dash", Version: 2, FolderUID: folderUID, FolderID: folderID}, nil)
|
||||||
|
mockFolderService := &foldertest.FakeService{
|
||||||
|
ExpectedFolder: &folder.Folder{ID: 1, UID: folderUID, Title: "Folder"},
|
||||||
|
}
|
||||||
|
|
||||||
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, nil, func(sc *scenarioContext) {
|
postDashboardScenario(t, "When calling POST on", "/api/dashboards", "/api/dashboards", cmd, dashboardService, mockFolderService, func(sc *scenarioContext) {
|
||||||
callPostDashboardShouldReturnSuccess(sc)
|
callPostDashboardShouldReturnSuccess(sc)
|
||||||
|
|
||||||
result := sc.ToJSON()
|
result := sc.ToJSON()
|
||||||
@ -664,6 +669,12 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
Data: fakeDash.Data,
|
Data: fakeDash.Data,
|
||||||
}}
|
}}
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
mockSQLStore := dbtest.NewFakeDB()
|
||||||
|
origNewGuardian := guardian.New
|
||||||
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true})
|
||||||
|
t.Cleanup(func() {
|
||||||
|
guardian.New = origNewGuardian
|
||||||
|
})
|
||||||
|
|
||||||
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
|
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
|
||||||
"/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) {
|
"/api/dashboards/id/:dashboardId/restore", dashboardService, fakeDashboardVersionService, cmd, func(sc *scenarioContext) {
|
||||||
sc.dashboardVersionService = fakeDashboardVersionService
|
sc.dashboardVersionService = fakeDashboardVersionService
|
||||||
@ -1084,6 +1095,9 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
cmd dtos.RestoreDashboardVersionCommand, fn scenarioFunc, sqlStore db.DB) {
|
cmd dtos.RestoreDashboardVersionCommand, fn scenarioFunc, sqlStore db.DB) {
|
||||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
|
folderSvc := foldertest.NewFakeService()
|
||||||
|
folderSvc.ExpectedFolder = &folder.Folder{}
|
||||||
|
|
||||||
hs := HTTPServer{
|
hs := HTTPServer{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
ProvisioningService: provisioning.NewProvisioningServiceMock(context.Background()),
|
||||||
@ -1097,6 +1111,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
Kinds: corekind.NewBase(nil),
|
Kinds: corekind.NewBase(nil),
|
||||||
accesscontrolService: actest.FakeService{},
|
accesscontrolService: actest.FakeService{},
|
||||||
|
folderService: folderSvc,
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
|
@ -224,7 +224,7 @@ func (hs *HTTPServer) MoveFolder(c *contextmodel.ReqContext) response.Response {
|
|||||||
cmd.SignedInUser = c.SignedInUser
|
cmd.SignedInUser = c.SignedInUser
|
||||||
theFolder, err := hs.folderService.Move(c.Req.Context(), &cmd)
|
theFolder, err := hs.folderService.Move(c.Req.Context(), &cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "move folder failed", err)
|
return response.ErrOrFallback(http.StatusInternalServerError, "move folder failed", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
folderDTO, err := hs.newToFolderDto(c, theFolder)
|
folderDTO, err := hs.newToFolderDto(c, theFolder)
|
||||||
|
@ -58,7 +58,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
|
|||||||
features: featuremgmt.WithFeatures(),
|
features: featuremgmt.WithFeatures(),
|
||||||
}
|
}
|
||||||
|
|
||||||
testDash = insertTestDashboard(t, store.db, "dashboard with alerts", 1, 0, false, "alert")
|
testDash = insertTestDashboard(t, store.db, "dashboard with alerts", 1, 0, "", false, "alert")
|
||||||
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
|
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
items = []*models.Alert{
|
items = []*models.Alert{
|
||||||
@ -337,7 +337,7 @@ func TestIntegrationPausingAlerts(t *testing.T) {
|
|||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
sqlStore := sqlStore{db: ss, cfg: cfg, log: log.New(), tagService: tagimpl.ProvideService(ss, ss.Cfg), features: featuremgmt.WithFeatures()}
|
sqlStore := sqlStore{db: ss, cfg: cfg, log: log.New(), tagService: tagimpl.ProvideService(ss, ss.Cfg), features: featuremgmt.WithFeatures()}
|
||||||
|
|
||||||
testDash := insertTestDashboard(t, sqlStore.db, "dashboard with alerts", 1, 0, false, "alert")
|
testDash := insertTestDashboard(t, sqlStore.db, "dashboard with alerts", 1, 0, "", false, "alert")
|
||||||
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgID, testDash.ID, simplejson.New(), sqlStore)
|
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgID, testDash.ID, simplejson.New(), sqlStore)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
@ -435,11 +435,12 @@ func (ss *sqlStore) pauseAllAlerts(t *testing.T, pauseState bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func insertTestDashboard(t *testing.T, store db.DB, title string, orgId int64,
|
func insertTestDashboard(t *testing.T, store db.DB, title string, orgId int64,
|
||||||
folderId int64, isFolder bool, tags ...any) *dashboards.Dashboard {
|
folderId int64, folderUID string, isFolder bool, tags ...any) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderId,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: isFolder,
|
IsFolder: isFolder,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -121,6 +121,7 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
|
|||||||
Overwrite: req.Overwrite,
|
Overwrite: req.Overwrite,
|
||||||
PluginID: req.PluginId,
|
PluginID: req.PluginId,
|
||||||
FolderID: req.FolderId,
|
FolderID: req.FolderId,
|
||||||
|
FolderUID: req.FolderUid,
|
||||||
}
|
}
|
||||||
|
|
||||||
dto := &dashboards.SaveDashboardDTO{
|
dto := &dashboards.SaveDashboardDTO{
|
||||||
|
@ -38,8 +38,8 @@ func TestIntegrationDashboardACLDataAccess(t *testing.T) {
|
|||||||
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
currentUser = createUser(t, sqlStore, "viewer", "Viewer", false)
|
currentUser = createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||||
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||||
childDash = insertTestDashboard(t, dashboardStore, "2 test dash", 1, savedFolder.ID, false, "prod", "webapp")
|
childDash = insertTestDashboard(t, dashboardStore, "2 test dash", 1, savedFolder.ID, savedFolder.UID, false, "prod", "webapp")
|
||||||
return currentUser.OrgID
|
return currentUser.OrgID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ func saveDashboard(sess *db.Session, cmd *dashboards.SaveDashboardCommand, emitE
|
|||||||
dash.Updated = time.Now()
|
dash.Updated = time.Now()
|
||||||
dash.UpdatedBy = userId
|
dash.UpdatedBy = userId
|
||||||
metrics.MApiDashboardInsert.Inc()
|
metrics.MApiDashboardInsert.Inc()
|
||||||
affectedRows, err = sess.Insert(dash)
|
affectedRows, err = sess.Nullable("folder_uid").Insert(dash)
|
||||||
} else {
|
} else {
|
||||||
dash.SetVersion(dash.Version + 1)
|
dash.SetVersion(dash.Version + 1)
|
||||||
|
|
||||||
@ -480,7 +480,7 @@ func saveDashboard(sess *db.Session, cmd *dashboards.SaveDashboardCommand, emitE
|
|||||||
|
|
||||||
dash.UpdatedBy = userId
|
dash.UpdatedBy = userId
|
||||||
|
|
||||||
affectedRows, err = sess.MustCols("folder_id").ID(dash.ID).Update(dash)
|
affectedRows, err = sess.MustCols("folder_id", "folder_uid").Nullable("folder_uid").ID(dash.ID).Update(dash)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1007,11 +1007,11 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(query.FolderUIDs) > 0 {
|
if len(query.FolderUIDs) > 0 {
|
||||||
filters = append(filters, searchstore.FolderUIDFilter{Dialect: d.store.GetDialect(), OrgID: orgID, UIDs: query.FolderUIDs})
|
filters = append(filters, searchstore.FolderUIDFilter{Dialect: d.store.GetDialect(), OrgID: orgID, UIDs: query.FolderUIDs, NestedFoldersEnabled: d.features.IsEnabled(featuremgmt.FlagNestedFolders)})
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []dashboards.DashboardSearchProjection
|
var res []dashboards.DashboardSearchProjection
|
||||||
sb := &searchstore.Builder{Dialect: d.store.GetDialect(), Filters: filters}
|
sb := &searchstore.Builder{Dialect: d.store.GetDialect(), Filters: filters, Features: d.features}
|
||||||
|
|
||||||
limit := query.Limit
|
limit := query.Limit
|
||||||
if limit < 1 {
|
if limit < 1 {
|
||||||
|
@ -49,10 +49,10 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
var err error
|
var err error
|
||||||
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
flder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
flder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||||
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod", "webapp")
|
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod", "webapp")
|
||||||
childDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, flder.ID, false, "prod", "webapp")
|
childDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, flder.ID, flder.UID, false, "prod", "webapp")
|
||||||
insertTestDashboard(t, dashboardStore, "test dash 45", 1, flder.ID, false, "prod")
|
insertTestDashboard(t, dashboardStore, "test dash 45", 1, flder.ID, flder.UID, false, "prod")
|
||||||
currentUser = &user.SignedInUser{
|
currentUser = &user.SignedInUser{
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
@ -147,11 +147,11 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
var err error
|
var err error
|
||||||
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod")
|
folder1 = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod")
|
||||||
folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod")
|
folder2 = insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, "", true, "prod")
|
||||||
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod")
|
dashInRoot = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod")
|
||||||
childDash1 = insertTestDashboard(t, dashboardStore, "child dash 1", 1, folder1.ID, false, "prod")
|
childDash1 = insertTestDashboard(t, dashboardStore, "child dash 1", 1, folder1.ID, folder1.UID, false, "prod")
|
||||||
childDash2 = insertTestDashboard(t, dashboardStore, "child dash 2", 1, folder2.ID, false, "prod")
|
childDash2 = insertTestDashboard(t, dashboardStore, "child dash 2", 1, folder2.ID, folder2.UID, false, "prod")
|
||||||
|
|
||||||
currentUser = &user.SignedInUser{
|
currentUser = &user.SignedInUser{
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
@ -186,7 +186,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("and acl is set for one dashboard folder", func(t *testing.T) {
|
t.Run("and acl is set for one dashboard folder", func(t *testing.T) {
|
||||||
t.Run("and a dashboard is moved from folder without acl to the folder with an acl", func(t *testing.T) {
|
t.Run("and a dashboard is moved from folder without acl to the folder with an acl", func(t *testing.T) {
|
||||||
moveDashboard(t, dashboardStore, 1, childDash2.Data, folder1.ID)
|
moveDashboard(t, dashboardStore, 1, childDash2.Data, folder1.ID, folder1.UID)
|
||||||
currentUser.Permissions = map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID), dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashInRoot.UID)}}}
|
currentUser.Permissions = map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID), dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashInRoot.UID)}}}
|
||||||
actest.AddUserPermissionToDB(t, sqlStore, currentUser)
|
actest.AddUserPermissionToDB(t, sqlStore, currentUser)
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
t.Run("and a dashboard is moved from folder with acl to the folder without an acl", func(t *testing.T) {
|
t.Run("and a dashboard is moved from folder with acl to the folder without an acl", func(t *testing.T) {
|
||||||
setup2()
|
setup2()
|
||||||
moveDashboard(t, dashboardStore, 1, childDash1.Data, folder2.ID)
|
moveDashboard(t, dashboardStore, 1, childDash1.Data, folder2.ID, folder2.UID)
|
||||||
currentUser.Permissions = map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashInRoot.UID), dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}, dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}}}
|
currentUser.Permissions = map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsProvider.GetResourceScopeUID(dashInRoot.UID), dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}, dashboards.ActionFoldersRead: {dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2.UID)}}}
|
||||||
actest.AddUserPermissionToDB(t, sqlStore, currentUser)
|
actest.AddUserPermissionToDB(t, sqlStore, currentUser)
|
||||||
|
|
||||||
@ -435,12 +435,13 @@ func TestIntegrationDashboardInheritedFolderRBAC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func moveDashboard(t *testing.T, dashboardStore dashboards.Store, orgId int64, dashboard *simplejson.Json,
|
func moveDashboard(t *testing.T, dashboardStore dashboards.Store, orgId int64, dashboard *simplejson.Json,
|
||||||
newFolderId int64) *dashboards.Dashboard {
|
newFolderId int64, newFolderUID string) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: newFolderId,
|
FolderID: newFolderId,
|
||||||
|
FolderUID: newFolderUID,
|
||||||
Dashboard: dashboard,
|
Dashboard: dashboard,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ func TestIntegrationDashboardProvisioningTest(t *testing.T) {
|
|||||||
folderCmd := dashboards.SaveDashboardCommand{
|
folderCmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
IsFolder: true,
|
IsFolder: true,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
@ -40,6 +41,7 @@ func TestIntegrationDashboardProvisioningTest(t *testing.T) {
|
|||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
FolderID: dash.ID,
|
FolderID: dash.ID,
|
||||||
|
FolderUID: dash.UID,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
"title": "test dashboard",
|
"title": "test dashboard",
|
||||||
@ -66,6 +68,7 @@ func TestIntegrationDashboardProvisioningTest(t *testing.T) {
|
|||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
FolderID: dash.ID,
|
FolderID: dash.ID,
|
||||||
|
FolderUID: dash.UID,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
"title": "another_dashboard",
|
"title": "another_dashboard",
|
||||||
|
@ -11,11 +11,17 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||||
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||||
"github.com/grafana/grafana/pkg/services/search/model"
|
"github.com/grafana/grafana/pkg/services/search/model"
|
||||||
@ -42,10 +48,10 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
var err error
|
var err error
|
||||||
dashboardStore, err = ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err = ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||||
savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.ID, false, "prod", "webapp")
|
savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.ID, savedFolder.UID, false, "prod", "webapp")
|
||||||
insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.ID, false, "prod")
|
insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.ID, savedFolder.UID, false, "prod")
|
||||||
savedDash2 = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod")
|
savedDash2 = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, "", false, "prod")
|
||||||
insertTestRule(t, sqlStore, savedFolder.OrgID, savedFolder.UID)
|
insertTestRule(t, sqlStore, savedFolder.OrgID, savedFolder.UID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +180,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Should be able to delete dashboard", func(t *testing.T) {
|
t.Run("Should be able to delete dashboard", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
dash := insertTestDashboard(t, dashboardStore, "delete me", 1, 0, false, "delete this")
|
dash := insertTestDashboard(t, dashboardStore, "delete me", 1, 0, "", false, "delete this")
|
||||||
|
|
||||||
err := dashboardStore.DeleteDashboard(context.Background(), &dashboards.DeleteDashboardCommand{
|
err := dashboardStore.DeleteDashboard(context.Background(), &dashboards.DeleteDashboardCommand{
|
||||||
ID: dash.ID,
|
ID: dash.ID,
|
||||||
@ -248,7 +254,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Should be able to delete empty folder", func(t *testing.T) {
|
t.Run("Should be able to delete empty folder", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
emptyFolder := insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, true, "prod", "webapp")
|
emptyFolder := insertTestDashboard(t, dashboardStore, "2 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||||
|
|
||||||
deleteCmd := &dashboards.DeleteDashboardCommand{ID: emptyFolder.ID}
|
deleteCmd := &dashboards.DeleteDashboardCommand{ID: emptyFolder.ID}
|
||||||
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
err := dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
|
||||||
@ -522,9 +528,9 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Can delete dashboards in folder", func(t *testing.T) {
|
t.Run("Can delete dashboards in folder", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
folder := insertTestDashboard(t, dashboardStore, "dash folder", 1, 0, true, "prod", "webapp")
|
folder := insertTestDashboard(t, dashboardStore, "dash folder", 1, 0, "", true, "prod", "webapp")
|
||||||
_ = insertTestDashboard(t, dashboardStore, "delete me 1", 1, folder.ID, false, "delete this 1")
|
_ = insertTestDashboard(t, dashboardStore, "delete me 1", 1, folder.ID, folder.UID, false, "delete this 1")
|
||||||
_ = insertTestDashboard(t, dashboardStore, "delete me 2", 1, folder.ID, false, "delete this 2")
|
_ = insertTestDashboard(t, dashboardStore, "delete me 2", 1, folder.ID, folder.UID, false, "delete this 2")
|
||||||
|
|
||||||
err := dashboardStore.DeleteDashboardsInFolder(
|
err := dashboardStore.DeleteDashboardsInFolder(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@ -577,8 +583,8 @@ func TestIntegrationDashboard_SortingOptions(t *testing.T) {
|
|||||||
dashboardStore, err := ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err := ProvideDashboardStore(sqlStore, &setting.Cfg{}, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, false)
|
dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, "", false)
|
||||||
dashA := insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, false)
|
dashA := insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, "", false)
|
||||||
assert.NotZero(t, dashA.ID)
|
assert.NotZero(t, dashA.ID)
|
||||||
assert.Less(t, dashB.ID, dashA.ID)
|
assert.Less(t, dashB.ID, dashA.ID)
|
||||||
qNoSort := &dashboards.FindPersistedDashboardsQuery{
|
qNoSort := &dashboards.FindPersistedDashboardsQuery{
|
||||||
@ -629,8 +635,8 @@ func TestIntegrationDashboard_Filter(t *testing.T) {
|
|||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, false)
|
insertTestDashboard(t, dashboardStore, "Alfa", 1, 0, "", false)
|
||||||
dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, false)
|
dashB := insertTestDashboard(t, dashboardStore, "Beta", 1, 0, "", false)
|
||||||
qNoFilter := &dashboards.FindPersistedDashboardsQuery{
|
qNoFilter := &dashboards.FindPersistedDashboardsQuery{
|
||||||
SignedInUser: &user.SignedInUser{
|
SignedInUser: &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
@ -674,7 +680,7 @@ func TestGetExistingDashboardByTitleAndFolder(t *testing.T) {
|
|||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
insertTestDashboard(t, dashboardStore, "Apple", 1, 0, false)
|
insertTestDashboard(t, dashboardStore, "Apple", 1, 0, "", false)
|
||||||
t.Run("Finds a dashboard with existing name in root directory and throws DashboardWithSameNameInFolderExists error", func(t *testing.T) {
|
t.Run("Finds a dashboard with existing name in root directory and throws DashboardWithSameNameInFolderExists error", func(t *testing.T) {
|
||||||
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
_, err = getExistingDashboardByTitleAndFolder(sess, &dashboards.Dashboard{Title: "Apple", OrgID: 1}, sqlStore.GetDialect(), false, false)
|
_, err = getExistingDashboardByTitleAndFolder(sess, &dashboards.Dashboard{Title: "Apple", OrgID: 1}, sqlStore.GetDialect(), false, false)
|
||||||
@ -692,8 +698,8 @@ func TestGetExistingDashboardByTitleAndFolder(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Finds a dashboard with existing name in specific folder and throws DashboardWithSameNameInFolderExists error", func(t *testing.T) {
|
t.Run("Finds a dashboard with existing name in specific folder and throws DashboardWithSameNameInFolderExists error", func(t *testing.T) {
|
||||||
savedFolder := insertTestDashboard(t, dashboardStore, "test dash folder", 1, 0, true, "prod", "webapp")
|
savedFolder := insertTestDashboard(t, dashboardStore, "test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||||
savedDash := insertTestDashboard(t, dashboardStore, "test dash", 1, savedFolder.ID, false, "prod", "webapp")
|
savedDash := insertTestDashboard(t, dashboardStore, "test dash", 1, savedFolder.ID, savedFolder.UID, false, "prod", "webapp")
|
||||||
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err = sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
_, err = getExistingDashboardByTitleAndFolder(sess, &dashboards.Dashboard{Title: savedDash.Title, FolderID: savedFolder.ID, OrgID: 1}, sqlStore.GetDialect(), false, false)
|
_, err = getExistingDashboardByTitleAndFolder(sess, &dashboards.Dashboard{Title: savedDash.Title, FolderID: savedFolder.ID, OrgID: 1}, sqlStore.GetDialect(), false, false)
|
||||||
return err
|
return err
|
||||||
@ -702,6 +708,122 @@ func TestGetExistingDashboardByTitleAndFolder(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping integration test")
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStore := db.InitTestDB(t)
|
||||||
|
cfg := setting.NewCfg()
|
||||||
|
cfg.IsFeatureToggleEnabled = func(key string) bool { return false }
|
||||||
|
quotaService := quotatest.New(false, nil)
|
||||||
|
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPanelTitleSearch)
|
||||||
|
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
orgID := int64(1)
|
||||||
|
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false)
|
||||||
|
|
||||||
|
ac := acimpl.ProvideAccessControl(sqlStore.Cfg)
|
||||||
|
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||||
|
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardStore, folderStore, sqlStore, features)
|
||||||
|
|
||||||
|
user := &user.SignedInUser{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
orgID: {
|
||||||
|
dashboards.ActionDashboardsRead: []string{dashboards.ScopeDashboardsAll},
|
||||||
|
dashboards.ActionFoldersRead: []string{dashboards.ScopeFoldersAll},
|
||||||
|
dashboards.ActionFoldersWrite: []string{dashboards.ScopeFoldersAll},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
origNewGuardian := guardian.New
|
||||||
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||||
|
CanSaveValue: true,
|
||||||
|
CanViewValue: true,
|
||||||
|
// CanEditValue is required to create library elements
|
||||||
|
CanEditValue: true,
|
||||||
|
})
|
||||||
|
t.Cleanup(func() {
|
||||||
|
guardian.New = origNewGuardian
|
||||||
|
})
|
||||||
|
|
||||||
|
f0, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||||
|
OrgID: orgID,
|
||||||
|
Title: "f0",
|
||||||
|
SignedInUser: user,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, f0.ID, f0.UID, false)
|
||||||
|
|
||||||
|
subfolder, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||||
|
OrgID: orgID,
|
||||||
|
Title: "subfolder",
|
||||||
|
ParentUID: f0.UID,
|
||||||
|
SignedInUser: user,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
insertTestDashboard(t, dashboardStore, "dashboard under subfolder", orgID, subfolder.ID, subfolder.UID, false)
|
||||||
|
|
||||||
|
type res struct {
|
||||||
|
title string
|
||||||
|
folderUID string
|
||||||
|
folderTitle string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
title string
|
||||||
|
expectedResult res
|
||||||
|
typ string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general",
|
||||||
|
title: "dashboard under general",
|
||||||
|
expectedResult: res{title: "dashboard under general"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under f0",
|
||||||
|
title: "dashboard under f0",
|
||||||
|
expectedResult: res{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under subfolder",
|
||||||
|
title: "dashboard under subfolder",
|
||||||
|
expectedResult: res{title: "dashboard under subfolder", folderUID: subfolder.UID, folderTitle: subfolder.Title},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
|
require.NoError(t, err)
|
||||||
|
res, err := dashboardStore.FindDashboards(context.Background(), &dashboards.FindPersistedDashboardsQuery{
|
||||||
|
SignedInUser: user,
|
||||||
|
Type: tc.typ,
|
||||||
|
Title: tc.title,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res))
|
||||||
|
|
||||||
|
r := tc.expectedResult
|
||||||
|
assert.Equal(t, r.title, res[0].Title)
|
||||||
|
if r.folderUID != "" {
|
||||||
|
assert.Equal(t, r.folderUID, res[0].FolderUID)
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, res[0].FolderUID)
|
||||||
|
}
|
||||||
|
if r.folderTitle != "" {
|
||||||
|
assert.Equal(t, r.folderTitle, res[0].FolderTitle)
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, res[0].FolderTitle)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
@ -711,100 +833,241 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
|||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
cfg.IsFeatureToggleEnabled = func(key string) bool { return false }
|
cfg.IsFeatureToggleEnabled = func(key string) bool { return false }
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagPanelTitleSearch)
|
||||||
|
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
orgID := int64(1)
|
orgID := int64(1)
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, false)
|
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false)
|
||||||
|
|
||||||
f0 := insertTestDashboard(t, dashboardStore, "f0", orgID, 0, true)
|
ac := acimpl.ProvideAccessControl(sqlStore.Cfg)
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, f0.ID, false)
|
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||||
|
folderServiceWithFlagOn := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), sqlStore.Cfg, dashboardStore, folderStore, sqlStore, features)
|
||||||
|
|
||||||
f1 := insertTestDashboard(t, dashboardStore, "f1", orgID, 0, true)
|
user := &user.SignedInUser{
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under f1", orgID, f1.ID, false)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
folderIDs []int64
|
|
||||||
folderUIDs []string
|
|
||||||
expectedResult []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "find dashboard under general using folder id",
|
|
||||||
folderIDs: []int64{0},
|
|
||||||
expectedResult: []string{"dashboard under general"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under f0 using folder id",
|
|
||||||
folderIDs: []int64{f0.ID},
|
|
||||||
expectedResult: []string{"dashboard under f0"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under f0 or f1 using folder id",
|
|
||||||
folderIDs: []int64{f0.ID, f1.ID},
|
|
||||||
expectedResult: []string{"dashboard under f0", "dashboard under f1"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under general using folder UID",
|
|
||||||
folderUIDs: []string{folder.GeneralFolderUID},
|
|
||||||
expectedResult: []string{"dashboard under general"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under f0 using folder UID",
|
|
||||||
folderUIDs: []string{f0.UID},
|
|
||||||
expectedResult: []string{"dashboard under f0"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under f0 or f1 using folder UID",
|
|
||||||
folderUIDs: []string{f0.UID, f1.UID},
|
|
||||||
expectedResult: []string{"dashboard under f0", "dashboard under f1"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under general or f0 using folder id",
|
|
||||||
folderIDs: []int64{0, f0.ID},
|
|
||||||
expectedResult: []string{"dashboard under f0", "dashboard under general"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under general or f0 or f1 using folder id",
|
|
||||||
folderIDs: []int64{0, f0.ID, f1.ID},
|
|
||||||
expectedResult: []string{"dashboard under f0", "dashboard under f1", "dashboard under general"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under general or f0 using folder UID",
|
|
||||||
folderUIDs: []string{folder.GeneralFolderUID, f0.UID},
|
|
||||||
expectedResult: []string{"dashboard under f0", "dashboard under general"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "find dashboard under general or f0 or f1 using folder UID",
|
|
||||||
folderUIDs: []string{folder.GeneralFolderUID, f0.UID, f1.UID},
|
|
||||||
expectedResult: []string{"dashboard under f0", "dashboard under f1", "dashboard under general"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
|
||||||
res, err := dashboardStore.FindDashboards(context.Background(), &dashboards.FindPersistedDashboardsQuery{
|
|
||||||
SignedInUser: &user.SignedInUser{
|
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Permissions: map[int64]map[string][]string{
|
Permissions: map[int64]map[string][]string{
|
||||||
orgID: {
|
orgID: {
|
||||||
dashboards.ActionDashboardsRead: []string{dashboards.ScopeDashboardsAll},
|
dashboards.ActionDashboardsRead: []string{dashboards.ScopeDashboardsAll},
|
||||||
dashboards.ActionFoldersRead: []string{dashboards.ScopeFoldersAll},
|
dashboards.ActionFoldersRead: []string{dashboards.ScopeFoldersAll},
|
||||||
|
dashboards.ActionFoldersWrite: []string{dashboards.ScopeFoldersAll},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
origNewGuardian := guardian.New
|
||||||
|
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||||
|
CanSaveValue: true,
|
||||||
|
CanViewValue: true,
|
||||||
|
// CanEditValue is required to create library elements
|
||||||
|
CanEditValue: true,
|
||||||
|
})
|
||||||
|
t.Cleanup(func() {
|
||||||
|
guardian.New = origNewGuardian
|
||||||
|
})
|
||||||
|
|
||||||
|
f0, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||||
|
OrgID: orgID,
|
||||||
|
Title: "f0",
|
||||||
|
SignedInUser: user,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, f0.ID, f0.UID, false)
|
||||||
|
|
||||||
|
f1, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||||
|
OrgID: orgID,
|
||||||
|
Title: "f1",
|
||||||
|
SignedInUser: user,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
insertTestDashboard(t, dashboardStore, "dashboard under f1", orgID, f1.ID, f1.UID, false)
|
||||||
|
|
||||||
|
subfolder, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||||
|
OrgID: orgID,
|
||||||
|
Title: "subfolder",
|
||||||
|
ParentUID: f0.UID,
|
||||||
|
SignedInUser: user,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
type res struct {
|
||||||
|
title string
|
||||||
|
folderUID string
|
||||||
|
folderTitle string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
folderIDs []int64
|
||||||
|
folderUIDs []string
|
||||||
|
query string
|
||||||
|
expectedResult map[string][]res
|
||||||
|
typ string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general using folder id",
|
||||||
|
folderIDs: []int64{0},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under general"}},
|
||||||
},
|
},
|
||||||
Type: searchstore.TypeDashboard,
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general using folder id",
|
||||||
|
folderIDs: []int64{0},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under f0 using folder id",
|
||||||
|
folderIDs: []int64{f0.ID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under f0 or f1 using folder id",
|
||||||
|
folderIDs: []int64{f0.ID, f1.ID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general using folder UID",
|
||||||
|
folderUIDs: []string{folder.GeneralFolderUID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general using folder UID",
|
||||||
|
folderUIDs: []string{folder.GeneralFolderUID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under f0 using folder UID",
|
||||||
|
folderUIDs: []string{f0.UID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under f0 or f1 using folder UID",
|
||||||
|
folderUIDs: []string{f0.UID, f1.UID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general or f0 using folder id",
|
||||||
|
folderIDs: []int64{0, f0.ID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general or f0 or f1 using folder id",
|
||||||
|
folderIDs: []int64{0, f0.ID, f1.ID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general or f0 using folder UID",
|
||||||
|
folderUIDs: []string{folder.GeneralFolderUID, f0.UID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find dashboard under general or f0 or f1 using folder UID",
|
||||||
|
folderUIDs: []string{folder.GeneralFolderUID, f0.UID, f1.UID},
|
||||||
|
typ: searchstore.TypeDashboard,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: "dashboard under f0", folderUID: f0.UID, folderTitle: f0.Title},
|
||||||
|
{title: "dashboard under f1", folderUID: f1.UID, folderTitle: f1.Title},
|
||||||
|
{title: "dashboard under general"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "find subfolder",
|
||||||
|
folderUIDs: []string{f0.UID},
|
||||||
|
typ: searchstore.TypeFolder,
|
||||||
|
expectedResult: map[string][]res{
|
||||||
|
"": {},
|
||||||
|
featuremgmt.FlagNestedFolders: {{title: subfolder.Title, folderUID: f0.UID, folderTitle: f0.Title}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
for featureFlags := range tc.expectedResult {
|
||||||
|
t.Run(fmt.Sprintf("%s with featureFlags: %v", tc.desc, featureFlags), func(t *testing.T) {
|
||||||
|
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(featureFlags), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
|
require.NoError(t, err)
|
||||||
|
res, err := dashboardStore.FindDashboards(context.Background(), &dashboards.FindPersistedDashboardsQuery{
|
||||||
|
SignedInUser: user,
|
||||||
|
Type: tc.typ,
|
||||||
FolderIds: tc.folderIDs,
|
FolderIds: tc.folderIDs,
|
||||||
FolderUIDs: tc.folderUIDs,
|
FolderUIDs: tc.folderUIDs,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(tc.expectedResult), len(res))
|
require.Equal(t, len(tc.expectedResult[featureFlags]), len(res))
|
||||||
|
|
||||||
for i, r := range tc.expectedResult {
|
for i, r := range tc.expectedResult[featureFlags] {
|
||||||
assert.Equal(t, r, res[i].Title)
|
assert.Equal(t, r.title, res[i].Title)
|
||||||
|
if r.folderUID != "" {
|
||||||
|
assert.Equal(t, r.folderUID, res[i].FolderUID)
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, res[i].FolderUID)
|
||||||
|
}
|
||||||
|
if r.folderTitle != "" {
|
||||||
|
assert.Equal(t, r.folderTitle, res[i].FolderTitle)
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, res[i].FolderTitle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertTestRule(t *testing.T, sqlStore db.DB, foderOrgID int64, folderUID string) {
|
func insertTestRule(t *testing.T, sqlStore db.DB, foderOrgID int64, folderUID string) {
|
||||||
@ -881,11 +1144,12 @@ func insertTestRule(t *testing.T, sqlStore db.DB, foderOrgID int64, folderUID st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
||||||
folderId int64, isFolder bool, tags ...interface{}) *dashboards.Dashboard {
|
folderId int64, folderUID string, isFolder bool, tags ...interface{}) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderId,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: isFolder,
|
IsFolder: isFolder,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FolderUIDMigration is a code migration that populates folder_uid column
|
||||||
|
type FolderUIDMigration struct {
|
||||||
|
migrator.MigrationBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FolderUIDMigration) SQL(dialect migrator.Dialect) string {
|
||||||
|
return "code migration"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *FolderUIDMigration) Exec(sess *xorm.Session, mgrtr *migrator.Migrator) error {
|
||||||
|
// for dashboards the source of truth is the dashboard table
|
||||||
|
q := `UPDATE dashboard
|
||||||
|
SET folder_uid = folder.uid
|
||||||
|
FROM dashboard folder
|
||||||
|
WHERE dashboard.folder_id = folder.id
|
||||||
|
AND dashboard.is_folder = ?`
|
||||||
|
if mgrtr.Dialect.DriverName() == migrator.MySQL {
|
||||||
|
q = `UPDATE dashboard AS d
|
||||||
|
LEFT JOIN dashboard AS folder ON d.folder_id = folder.id
|
||||||
|
SET d.folder_uid = folder.uid
|
||||||
|
WHERE d.is_folder = ?`
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := sess.Exec(q, mgrtr.Dialect.BooleanStr(false))
|
||||||
|
if err != nil {
|
||||||
|
mgrtr.Logger.Error("Failed to migrate dashboard folder_uid for dashboards", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dashboardRowsAffected, dashboardRowsAffectedErr := r.RowsAffected()
|
||||||
|
if dashboardRowsAffectedErr != nil {
|
||||||
|
mgrtr.Logger.Error("Failed to get dashboard rows affected", "error", dashboardRowsAffectedErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for folders the source of truth is the folder table
|
||||||
|
q = `UPDATE dashboard
|
||||||
|
SET folder_uid = folder.parent_uid
|
||||||
|
FROM folder
|
||||||
|
WHERE dashboard.uid = folder.uid AND dashboard.org_id = folder.org_id
|
||||||
|
AND dashboard.is_folder = ?`
|
||||||
|
if mgrtr.Dialect.DriverName() == migrator.MySQL {
|
||||||
|
q = `UPDATE dashboard
|
||||||
|
SET folder_uid = (
|
||||||
|
SELECT folder.parent_uid
|
||||||
|
FROM folder
|
||||||
|
WHERE dashboard.uid = folder.uid AND dashboard.org_id = folder.org_id
|
||||||
|
)
|
||||||
|
WHERE is_folder = ?`
|
||||||
|
}
|
||||||
|
r, err = sess.Exec(q, mgrtr.Dialect.BooleanStr(true))
|
||||||
|
if err != nil {
|
||||||
|
mgrtr.Logger.Error("Failed to migrate dashboard folder_uid for folders", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
folderRowsAffected, folderRowsAffectedErr := r.RowsAffected()
|
||||||
|
if folderRowsAffectedErr != nil {
|
||||||
|
mgrtr.Logger.Error("Failed to get folder rows affected", "error", folderRowsAffectedErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
mgrtr.Logger.Debug("Migrating dashboard data", "dashboards rows", dashboardRowsAffected, "folder rows", folderRowsAffected)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddDashboardFolderMigrations(mg *migrator.Migrator) {
|
||||||
|
mg.AddMigration("Add folder_uid for dashboard", migrator.NewAddColumnMigration(migrator.Table{Name: "dashboard"}, &migrator.Column{
|
||||||
|
Name: "folder_uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: true,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mg.AddMigration("Populate dashboard folder_uid column", &FolderUIDMigration{})
|
||||||
|
|
||||||
|
mg.AddMigration("Add unique index for dashboard_org_id_folder_uid_title", migrator.NewAddIndexMigration(migrator.Table{Name: "dashboard"}, &migrator.Index{
|
||||||
|
Cols: []string{"org_id", "folder_uid", "title"}, Type: migrator.UniqueIndex,
|
||||||
|
}))
|
||||||
|
}
|
@ -42,6 +42,7 @@ type Dashboard struct {
|
|||||||
UpdatedBy int64
|
UpdatedBy int64
|
||||||
CreatedBy int64
|
CreatedBy int64
|
||||||
FolderID int64 `xorm:"folder_id"`
|
FolderID int64 `xorm:"folder_id"`
|
||||||
|
FolderUID string `xorm:"folder_uid"`
|
||||||
IsFolder bool
|
IsFolder bool
|
||||||
HasACL bool `xorm:"has_acl"`
|
HasACL bool `xorm:"has_acl"`
|
||||||
|
|
||||||
@ -186,6 +187,7 @@ func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
|
|||||||
dash.PluginID = cmd.PluginID
|
dash.PluginID = cmd.PluginID
|
||||||
dash.IsFolder = cmd.IsFolder
|
dash.IsFolder = cmd.IsFolder
|
||||||
dash.FolderID = cmd.FolderID
|
dash.FolderID = cmd.FolderID
|
||||||
|
dash.FolderUID = cmd.FolderUID
|
||||||
dash.UpdateSlug()
|
dash.UpdateSlug()
|
||||||
return dash
|
return dash
|
||||||
}
|
}
|
||||||
@ -246,6 +248,7 @@ type SaveDashboardCommand struct {
|
|||||||
OrgID int64 `json:"-" xorm:"org_id"`
|
OrgID int64 `json:"-" xorm:"org_id"`
|
||||||
RestoredFrom int `json:"-"`
|
RestoredFrom int `json:"-"`
|
||||||
PluginID string `json:"-" xorm:"plugin_id"`
|
PluginID string `json:"-" xorm:"plugin_id"`
|
||||||
|
// Deprecated: use FolderUID instead
|
||||||
FolderID int64 `json:"folderId" xorm:"folder_id"`
|
FolderID int64 `json:"folderId" xorm:"folder_id"`
|
||||||
FolderUID string `json:"folderUid" xorm:"folder_uid"`
|
FolderUID string `json:"folderUid" xorm:"folder_uid"`
|
||||||
IsFolder bool `json:"isFolder"`
|
IsFolder bool `json:"isFolder"`
|
||||||
|
@ -66,7 +66,7 @@ func TestSaveDashboardCommand_GetDashboardModel(t *testing.T) {
|
|||||||
json := simplejson.New()
|
json := simplejson.New()
|
||||||
json.Set("title", "test dash")
|
json.Set("title", "test dash")
|
||||||
|
|
||||||
cmd := &SaveDashboardCommand{Dashboard: json, FolderID: 1}
|
cmd := &SaveDashboardCommand{Dashboard: json, FolderID: 1, FolderUID: "1"}
|
||||||
dash := cmd.GetDashboardModel()
|
dash := cmd.GetDashboardModel()
|
||||||
|
|
||||||
assert.Equal(t, int64(1), dash.FolderID)
|
assert.Equal(t, int64(1), dash.FolderID)
|
||||||
|
@ -92,6 +92,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(ctx co
|
|||||||
return dr.dashboardStore.GetProvisionedDataByDashboardUID(ctx, orgID, dashboardUID)
|
return dr.dashboardStore.GetProvisionedDataByDashboardUID(ctx, orgID, dashboardUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO, shouldValidateAlerts bool,
|
func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO, shouldValidateAlerts bool,
|
||||||
validateProvisionedDashboard bool) (*dashboards.SaveDashboardCommand, error) {
|
validateProvisionedDashboard bool) (*dashboards.SaveDashboardCommand, error) {
|
||||||
dash := dto.Dashboard
|
dash := dto.Dashboard
|
||||||
@ -193,6 +194,7 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||||||
Overwrite: dto.Overwrite,
|
Overwrite: dto.Overwrite,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
FolderID: dash.FolderID,
|
FolderID: dash.FolderID,
|
||||||
|
FolderUID: dash.FolderUID,
|
||||||
IsFolder: dash.IsFolder,
|
IsFolder: dash.IsFolder,
|
||||||
PluginID: dash.PluginID,
|
PluginID: dash.PluginID,
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.otherSavedFolder.ID,
|
FolderID: sc.otherSavedFolder.ID,
|
||||||
|
FolderUID: sc.otherSavedFolder.UID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -152,6 +153,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInFolder.Title,
|
"title": sc.savedDashInFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedFolder.ID,
|
FolderID: sc.savedFolder.ID,
|
||||||
|
FolderUID: sc.savedFolder.UID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -176,6 +178,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "New dash",
|
"title": "New dash",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedFolder.ID,
|
FolderID: sc.savedFolder.ID,
|
||||||
|
FolderUID: sc.savedFolder.UID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -200,6 +203,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedDashInGeneralFolder.FolderID,
|
FolderID: sc.savedDashInGeneralFolder.FolderID,
|
||||||
|
FolderUID: sc.savedDashInGeneralFolder.FolderUID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -224,6 +228,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedDashInFolder.FolderID,
|
FolderID: sc.savedDashInFolder.FolderID,
|
||||||
|
FolderUID: sc.savedDashInFolder.FolderUID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -248,6 +253,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.otherSavedFolder.ID,
|
FolderID: sc.otherSavedFolder.ID,
|
||||||
|
FolderUID: sc.otherSavedFolder.UID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -272,6 +278,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -296,6 +303,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.otherSavedFolder.ID,
|
FolderID: sc.otherSavedFolder.ID,
|
||||||
|
FolderUID: sc.otherSavedFolder.UID,
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -320,6 +328,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Dash",
|
"title": "Dash",
|
||||||
}),
|
}),
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
UserID: 10000,
|
UserID: 10000,
|
||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
}
|
}
|
||||||
@ -351,6 +360,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInFolder.Title,
|
"title": sc.savedDashInFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +384,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInGeneralFolder.Title,
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedFolder.ID,
|
FolderID: sc.savedFolder.ID,
|
||||||
|
FolderUID: sc.savedFolder.UID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,6 +476,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Expect error",
|
"title": "Expect error",
|
||||||
}),
|
}),
|
||||||
FolderID: 123412321,
|
FolderID: 123412321,
|
||||||
|
FolderUID: "123412321",
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,6 +493,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "test dash 23",
|
"title": "test dash 23",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedFolder.ID,
|
FolderID: sc.savedFolder.ID,
|
||||||
|
FolderUID: sc.savedFolder.UID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,6 +511,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"version": sc.savedDashInGeneralFolder.Version,
|
"version": sc.savedDashInGeneralFolder.Version,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedFolder.ID,
|
FolderID: sc.savedFolder.ID,
|
||||||
|
FolderUID: sc.savedFolder.UID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,6 +535,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "test dash 23",
|
"title": "test dash 23",
|
||||||
}),
|
}),
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,6 +553,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"version": sc.savedDashInFolder.Version,
|
"version": sc.savedDashInFolder.Version,
|
||||||
}),
|
}),
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,6 +576,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInFolder.Title,
|
"title": sc.savedDashInFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedDashInFolder.FolderID,
|
FolderID: sc.savedDashInFolder.FolderID,
|
||||||
|
FolderUID: sc.savedDashInFolder.FolderUID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,6 +593,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInGeneralFolder.Title,
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedDashInGeneralFolder.FolderID,
|
FolderID: sc.savedDashInGeneralFolder.FolderID,
|
||||||
|
FolderUID: sc.savedDashInGeneralFolder.FolderUID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,6 +630,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Updated title",
|
"title": "Updated title",
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedFolder.ID,
|
FolderID: sc.savedFolder.ID,
|
||||||
|
FolderUID: sc.savedFolder.UID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,6 +653,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": "Updated title",
|
"title": "Updated title",
|
||||||
}),
|
}),
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,6 +716,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInFolder.Title,
|
"title": sc.savedDashInFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedDashInFolder.FolderID,
|
FolderID: sc.savedDashInFolder.FolderID,
|
||||||
|
FolderUID: sc.savedDashInFolder.FolderUID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,6 +741,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
|
|||||||
"title": sc.savedDashInGeneralFolder.Title,
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
}),
|
}),
|
||||||
FolderID: sc.savedDashInGeneralFolder.FolderID,
|
FolderID: sc.savedDashInGeneralFolder.FolderID,
|
||||||
|
FolderUID: sc.savedDashInGeneralFolder.FolderUID,
|
||||||
Overwrite: shouldOverwrite,
|
Overwrite: shouldOverwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,9 +898,9 @@ func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionSc
|
|||||||
guardian.InitAccessControlGuardian(cfg, ac, dashboardService)
|
guardian.InitAccessControlGuardian(cfg, ac, dashboardService)
|
||||||
|
|
||||||
savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore)
|
savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore)
|
||||||
savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.ID, sqlStore)
|
savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.ID, savedFolder.UID, sqlStore)
|
||||||
saveTestDashboard(t, "Other saved dash in folder", testOrgID, savedFolder.ID, sqlStore)
|
saveTestDashboard(t, "Other saved dash in folder", testOrgID, savedFolder.ID, savedFolder.UID, sqlStore)
|
||||||
savedDashInGeneralFolder := saveTestDashboard(t, "Saved dashboard in general folder", testOrgID, 0, sqlStore)
|
savedDashInGeneralFolder := saveTestDashboard(t, "Saved dashboard in general folder", testOrgID, 0, "", sqlStore)
|
||||||
otherSavedFolder := saveTestFolder(t, "Other saved folder", testOrgID, sqlStore)
|
otherSavedFolder := saveTestFolder(t, "Other saved folder", testOrgID, sqlStore)
|
||||||
|
|
||||||
require.Equal(t, "Saved folder", savedFolder.Title)
|
require.Equal(t, "Saved folder", savedFolder.Title)
|
||||||
@ -966,12 +988,13 @@ func callSaveWithError(t *testing.T, cmd dashboards.SaveDashboardCommand, sqlSto
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlStore db.DB) *dashboards.Dashboard {
|
func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, folderUID string, sqlStore db.DB) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
FolderID: folderID,
|
FolderID: folderID,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
@ -1016,6 +1039,7 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore db.DB) *da
|
|||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
FolderID: 0,
|
FolderID: 0,
|
||||||
|
FolderUID: "",
|
||||||
IsFolder: true,
|
IsFolder: true,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -2,6 +2,7 @@ package dashverimpl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ func testIntegrationGetDashboardVersion(t *testing.T, fn getStore) {
|
|||||||
dashVerStore := fn(ss)
|
dashVerStore := fn(ss)
|
||||||
|
|
||||||
t.Run("Get a Dashboard ID and version ID", func(t *testing.T) {
|
t.Run("Get a Dashboard ID and version ID", func(t *testing.T) {
|
||||||
savedDash := insertTestDashboard(t, ss, "test dash 26", 1, 0, false, "diff")
|
savedDash := insertTestDashboard(t, ss, "test dash 26", 1, 0, "", false, "diff")
|
||||||
|
|
||||||
query := dashver.GetDashboardVersionQuery{
|
query := dashver.GetDashboardVersionQuery{
|
||||||
DashboardID: savedDash.ID,
|
DashboardID: savedDash.ID,
|
||||||
@ -66,7 +67,7 @@ func testIntegrationGetDashboardVersion(t *testing.T, fn getStore) {
|
|||||||
t.Run("Clean up old dashboard versions", func(t *testing.T) {
|
t.Run("Clean up old dashboard versions", func(t *testing.T) {
|
||||||
versionsToWrite := 10
|
versionsToWrite := 10
|
||||||
for i := 0; i < versionsToWrite-1; i++ {
|
for i := 0; i < versionsToWrite-1; i++ {
|
||||||
insertTestDashboard(t, ss, "test dash 53", 1, int64(i), false, "diff-all")
|
insertTestDashboard(t, ss, "test dash 53", 1, int64(i), strconv.Itoa(i), false, "diff-all")
|
||||||
}
|
}
|
||||||
versionIDsToDelete := []any{1, 2, 3, 4}
|
versionIDsToDelete := []any{1, 2, 3, 4}
|
||||||
res, err := dashVerStore.DeleteBatch(
|
res, err := dashVerStore.DeleteBatch(
|
||||||
@ -78,7 +79,7 @@ func testIntegrationGetDashboardVersion(t *testing.T, fn getStore) {
|
|||||||
assert.EqualValues(t, 4, res)
|
assert.EqualValues(t, 4, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
savedDash := insertTestDashboard(t, ss, "test dash 43", 1, 0, false, "diff-all")
|
savedDash := insertTestDashboard(t, ss, "test dash 43", 1, 0, "", false, "diff-all")
|
||||||
t.Run("Get all versions for a given Dashboard ID", func(t *testing.T) {
|
t.Run("Get all versions for a given Dashboard ID", func(t *testing.T) {
|
||||||
query := dashver.ListDashboardVersionsQuery{
|
query := dashver.ListDashboardVersionsQuery{
|
||||||
DashboardID: savedDash.ID,
|
DashboardID: savedDash.ID,
|
||||||
@ -134,11 +135,12 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func insertTestDashboard(t *testing.T, sqlStore db.DB, title string, orgId int64,
|
func insertTestDashboard(t *testing.T, sqlStore db.DB, title string, orgId int64,
|
||||||
folderId int64, isFolder bool, tags ...any) *dashboards.Dashboard {
|
folderId int64, folderUID string, isFolder bool, tags ...any) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderId,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: isFolder,
|
IsFolder: isFolder,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -37,7 +37,7 @@ func TestIntegrationDashboardFolderStore(t *testing.T) {
|
|||||||
sqlStore = db.InitTestDB(t)
|
sqlStore = db.InitTestDB(t)
|
||||||
folderStore := ProvideDashboardFolderStore(sqlStore)
|
folderStore := ProvideDashboardFolderStore(sqlStore)
|
||||||
folder2 = insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
|
folder2 = insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
|
||||||
_ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.ID, "prod")
|
_ = insertTestDashboard(t, dashboardStore, title, orgId, folder2.ID, folder2.UID, "prod")
|
||||||
folder1 = insertTestFolder(t, dashboardStore, title, orgId, 0, "prod")
|
folder1 = insertTestFolder(t, dashboardStore, title, orgId, 0, "prod")
|
||||||
|
|
||||||
t.Run("GetFolderByTitle should find the folder", func(t *testing.T) {
|
t.Run("GetFolderByTitle should find the folder", func(t *testing.T) {
|
||||||
@ -52,7 +52,7 @@ func TestIntegrationDashboardFolderStore(t *testing.T) {
|
|||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
folderStore := ProvideDashboardFolderStore(sqlStore)
|
folderStore := ProvideDashboardFolderStore(sqlStore)
|
||||||
folder := insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
|
folder := insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
|
||||||
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, "prod")
|
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, folder.UID, "prod")
|
||||||
|
|
||||||
t.Run("should return folder by UID", func(t *testing.T) {
|
t.Run("should return folder by UID", func(t *testing.T) {
|
||||||
d, err := folderStore.GetFolderByUID(context.Background(), orgId, folder.UID)
|
d, err := folderStore.GetFolderByUID(context.Background(), orgId, folder.UID)
|
||||||
@ -76,7 +76,7 @@ func TestIntegrationDashboardFolderStore(t *testing.T) {
|
|||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
folderStore := ProvideDashboardFolderStore(sqlStore)
|
folderStore := ProvideDashboardFolderStore(sqlStore)
|
||||||
folder := insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
|
folder := insertTestFolder(t, dashboardStore, "TEST", orgId, 0, "prod")
|
||||||
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, "prod")
|
dash := insertTestDashboard(t, dashboardStore, "Very Unique Name", orgId, folder.ID, folder.UID, "prod")
|
||||||
|
|
||||||
t.Run("should return folder by ID", func(t *testing.T) {
|
t.Run("should return folder by ID", func(t *testing.T) {
|
||||||
d, err := folderStore.GetFolderByID(context.Background(), orgId, folder.ID)
|
d, err := folderStore.GetFolderByID(context.Background(), orgId, folder.ID)
|
||||||
@ -96,11 +96,12 @@ func TestIntegrationDashboardFolderStore(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64, folderId int64, tags ...any) *dashboards.Dashboard {
|
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64, folderID int64, folderUID string, tags ...any) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderID,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
@ -116,11 +117,12 @@ func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title st
|
|||||||
return dash
|
return dash
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertTestFolder(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64, folderId int64, tags ...any) *dashboards.Dashboard {
|
func insertTestFolder(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64, folderId int64, folderUID string, tags ...any) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderId,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: true,
|
IsFolder: true,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -276,6 +276,9 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dashFolder := dashboards.NewDashboardFolder(cmd.Title)
|
||||||
|
dashFolder.OrgID = cmd.OrgID
|
||||||
|
|
||||||
if s.features.IsEnabled(featuremgmt.FlagNestedFolders) && cmd.ParentUID != "" {
|
if s.features.IsEnabled(featuremgmt.FlagNestedFolders) && cmd.ParentUID != "" {
|
||||||
// Check that the user is allowed to create a subfolder in this folder
|
// Check that the user is allowed to create a subfolder in this folder
|
||||||
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(cmd.ParentUID))
|
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(cmd.ParentUID))
|
||||||
@ -286,11 +289,9 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
if !hasAccess {
|
if !hasAccess {
|
||||||
return nil, dashboards.ErrFolderAccessDenied
|
return nil, dashboards.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
dashFolder.FolderUID = cmd.ParentUID
|
||||||
}
|
}
|
||||||
|
|
||||||
dashFolder := dashboards.NewDashboardFolder(cmd.Title)
|
|
||||||
dashFolder.OrgID = cmd.OrgID
|
|
||||||
|
|
||||||
trimmedUID := strings.TrimSpace(cmd.UID)
|
trimmedUID := strings.TrimSpace(cmd.UID)
|
||||||
if trimmedUID == accesscontrol.GeneralFolderUID {
|
if trimmedUID == accesscontrol.GeneralFolderUID {
|
||||||
return nil, dashboards.ErrFolderInvalidUID
|
return nil, dashboards.ErrFolderInvalidUID
|
||||||
@ -325,7 +326,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
|||||||
User: user,
|
User: user,
|
||||||
}
|
}
|
||||||
|
|
||||||
saveDashboardCmd, err := s.BuildSaveDashboardCommand(ctx, dto)
|
saveDashboardCmd, err := s.buildSaveDashboardCommand(ctx, dto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
@ -419,6 +420,10 @@ func (s *Service) legacyUpdate(ctx context.Context, cmd *folder.UpdateFolderComm
|
|||||||
}
|
}
|
||||||
|
|
||||||
dashFolder := queryResult
|
dashFolder := queryResult
|
||||||
|
if cmd.NewParentUID != nil {
|
||||||
|
dashFolder.FolderUID = *cmd.NewParentUID
|
||||||
|
}
|
||||||
|
|
||||||
currentTitle := dashFolder.Title
|
currentTitle := dashFolder.Title
|
||||||
|
|
||||||
if !dashFolder.IsFolder {
|
if !dashFolder.IsFolder {
|
||||||
@ -447,7 +452,7 @@ func (s *Service) legacyUpdate(ctx context.Context, cmd *folder.UpdateFolderComm
|
|||||||
Overwrite: cmd.Overwrite,
|
Overwrite: cmd.Overwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
saveDashboardCmd, err := s.BuildSaveDashboardCommand(ctx, dto)
|
saveDashboardCmd, err := s.buildSaveDashboardCommand(ctx, dto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
@ -621,7 +626,7 @@ func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*fol
|
|||||||
// if the current folder is already a parent of newparent, we should return error
|
// if the current folder is already a parent of newparent, we should return error
|
||||||
for _, parent := range parents {
|
for _, parent := range parents {
|
||||||
if parent.UID == cmd.UID {
|
if parent.UID == cmd.UID {
|
||||||
return nil, folder.ErrCircularReference
|
return nil, folder.ErrCircularReference.Errorf("failed to move folder")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,12 +634,34 @@ func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*fol
|
|||||||
if cmd.NewParentUID != "" {
|
if cmd.NewParentUID != "" {
|
||||||
newParentUID = cmd.NewParentUID
|
newParentUID = cmd.NewParentUID
|
||||||
}
|
}
|
||||||
return s.store.Update(ctx, folder.UpdateFolderCommand{
|
|
||||||
|
var f *folder.Folder
|
||||||
|
if err := s.db.InTransaction(ctx, func(ctx context.Context) error {
|
||||||
|
if f, err = s.store.Update(ctx, folder.UpdateFolderCommand{
|
||||||
UID: cmd.UID,
|
UID: cmd.UID,
|
||||||
OrgID: cmd.OrgID,
|
OrgID: cmd.OrgID,
|
||||||
NewParentUID: &newParentUID,
|
NewParentUID: &newParentUID,
|
||||||
SignedInUser: cmd.SignedInUser,
|
SignedInUser: cmd.SignedInUser,
|
||||||
})
|
}); err != nil {
|
||||||
|
return folder.ErrInternal.Errorf("failed to move folder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.legacyUpdate(ctx, &folder.UpdateFolderCommand{
|
||||||
|
UID: cmd.UID,
|
||||||
|
OrgID: cmd.OrgID,
|
||||||
|
NewParentUID: &newParentUID,
|
||||||
|
SignedInUser: cmd.SignedInUser,
|
||||||
|
// bypass optimistic locking used for dashboards
|
||||||
|
Overwrite: true,
|
||||||
|
}); err != nil {
|
||||||
|
return folder.ErrInternal.Errorf("failed to move legacy folder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nestedFolderDelete inspects the folder referenced by the cmd argument, deletes all the entries for
|
// nestedFolderDelete inspects the folder referenced by the cmd argument, deletes all the entries for
|
||||||
@ -735,9 +762,9 @@ func (s *Service) getNestedFolders(ctx context.Context, orgID int64, uid string)
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildSaveDashboardCommand is a simplified version on DashboardServiceImpl.BuildSaveDashboardCommand
|
// buildSaveDashboardCommand is a simplified version on DashboardServiceImpl.buildSaveDashboardCommand
|
||||||
// keeping only the meaningful functionality for folders
|
// keeping only the meaningful functionality for folders
|
||||||
func (s *Service) BuildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO) (*dashboards.SaveDashboardCommand, error) {
|
func (s *Service) buildSaveDashboardCommand(ctx context.Context, dto *dashboards.SaveDashboardDTO) (*dashboards.SaveDashboardCommand, error) {
|
||||||
dash := dto.Dashboard
|
dash := dto.Dashboard
|
||||||
|
|
||||||
dash.OrgID = dto.OrgID
|
dash.OrgID = dto.OrgID
|
||||||
@ -807,6 +834,7 @@ func (s *Service) BuildSaveDashboardCommand(ctx context.Context, dto *dashboards
|
|||||||
Overwrite: dto.Overwrite,
|
Overwrite: dto.Overwrite,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
FolderID: dash.FolderID,
|
FolderID: dash.FolderID,
|
||||||
|
FolderUID: dash.FolderUID,
|
||||||
IsFolder: dash.IsFolder,
|
IsFolder: dash.IsFolder,
|
||||||
PluginID: dash.PluginID,
|
PluginID: dash.PluginID,
|
||||||
}
|
}
|
||||||
|
@ -416,8 +416,8 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
subfolder, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
subfolder, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in parent", orgID, parent.ID, "prod")
|
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in parent", orgID, parent.ID, parent.UID, "prod")
|
||||||
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in subfolder", orgID, subfolder.ID, "prod")
|
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in subfolder", orgID, subfolder.ID, subfolder.UID, "prod")
|
||||||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||||
|
|
||||||
@ -491,8 +491,8 @@ func TestIntegrationNestedFolderService(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
subfolder, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
subfolder, err := serviceWithFlagOn.dashboardFolderStore.GetFolderByUID(context.Background(), orgID, ancestorUIDs[1])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in parent", orgID, parent.ID, "prod")
|
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in parent", orgID, parent.ID, parent.UID, "prod")
|
||||||
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in subfolder", orgID, subfolder.ID, "prod")
|
_ = insertTestDashboard(t, serviceWithFlagOn.dashboardStore, "dashboard in subfolder", orgID, subfolder.ID, subfolder.UID, "prod")
|
||||||
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
_ = createRule(t, alertStore, parent.UID, "parent alert")
|
||||||
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
_ = createRule(t, alertStore, subfolder.UID, "sub alert")
|
||||||
|
|
||||||
@ -996,9 +996,10 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("newFolder")}}
|
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersWrite: {dashboards.ScopeFoldersProvider.GetResourceScopeUID("newFolder")}}
|
||||||
|
|
||||||
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), acimpl.ProvideAccessControl(setting.NewCfg()), dbtest.NewFakeDB())
|
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), acimpl.ProvideAccessControl(setting.NewCfg()), dbtest.NewFakeDB())
|
||||||
f, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: nestedFolderUser})
|
_, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "newFolder", OrgID: orgID, SignedInUser: nestedFolderUser})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, f)
|
// the folder is set inside InTransaction() but the fake one is called
|
||||||
|
// require.NotNil(t, f)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("move to the root folder without folder creation permissions fails", func(t *testing.T) {
|
t.Run("move to the root folder without folder creation permissions fails", func(t *testing.T) {
|
||||||
@ -1032,9 +1033,10 @@ func TestNestedFolderService(t *testing.T) {
|
|||||||
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersCreate: {}}
|
nestedFolderUser.Permissions[orgID] = map[string][]string{dashboards.ActionFoldersCreate: {}}
|
||||||
|
|
||||||
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), acimpl.ProvideAccessControl(setting.NewCfg()), dbtest.NewFakeDB())
|
folderSvc := setup(t, dashStore, dashboardFolderStore, nestedFolderStore, featuremgmt.WithFeatures("nestedFolders"), acimpl.ProvideAccessControl(setting.NewCfg()), dbtest.NewFakeDB())
|
||||||
f, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "", OrgID: orgID, SignedInUser: nestedFolderUser})
|
_, err := folderSvc.Move(context.Background(), &folder.MoveFolderCommand{UID: "myFolder", NewParentUID: "", OrgID: orgID, SignedInUser: nestedFolderUser})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, f)
|
// the folder is set inside InTransaction() but the fake one is called
|
||||||
|
// require.NotNil(t, f)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("move when parentUID in the current subtree returns error from nested folder service", func(t *testing.T) {
|
t.Run("move when parentUID in the current subtree returns error from nested folder service", func(t *testing.T) {
|
||||||
|
@ -291,6 +291,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
|
|||||||
saveDashboardCmd := dashboards.SaveDashboardCommand{
|
saveDashboardCmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
FolderID: 1,
|
FolderID: 1,
|
||||||
|
FolderUID: "1",
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -63,9 +63,9 @@ func TestIntegrationListPublicDashboard(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore = ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
|
|
||||||
bDash = insertTestDashboard(t, dashboardStore, "b", orgId, 0, false)
|
bDash = insertTestDashboard(t, dashboardStore, "b", orgId, 0, "", false)
|
||||||
aDash = insertTestDashboard(t, dashboardStore, "a", orgId, 0, false)
|
aDash = insertTestDashboard(t, dashboardStore, "a", orgId, 0, "", false)
|
||||||
cDash = insertTestDashboard(t, dashboardStore, "c", orgId, 0, false)
|
cDash = insertTestDashboard(t, dashboardStore, "c", orgId, 0, "", false)
|
||||||
|
|
||||||
// these are in order of how they should be returned from ListPUblicDashboards
|
// these are in order of how they should be returned from ListPUblicDashboards
|
||||||
aPublicDash = insertPublicDashboard(t, publicdashboardStore, aDash.UID, orgId, false, PublicShareType)
|
aPublicDash = insertPublicDashboard(t, publicdashboardStore, aDash.UID, orgId, false, PublicShareType)
|
||||||
@ -178,7 +178,7 @@ func TestIntegrationFindDashboard(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardStore = store
|
dashboardStore = store
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("FindDashboard can get original dashboard by uid", func(t *testing.T) {
|
t.Run("FindDashboard can get original dashboard by uid", func(t *testing.T) {
|
||||||
@ -208,7 +208,7 @@ func TestIntegrationExistsEnabledByAccessToken(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardStore = store
|
dashboardStore = store
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
t.Run("ExistsEnabledByAccessToken will return true when at least one public dashboard has a matching access token", func(t *testing.T) {
|
t.Run("ExistsEnabledByAccessToken will return true when at least one public dashboard has a matching access token", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
@ -281,7 +281,7 @@ func TestIntegrationExistsEnabledByDashboardUid(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardStore = store
|
dashboardStore = store
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("ExistsEnabledByDashboardUid Will return true when dashboard has at least one enabled public dashboard", func(t *testing.T) {
|
t.Run("ExistsEnabledByDashboardUid Will return true when dashboard has at least one enabled public dashboard", func(t *testing.T) {
|
||||||
@ -346,7 +346,7 @@ func TestIntegrationFindByDashboardUid(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardStore = store
|
dashboardStore = store
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("returns public dashboard by dashboardUid", func(t *testing.T) {
|
t.Run("returns public dashboard by dashboardUid", func(t *testing.T) {
|
||||||
@ -413,7 +413,7 @@ func TestIntegrationFindByAccessToken(t *testing.T) {
|
|||||||
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil))
|
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("returns public dashboard by accessToken", func(t *testing.T) {
|
t.Run("returns public dashboard by accessToken", func(t *testing.T) {
|
||||||
@ -483,8 +483,8 @@ func TestIntegrationCreatePublicDashboard(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardStore = store
|
dashboardStore = store
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, true)
|
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, "", true)
|
||||||
insertPublicDashboard(t, publicdashboardStore, savedDashboard2.UID, savedDashboard2.OrgID, false, PublicShareType)
|
insertPublicDashboard(t, publicdashboardStore, savedDashboard2.UID, savedDashboard2.OrgID, false, PublicShareType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,8 +562,8 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
anotherSavedDashboard = insertTestDashboard(t, dashboardStore, "test another Dashie", 1, 0, true)
|
anotherSavedDashboard = insertTestDashboard(t, dashboardStore, "test another Dashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("updates an existing dashboard", func(t *testing.T) {
|
t.Run("updates an existing dashboard", func(t *testing.T) {
|
||||||
@ -666,7 +666,7 @@ func TestIntegrationGetOrgIdByAccessToken(t *testing.T) {
|
|||||||
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
}
|
}
|
||||||
t.Run("GetOrgIdByAccessToken will OrgId when enabled", func(t *testing.T) {
|
t.Run("GetOrgIdByAccessToken will OrgId when enabled", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
@ -738,7 +738,7 @@ func TestIntegrationDelete(t *testing.T) {
|
|||||||
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil))
|
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true)
|
||||||
savedPublicDashboard = insertPublicDashboard(t, publicdashboardStore, savedDashboard.UID, savedDashboard.OrgID, true, PublicShareType)
|
savedPublicDashboard = insertPublicDashboard(t, publicdashboardStore, savedDashboard.UID, savedDashboard.OrgID, true, PublicShareType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -790,9 +790,9 @@ func TestGetDashboardByFolder(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pubdashStore := ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
pubdashStore := ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "title", 1, 1, true, PublicShareType)
|
dashboard := insertTestDashboard(t, dashboardStore, "title", 1, 1, "1", true, PublicShareType)
|
||||||
pubdash := insertPublicDashboard(t, pubdashStore, dashboard.UID, dashboard.OrgID, true, PublicShareType)
|
pubdash := insertPublicDashboard(t, pubdashStore, dashboard.UID, dashboard.OrgID, true, PublicShareType)
|
||||||
dashboard2 := insertTestDashboard(t, dashboardStore, "title", 1, 2, true, PublicShareType)
|
dashboard2 := insertTestDashboard(t, dashboardStore, "title", 1, 2, "2", true, PublicShareType)
|
||||||
_ = insertPublicDashboard(t, pubdashStore, dashboard2.UID, dashboard2.OrgID, true, PublicShareType)
|
_ = insertPublicDashboard(t, pubdashStore, dashboard2.UID, dashboard2.OrgID, true, PublicShareType)
|
||||||
|
|
||||||
pubdashes, err := pubdashStore.FindByDashboardFolder(context.Background(), dashboard)
|
pubdashes, err := pubdashStore.FindByDashboardFolder(context.Background(), dashboard)
|
||||||
@ -823,10 +823,10 @@ func TestGetMetrics(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboardStore = store
|
dashboardStore = store
|
||||||
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
|
||||||
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, false)
|
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", false)
|
||||||
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, false)
|
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, "", false)
|
||||||
savedDashboard3 = insertTestDashboard(t, dashboardStore, "testDashie3", 2, 0, false)
|
savedDashboard3 = insertTestDashboard(t, dashboardStore, "testDashie3", 2, 0, "", false)
|
||||||
savedDashboard4 = insertTestDashboard(t, dashboardStore, "testDashie4", 2, 0, false)
|
savedDashboard4 = insertTestDashboard(t, dashboardStore, "testDashie4", 2, 0, "", false)
|
||||||
insertPublicDashboard(t, publicdashboardStore, savedDashboard.UID, savedDashboard.OrgID, true, PublicShareType)
|
insertPublicDashboard(t, publicdashboardStore, savedDashboard.UID, savedDashboard.OrgID, true, PublicShareType)
|
||||||
insertPublicDashboard(t, publicdashboardStore, savedDashboard2.UID, savedDashboard2.OrgID, true, PublicShareType)
|
insertPublicDashboard(t, publicdashboardStore, savedDashboard2.UID, savedDashboard2.OrgID, true, PublicShareType)
|
||||||
insertPublicDashboard(t, publicdashboardStore, savedDashboard3.UID, savedDashboard3.OrgID, true, EmailShareType)
|
insertPublicDashboard(t, publicdashboardStore, savedDashboard3.UID, savedDashboard3.OrgID, true, EmailShareType)
|
||||||
@ -868,11 +868,12 @@ func TestGetMetrics(t *testing.T) {
|
|||||||
|
|
||||||
// helper function to insert a dashboard
|
// helper function to insert a dashboard
|
||||||
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
||||||
folderId int64, isFolder bool, tags ...any) *dashboards.Dashboard {
|
folderId int64, folderUID string, isFolder bool, tags ...any) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderId,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: isFolder,
|
IsFolder: isFolder,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -721,7 +721,7 @@ func TestGetQueryDataResponse(t *testing.T) {
|
|||||||
"targets": []interface{}{hiddenQuery},
|
"targets": []interface{}{hiddenQuery},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashWithHiddenQuery", 1, 0, true, []map[string]interface{}{}, customPanels)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashWithHiddenQuery", 1, 0, "", true, []map[string]interface{}{}, customPanels)
|
||||||
isEnabled := true
|
isEnabled := true
|
||||||
dto := &SavePublicDashboardDTO{
|
dto := &SavePublicDashboardDTO{
|
||||||
DashboardUid: dashboard.UID,
|
DashboardUid: dashboard.UID,
|
||||||
@ -1130,7 +1130,7 @@ func TestGetMetricRequest(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]interface{}{}, nil)
|
||||||
publicDashboard := &PublicDashboard{
|
publicDashboard := &PublicDashboard{
|
||||||
Uid: "1",
|
Uid: "1",
|
||||||
DashboardUid: dashboard.UID,
|
DashboardUid: dashboard.UID,
|
||||||
@ -1216,8 +1216,8 @@ func TestBuildMetricRequest(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
|
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]interface{}{}, nil)
|
||||||
nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 1, 0, true, []map[string]interface{}{}, nil)
|
nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 1, 0, "", true, []map[string]interface{}{}, nil)
|
||||||
from, to := internal.GetTimeRangeFromDashboard(t, publicDashboard.Data)
|
from, to := internal.GetTimeRangeFromDashboard(t, publicDashboard.Data)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -1343,7 +1343,7 @@ func TestBuildMetricRequest(t *testing.T) {
|
|||||||
"targets": []interface{}{hiddenQuery, nonHiddenQuery},
|
"targets": []interface{}{hiddenQuery, nonHiddenQuery},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashWithHiddenQuery", 1, 0, true, []map[string]interface{}{}, customPanels)
|
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashWithHiddenQuery", 1, 0, "", true, []map[string]interface{}{}, customPanels)
|
||||||
|
|
||||||
reqDTO, err := service.buildMetricRequest(
|
reqDTO, err := service.buildMetricRequest(
|
||||||
publicDashboard,
|
publicDashboard,
|
||||||
@ -1375,7 +1375,7 @@ func TestBuildAnonymousUser(t *testing.T) {
|
|||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]interface{}{}, nil)
|
||||||
|
|
||||||
t.Run("will add datasource read and query permissions to user for each datasource in dashboard", func(t *testing.T) {
|
t.Run("will add datasource read and query permissions to user for each datasource in dashboard", func(t *testing.T) {
|
||||||
user := buildAnonymousUser(context.Background(), dashboard)
|
user := buildAnonymousUser(context.Background(), dashboard)
|
||||||
|
@ -594,7 +594,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -680,7 +680,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -718,7 +718,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -752,7 +752,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
templateVars := make([]map[string]any, 1)
|
templateVars := make([]map[string]any, 1)
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, templateVars, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, templateVars, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -827,7 +827,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -905,7 +905,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]interface{}{}, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -966,7 +966,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
|
|
||||||
publicdashboardStore := &FakePublicDashboardStore{}
|
publicdashboardStore := &FakePublicDashboardStore{}
|
||||||
publicdashboardStore.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(&PublicDashboard{Uid: "newPubdashUid"}, nil)
|
publicdashboardStore.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(&PublicDashboard{Uid: "newPubdashUid"}, nil)
|
||||||
@ -1003,7 +1003,7 @@ func TestCreatePublicDashboard(t *testing.T) {
|
|||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
@ -1047,8 +1047,8 @@ func TestUpdatePublicDashboard(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
dashboard2 := insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, true, []map[string]any{}, nil)
|
dashboard2 := insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
log: log.New("test.logger"),
|
log: log.New("test.logger"),
|
||||||
@ -1233,7 +1233,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
|
||||||
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
|
||||||
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]any{}, nil)
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, "", true, []map[string]any{}, nil)
|
||||||
|
|
||||||
service := &PublicDashboardServiceImpl{
|
service := &PublicDashboardServiceImpl{
|
||||||
log: log.New("test.logger"),
|
log: log.New("test.logger"),
|
||||||
@ -1803,7 +1803,7 @@ func AddAnnotationsToDashboard(t *testing.T, dash *dashboards.Dashboard, annotat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
|
||||||
folderId int64, isFolder bool, templateVars []map[string]any, customPanels []any, tags ...any) *dashboards.Dashboard {
|
folderId int64, folderUID string, isFolder bool, templateVars []map[string]any, customPanels []any, tags ...any) *dashboards.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
var dashboardPanels []any
|
var dashboardPanels []any
|
||||||
@ -1854,6 +1854,7 @@ func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title st
|
|||||||
cmd := dashboards.SaveDashboardCommand{
|
cmd := dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgId,
|
OrgID: orgId,
|
||||||
FolderID: folderId,
|
FolderID: folderId,
|
||||||
|
FolderUID: folderUID,
|
||||||
IsFolder: isFolder,
|
IsFolder: isFolder,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
dashboardFolderMigrations "github.com/grafana/grafana/pkg/services/dashboards/database/migrations"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrations/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations/anonservice"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrations/anonservice"
|
||||||
@ -103,6 +104,8 @@ func (*OSSMigrations) AddMigration(mg *Migrator) {
|
|||||||
|
|
||||||
ualert.MigrationServiceMigration(mg)
|
ualert.MigrationServiceMigration(mg)
|
||||||
ualert.CreatedFoldersMigration(mg)
|
ualert.CreatedFoldersMigration(mg)
|
||||||
|
|
||||||
|
dashboardFolderMigrations.AddDashboardFolderMigrations(mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addStarMigrations(mg *Migrator) {
|
func addStarMigrations(mg *Migrator) {
|
||||||
|
@ -817,6 +817,7 @@ func setupNestedTest(t *testing.T, usr *user.SignedInUser, perms []accesscontrol
|
|||||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
FolderID: parent.ID,
|
FolderID: parent.ID,
|
||||||
|
FolderUID: parent.UID,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"title": "dashboard under parent folder",
|
"title": "dashboard under parent folder",
|
||||||
}),
|
}),
|
||||||
@ -827,6 +828,7 @@ func setupNestedTest(t *testing.T, usr *user.SignedInUser, perms []accesscontrol
|
|||||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
FolderID: subfolder.ID,
|
FolderID: subfolder.ID,
|
||||||
|
FolderUID: subfolder.UID,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||||
"title": "dashboard under subfolder",
|
"title": "dashboard under subfolder",
|
||||||
}),
|
}),
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/search/model"
|
"github.com/grafana/grafana/pkg/services/search/model"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
)
|
)
|
||||||
@ -16,6 +17,7 @@ type Builder struct {
|
|||||||
// to modify the query.
|
// to modify the query.
|
||||||
Filters []any
|
Filters []any
|
||||||
Dialect migrator.Dialect
|
Dialect migrator.Dialect
|
||||||
|
Features featuremgmt.FeatureToggles
|
||||||
|
|
||||||
params []any
|
params []any
|
||||||
sql bytes.Buffer
|
sql bytes.Buffer
|
||||||
@ -35,8 +37,14 @@ func (b *Builder) ToSQL(limit, page int64) (string, []any) {
|
|||||||
INNER JOIN dashboard ON ids.id = dashboard.id`)
|
INNER JOIN dashboard ON ids.id = dashboard.id`)
|
||||||
b.sql.WriteString("\n")
|
b.sql.WriteString("\n")
|
||||||
|
|
||||||
|
if b.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||||
b.sql.WriteString(
|
b.sql.WriteString(
|
||||||
`LEFT OUTER JOIN dashboard AS folder ON folder.id = dashboard.folder_id
|
`LEFT OUTER JOIN folder ON folder.uid = dashboard.folder_uid AND folder.org_id = dashboard.org_id`)
|
||||||
|
} else {
|
||||||
|
b.sql.WriteString(`
|
||||||
|
LEFT OUTER JOIN dashboard AS folder ON folder.id = dashboard.folder_id`)
|
||||||
|
}
|
||||||
|
b.sql.WriteString(`
|
||||||
LEFT OUTER JOIN dashboard_tag ON dashboard.id = dashboard_tag.dashboard_id`)
|
LEFT OUTER JOIN dashboard_tag ON dashboard.id = dashboard_tag.dashboard_id`)
|
||||||
b.sql.WriteString("\n")
|
b.sql.WriteString("\n")
|
||||||
b.sql.WriteString(orderQuery)
|
b.sql.WriteString(orderQuery)
|
||||||
@ -58,7 +66,15 @@ func (b *Builder) buildSelect() {
|
|||||||
dashboard.is_folder,
|
dashboard.is_folder,
|
||||||
dashboard.folder_id,
|
dashboard.folder_id,
|
||||||
folder.uid AS folder_uid,
|
folder.uid AS folder_uid,
|
||||||
folder.slug AS folder_slug,
|
`)
|
||||||
|
if b.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||||
|
b.sql.WriteString(`
|
||||||
|
folder.title AS folder_slug,`)
|
||||||
|
} else {
|
||||||
|
b.sql.WriteString(`
|
||||||
|
folder.slug AS folder_slug,`)
|
||||||
|
}
|
||||||
|
b.sql.WriteString(`
|
||||||
folder.title AS folder_title `)
|
folder.title AS folder_title `)
|
||||||
|
|
||||||
for _, f := range b.Filters {
|
for _, f := range b.Filters {
|
||||||
|
@ -61,6 +61,7 @@ type FolderUIDFilter struct {
|
|||||||
Dialect migrator.Dialect
|
Dialect migrator.Dialect
|
||||||
OrgID int64
|
OrgID int64
|
||||||
UIDs []string
|
UIDs []string
|
||||||
|
NestedFoldersEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FolderUIDFilter) Where() (string, []any) {
|
func (f FolderUIDFilter) Where() (string, []any) {
|
||||||
@ -84,10 +85,16 @@ func (f FolderUIDFilter) Where() (string, []any) {
|
|||||||
// do nothing
|
// do nothing
|
||||||
case len(params) == 1:
|
case len(params) == 1:
|
||||||
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid = ?)"
|
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid = ?)"
|
||||||
|
if f.NestedFoldersEnabled {
|
||||||
|
q = "dashboard.org_id = ? AND dashboard.folder_uid = ?"
|
||||||
|
}
|
||||||
params = append([]any{f.OrgID}, params...)
|
params = append([]any{f.OrgID}, params...)
|
||||||
default:
|
default:
|
||||||
sqlArray := "(?" + strings.Repeat(",?", len(params)-1) + ")"
|
sqlArray := "(?" + strings.Repeat(",?", len(params)-1) + ")"
|
||||||
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN " + sqlArray + ")"
|
q = "dashboard.folder_id IN (SELECT id FROM dashboard WHERE org_id = ? AND uid IN " + sqlArray + ")"
|
||||||
|
if f.NestedFoldersEnabled {
|
||||||
|
q = "dashboard.org_id = ? AND dashboard.folder_uid IN " + sqlArray
|
||||||
|
}
|
||||||
params = append([]any{f.OrgID}, params...)
|
params = append([]any{f.OrgID}, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
|
|||||||
searchstore.TitleSorter{},
|
searchstore.TitleSorter{},
|
||||||
},
|
},
|
||||||
Dialect: store.GetDialect(),
|
Dialect: store.GetDialect(),
|
||||||
|
Features: featuremgmt.WithFeatures(),
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []dashboards.DashboardSearchProjection{}
|
res := []dashboards.DashboardSearchProjection{}
|
||||||
@ -84,6 +85,7 @@ func TestBuilder_Pagination(t *testing.T) {
|
|||||||
searchstore.TitleSorter{},
|
searchstore.TitleSorter{},
|
||||||
},
|
},
|
||||||
Dialect: store.GetDialect(),
|
Dialect: store.GetDialect(),
|
||||||
|
Features: featuremgmt.WithFeatures(),
|
||||||
}
|
}
|
||||||
|
|
||||||
resPg1 := []dashboards.DashboardSearchProjection{}
|
resPg1 := []dashboards.DashboardSearchProjection{}
|
||||||
@ -244,6 +246,7 @@ func TestBuilder_RBAC(t *testing.T) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
Dialect: store.GetDialect(),
|
Dialect: store.GetDialect(),
|
||||||
|
Features: features,
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []dashboards.DashboardSearchProjection{}
|
res := []dashboards.DashboardSearchProjection{}
|
||||||
|
Loading…
Reference in New Issue
Block a user