PluginManager: Make Plugins, Renderer and DataSources non-global (#31866)
* PluginManager: Make Plugins and DataSources non-global Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix integration tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Replace outdated command Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * DashboardService: Ensure it gets constructed with necessary parameters Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix build Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * DashboardService: Ensure it gets constructed with necessary parameters Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove dead code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove FocusConvey Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Remove dead code Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Undo interface changes Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Backend: Move tsdbifaces.RequestHandler to plugins.DataRequestHandler Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Rename to DataSourceCount Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Consolidate dashboard interfaces into one Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Fix dashboard integration tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/models"
|
||||||
|
|
||||||
|
// updateDashboardACL updates a dashboard's ACL items.
|
||||||
|
//
|
||||||
|
// Stubbable by tests.
|
||||||
|
var updateDashboardACL = func(hs *HTTPServer, dashID int64, items []*models.DashboardAcl) error {
|
||||||
|
return hs.SQLStore.UpdateDashboardACL(dashID, items)
|
||||||
|
}
|
||||||
+5
-5
@@ -266,7 +266,7 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
apiRoute.Get("/datasources/id/:name", routing.Wrap(GetDataSourceIdByName), reqSignedIn)
|
apiRoute.Get("/datasources/id/:name", routing.Wrap(GetDataSourceIdByName), reqSignedIn)
|
||||||
|
|
||||||
apiRoute.Get("/plugins", routing.Wrap(hs.GetPluginList))
|
apiRoute.Get("/plugins", routing.Wrap(hs.GetPluginList))
|
||||||
apiRoute.Get("/plugins/:pluginId/settings", routing.Wrap(GetPluginSettingByID))
|
apiRoute.Get("/plugins/:pluginId/settings", routing.Wrap(hs.GetPluginSettingByID))
|
||||||
apiRoute.Get("/plugins/:pluginId/markdown/:name", routing.Wrap(hs.GetPluginMarkdown))
|
apiRoute.Get("/plugins/:pluginId/markdown/:name", routing.Wrap(hs.GetPluginMarkdown))
|
||||||
apiRoute.Get("/plugins/:pluginId/health", routing.Wrap(hs.CheckHealth))
|
apiRoute.Get("/plugins/:pluginId/health", routing.Wrap(hs.CheckHealth))
|
||||||
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
|
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
|
||||||
@@ -288,13 +288,13 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
|
|
||||||
// Folders
|
// Folders
|
||||||
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
|
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
|
||||||
folderRoute.Get("/", routing.Wrap(GetFolders))
|
folderRoute.Get("/", routing.Wrap(hs.GetFolders))
|
||||||
folderRoute.Get("/id/:id", routing.Wrap(GetFolderByID))
|
folderRoute.Get("/id/:id", routing.Wrap(hs.GetFolderByID))
|
||||||
folderRoute.Post("/", bind(models.CreateFolderCommand{}), routing.Wrap(hs.CreateFolder))
|
folderRoute.Post("/", bind(models.CreateFolderCommand{}), routing.Wrap(hs.CreateFolder))
|
||||||
|
|
||||||
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
|
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
|
||||||
folderUidRoute.Get("/", routing.Wrap(GetFolderByUID))
|
folderUidRoute.Get("/", routing.Wrap(hs.GetFolderByUID))
|
||||||
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), routing.Wrap(UpdateFolder))
|
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), routing.Wrap(hs.UpdateFolder))
|
||||||
folderUidRoute.Delete("/", routing.Wrap(hs.DeleteFolder))
|
folderUidRoute.Delete("/", routing.Wrap(hs.DeleteFolder))
|
||||||
|
|
||||||
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
|
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
|
||||||
@@ -122,7 +121,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
|
|||||||
meta.FolderUrl = query.Result.GetUrl()
|
meta.FolderUrl = query.Result.GetUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := dashboards.NewProvisioningService()
|
svc := dashboards.NewProvisioningService(hs.SQLStore)
|
||||||
provisioningData, err := svc.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
provisioningData, err := svc.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
||||||
@@ -227,7 +226,7 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := dashboards.NewService()
|
svc := dashboards.NewService(hs.SQLStore)
|
||||||
err := svc.DeleteDashboard(dash.Id, c.OrgId)
|
err := svc.DeleteDashboard(dash.Id, c.OrgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var dashboardErr models.DashboardErr
|
var dashboardErr models.DashboardErr
|
||||||
@@ -264,7 +263,7 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := dashboards.NewProvisioningService()
|
svc := dashboards.NewProvisioningService(hs.SQLStore)
|
||||||
provisioningData, err := svc.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
provisioningData, err := svc.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
return response.Error(500, "Error while checking if dashboard is provisioned", err)
|
||||||
@@ -291,15 +290,15 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
|||||||
Overwrite: cmd.Overwrite,
|
Overwrite: cmd.Overwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
dashSvc := dashboards.NewService()
|
dashSvc := dashboards.NewService(hs.SQLStore)
|
||||||
dashboard, err := dashSvc.SaveDashboard(dashItem, allowUiUpdate)
|
dashboard, err := dashSvc.SaveDashboard(dashItem, allowUiUpdate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dashboardSaveErrorToApiResponse(err)
|
return hs.dashboardSaveErrorToApiResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.Cfg.EditorsCanAdmin && newDashboard {
|
if hs.Cfg.EditorsCanAdmin && newDashboard {
|
||||||
inFolder := cmd.FolderId > 0
|
inFolder := cmd.FolderId > 0
|
||||||
err := dashboards.MakeUserAdmin(hs.Bus, cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder)
|
err := dashSvc.MakeUserAdmin(cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hs.log.Error("Could not make user admin", "dashboard", dashboard.Title, "user", c.SignedInUser.UserId, "error", err)
|
hs.log.Error("Could not make user admin", "dashboard", dashboard.Title, "user", c.SignedInUser.UserId, "error", err)
|
||||||
}
|
}
|
||||||
@@ -335,7 +334,7 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func dashboardSaveErrorToApiResponse(err error) response.Response {
|
func (hs *HTTPServer) dashboardSaveErrorToApiResponse(err error) response.Response {
|
||||||
var dashboardErr models.DashboardErr
|
var dashboardErr models.DashboardErr
|
||||||
if ok := errors.As(err, &dashboardErr); ok {
|
if ok := errors.As(err, &dashboardErr); ok {
|
||||||
if body := dashboardErr.Body(); body != nil {
|
if body := dashboardErr.Body(); body != nil {
|
||||||
@@ -360,7 +359,7 @@ func dashboardSaveErrorToApiResponse(err error) response.Response {
|
|||||||
if ok := errors.As(err, &pluginErr); ok {
|
if ok := errors.As(err, &pluginErr); ok {
|
||||||
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
||||||
// look up plugin name
|
// look up plugin name
|
||||||
if pluginDef, exist := manager.Plugins[pluginErr.PluginId]; exist {
|
if pluginDef := hs.PluginManager.GetPlugin(pluginErr.PluginId); pluginDef != nil {
|
||||||
message = fmt.Sprintf("The dashboard belongs to plugin %s.", pluginDef.Name)
|
message = fmt.Sprintf("The dashboard belongs to plugin %s.", pluginDef.Name)
|
||||||
}
|
}
|
||||||
return response.JSON(412, util.DynMap{"status": "plugin-dashboard", "message": message})
|
return response.JSON(412, util.DynMap{"status": "plugin-dashboard", "message": message})
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
)
|
)
|
||||||
@@ -68,11 +67,9 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dt
|
|||||||
return dashboardGuardianResponse(err)
|
return dashboardGuardianResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := models.UpdateDashboardAclCommand{}
|
var items []*models.DashboardAcl
|
||||||
cmd.DashboardID = dashID
|
|
||||||
|
|
||||||
for _, item := range apiCmd.Items {
|
for _, item := range apiCmd.Items {
|
||||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
items = append(items, &models.DashboardAcl{
|
||||||
OrgID: c.OrgId,
|
OrgID: c.OrgId,
|
||||||
DashboardID: dashID,
|
DashboardID: dashID,
|
||||||
UserID: item.UserID,
|
UserID: item.UserID,
|
||||||
@@ -88,9 +85,9 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Error while retrieving hidden permissions", err)
|
return response.Error(500, "Error while retrieving hidden permissions", err)
|
||||||
}
|
}
|
||||||
cmd.Items = append(cmd.Items, hiddenACL...)
|
items = append(items, hiddenACL...)
|
||||||
|
|
||||||
if okToUpdate, err := g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, cmd.Items); err != nil || !okToUpdate {
|
if okToUpdate, err := g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, items); err != nil || !okToUpdate {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, guardian.ErrGuardianPermissionExists) || errors.Is(err, guardian.ErrGuardianOverride) {
|
if errors.Is(err, guardian.ErrGuardianPermissionExists) || errors.Is(err, guardian.ErrGuardianOverride) {
|
||||||
return response.Error(400, err.Error(), err)
|
return response.Error(400, err.Error(), err)
|
||||||
@@ -102,7 +99,7 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dt
|
|||||||
return response.Error(403, "Cannot remove own admin permission for a folder", nil)
|
return response.Error(403, "Cannot remove own admin permission for a folder", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bus.Dispatch(&cmd); err != nil {
|
if err := updateDashboardACL(hs, dashID, items); err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardAclInfoMissing) ||
|
if errors.Is(err, models.ErrDashboardAclInfoMissing) ||
|
||||||
errors.Is(err, models.ErrDashboardPermissionDashboardEmpty) {
|
errors.Is(err, models.ErrDashboardPermissionDashboardEmpty) {
|
||||||
return response.Error(409, err.Error(), err)
|
return response.Error(409, err.Error(), err)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
callUpdateDashboardPermissions(sc)
|
callUpdateDashboardPermissions(t, sc)
|
||||||
assert.Equal(t, 404, sc.resp.Code)
|
assert.Equal(t, 404, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -91,7 +91,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
callUpdateDashboardPermissions(sc)
|
callUpdateDashboardPermissions(t, sc)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -151,7 +151,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
callUpdateDashboardPermissions(sc)
|
callUpdateDashboardPermissions(t, sc)
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -189,7 +189,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
callUpdateDashboardPermissions(sc)
|
callUpdateDashboardPermissions(t, sc)
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -217,7 +217,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/dashboards/id/:id/permissions",
|
routePattern: "/api/dashboards/id/:id/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateDashboardPermissions(sc)
|
callUpdateDashboardPermissions(t, sc)
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -260,7 +260,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
callUpdateDashboardPermissions(sc)
|
callUpdateDashboardPermissions(t, sc)
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -335,13 +335,20 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
// TODO: Replace this fake with a fake SQLStore instead (once we can use an interface in its stead)
|
||||||
assert.Len(t, cmd.Items, 4)
|
origUpdateDashboardACL := updateDashboardACL
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
updateDashboardACL = origUpdateDashboardACL
|
||||||
})
|
})
|
||||||
|
var gotItems []*models.DashboardAcl
|
||||||
|
updateDashboardACL = func(hs *HTTPServer, folderID int64, items []*models.DashboardAcl) error {
|
||||||
|
gotItems = items
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
|
assert.Len(t, gotItems, 4)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
})
|
})
|
||||||
@@ -353,10 +360,16 @@ func callGetDashboardPermissions(sc *scenarioContext, hs *HTTPServer) {
|
|||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
func callUpdateDashboardPermissions(sc *scenarioContext) {
|
func callUpdateDashboardPermissions(t *testing.T, sc *scenarioContext) {
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
t.Helper()
|
||||||
return nil
|
|
||||||
|
origUpdateDashboardACL := updateDashboardACL
|
||||||
|
t.Cleanup(func() {
|
||||||
|
updateDashboardACL = origUpdateDashboardACL
|
||||||
})
|
})
|
||||||
|
updateDashboardACL = func(hs *HTTPServer, dashID int64, items []*models.DashboardAcl) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-61
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@@ -28,7 +29,10 @@ func TestGetHomeDashboard(t *testing.T) {
|
|||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
cfg.StaticRootPath = "../../public/"
|
cfg.StaticRootPath = "../../public/"
|
||||||
|
|
||||||
hs := &HTTPServer{Cfg: cfg, Bus: bus.New()}
|
hs := &HTTPServer{
|
||||||
|
Cfg: cfg, Bus: bus.New(),
|
||||||
|
PluginManager: &fakePluginManager{},
|
||||||
|
}
|
||||||
hs.Bus.AddHandler(func(query *models.GetPreferencesWithDefaultsQuery) error {
|
hs.Bus.AddHandler(func(query *models.GetPreferencesWithDefaultsQuery) error {
|
||||||
query.Result = &models.Preferences{
|
query.Result = &models.Preferences{
|
||||||
HomeDashboardId: 0,
|
HomeDashboardId: 0,
|
||||||
@@ -102,11 +106,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
|
||||||
query.Result = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
viewerRole := models.ROLE_VIEWER
|
viewerRole := models.ROLE_VIEWER
|
||||||
editorRole := models.ROLE_EDITOR
|
editorRole := models.ROLE_EDITOR
|
||||||
|
|
||||||
@@ -165,7 +164,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
|
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
@@ -175,7 +174,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
|
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
@@ -230,7 +229,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -239,7 +238,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -281,11 +280,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
setting.ViewersCanEdit = false
|
setting.ViewersCanEdit = false
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
|
||||||
query.Result = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetDashboardsBySlugQuery) error {
|
bus.AddHandler("test", func(query *models.GetDashboardsBySlugQuery) error {
|
||||||
dashboards := []*models.Dashboard{fakeDash}
|
dashboards := []*models.Dashboard{fakeDash}
|
||||||
query.Result = dashboards
|
query.Result = dashboards
|
||||||
@@ -354,7 +348,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, hs)
|
callDeleteDashboardBySlug(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -363,7 +357,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, hs)
|
callDeleteDashboardByUID(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -414,7 +408,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, hs)
|
callDeleteDashboardBySlug(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -423,7 +417,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUp()
|
state := setUp()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, hs)
|
callDeleteDashboardByUID(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -492,7 +486,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, hs)
|
callDeleteDashboardBySlug(sc, hs)
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -500,7 +494,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, hs)
|
callDeleteDashboardByUID(sc, hs)
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -570,7 +564,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, hs)
|
callDeleteDashboardBySlug(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -578,7 +572,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, hs)
|
callDeleteDashboardByUID(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -624,7 +618,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, hs)
|
callDeleteDashboardBySlug(sc, hs)
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -632,7 +626,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, hs)
|
callDeleteDashboardByUID(sc, hs)
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -690,7 +684,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, hs)
|
callDeleteDashboardBySlug(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
assert.Equal(t, "child-dash", state.dashQueries[0].Slug)
|
||||||
})
|
})
|
||||||
@@ -698,7 +692,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
state := setUpInner()
|
state := setUpInner()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, hs)
|
callDeleteDashboardByUID(sc, hs)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||||
})
|
})
|
||||||
@@ -730,11 +724,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
dashTwo.FolderId = 3
|
dashTwo.FolderId = 3
|
||||||
dashTwo.HasAcl = false
|
dashTwo.HasAcl = false
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
|
||||||
query.Result = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetDashboardsBySlugQuery) error {
|
bus.AddHandler("test", func(query *models.GetDashboardsBySlugQuery) error {
|
||||||
dashboards := []*models.Dashboard{dashOne, dashTwo}
|
dashboards := []*models.Dashboard{dashOne, dashTwo}
|
||||||
query.Result = dashboards
|
query.Result = dashboards
|
||||||
@@ -743,14 +732,15 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
|
|
||||||
role := models.ROLE_EDITOR
|
role := models.ROLE_EDITOR
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash",
|
||||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
|
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
|
|
||||||
assert.Equal(t, 412, sc.resp.Code)
|
assert.Equal(t, 412, sc.resp.Code)
|
||||||
result := sc.ToJSON()
|
result := sc.ToJSON()
|
||||||
assert.Equal(t, "multiple-slugs-exists", result.Get("status").MustString())
|
assert.Equal(t, "multiple-slugs-exists", result.Get("status").MustString())
|
||||||
assert.Equal(t, models.ErrDashboardsWithSameSlugExists.Error(), result.Get("message").MustString())
|
assert.Equal(t, models.ErrDashboardsWithSameSlugExists.Error(), result.Get("message").MustString())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Post dashboard response tests", func(t *testing.T) {
|
t.Run("Post dashboard response tests", func(t *testing.T) {
|
||||||
@@ -856,11 +846,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
|
||||||
query.Result = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetDashboardVersionQuery) error {
|
bus.AddHandler("test", func(query *models.GetDashboardVersionQuery) error {
|
||||||
query.Result = &models.DashboardVersion{
|
query.Result = &models.DashboardVersion{
|
||||||
Data: simplejson.NewFromAny(map[string]interface{}{
|
Data: simplejson.NewFromAny(map[string]interface{}{
|
||||||
@@ -1017,10 +1002,13 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
origGetProvisionedData := dashboards.GetProvisionedData
|
||||||
query.Result = &models.DashboardProvisioning{ExternalId: "/tmp/grafana/dashboards/test/dashboard1.json"}
|
t.Cleanup(func() {
|
||||||
return nil
|
dashboards.GetProvisionedData = origGetProvisionedData
|
||||||
})
|
})
|
||||||
|
dashboards.GetProvisionedData = func(dboards.Store, int64) (*models.DashboardProvisioning, error) {
|
||||||
|
return &models.DashboardProvisioning{ExternalId: "/tmp/grafana/dashboards/test/dashboard1.json"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
|
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
|
||||||
query.Result = []*models.DashboardAclInfoDTO{
|
query.Result = []*models.DashboardAclInfoDTO{
|
||||||
@@ -1030,20 +1018,21 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash", "/api/dashboards/db/:slug", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash",
|
||||||
setUp()
|
"/api/dashboards/db/:slug", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||||
|
setUp()
|
||||||
|
|
||||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
result := sc.ToJSON()
|
result := sc.ToJSON()
|
||||||
assert.Equal(t, models.ErrDashboardCannotDeleteProvisionedDashboard.Error(), result.Get("error").MustString())
|
assert.Equal(t, models.ErrDashboardCannotDeleteProvisionedDashboard.Error(), result.Get("error").MustString())
|
||||||
})
|
})
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/abcdefghi", "/api/dashboards/db/:uid", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/abcdefghi", "/api/dashboards/db/:uid", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||||
setUp()
|
setUp()
|
||||||
|
|
||||||
CallDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||||
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
result := sc.ToJSON()
|
result := sc.ToJSON()
|
||||||
@@ -1141,7 +1130,7 @@ func callGetDashboardVersions(sc *scenarioContext) {
|
|||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallDeleteDashboardBySlug(sc *scenarioContext, hs *HTTPServer) {
|
func callDeleteDashboardBySlug(sc *scenarioContext, hs *HTTPServer) {
|
||||||
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -1150,7 +1139,7 @@ func CallDeleteDashboardBySlug(sc *scenarioContext, hs *HTTPServer) {
|
|||||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallDeleteDashboardByUID(sc *scenarioContext, hs *HTTPServer) {
|
func callDeleteDashboardByUID(sc *scenarioContext, hs *HTTPServer) {
|
||||||
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -1187,6 +1176,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
|||||||
QuotaService: "a.QuotaService{
|
QuotaService: "a.QuotaService{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
},
|
},
|
||||||
|
PluginManager: &fakePluginManager{},
|
||||||
}
|
}
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
@@ -1204,7 +1194,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
|||||||
dashboards.NewProvisioningService = origProvisioningService
|
dashboards.NewProvisioningService = origProvisioningService
|
||||||
})
|
})
|
||||||
dashboards.MockDashboardService(mock)
|
dashboards.MockDashboardService(mock)
|
||||||
dashboards.NewProvisioningService = func() dashboards.DashboardProvisioningService {
|
dashboards.NewProvisioningService = func(dboards.Store) dashboards.DashboardProvisioningService {
|
||||||
return mockDashboardProvisioningService{}
|
return mockDashboardProvisioningService{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1268,7 +1258,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
dashboards.NewService = origNewDashboardService
|
dashboards.NewService = origNewDashboardService
|
||||||
dashboards.NewProvisioningService = origProvisioningService
|
dashboards.NewProvisioningService = origProvisioningService
|
||||||
})
|
})
|
||||||
dashboards.NewProvisioningService = func() dashboards.DashboardProvisioningService {
|
dashboards.NewProvisioningService = func(dboards.Store) dashboards.DashboardProvisioningService {
|
||||||
return mockDashboardProvisioningService{}
|
return mockDashboardProvisioningService{}
|
||||||
}
|
}
|
||||||
dashboards.MockDashboardService(mock)
|
dashboards.MockDashboardService(mock)
|
||||||
@@ -1290,6 +1280,7 @@ type mockDashboardProvisioningService struct {
|
|||||||
dashboards.DashboardProvisioningService
|
dashboards.DashboardProvisioningService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(dashboardId int64) (*models.DashboardProvisioning, error) {
|
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(dashboardID int64) (
|
||||||
return &models.DashboardProvisioning{}, nil
|
*models.DashboardProvisioning, error) {
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProxyDataSourceRequest proxies datasource requests
|
// ProxyDataSourceRequest proxies datasource requests
|
||||||
@@ -35,8 +34,8 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[ds.Type]
|
plugin := hs.PluginManager.GetDataSource(ds.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
c.JsonApiErr(http.StatusInternalServerError, "Unable to find datasource plugin", err)
|
c.JsonApiErr(http.StatusInternalServerError, "Unable to find datasource plugin", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,7 +46,7 @@ func (hs *HTTPServer) GetDataSources(c *models.ReqContext) response.Response {
|
|||||||
ReadOnly: ds.ReadOnly,
|
ReadOnly: ds.ReadOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
if plugin, exists := manager.DataSources[ds.Type]; exists {
|
if plugin := hs.PluginManager.GetDataSource(ds.Type); plugin != nil {
|
||||||
dsItem.TypeLogoUrl = plugin.Info.Logos.Small
|
dsItem.TypeLogoUrl = plugin.Info.Logos.Small
|
||||||
dsItem.TypeName = plugin.Name
|
dsItem.TypeName = plugin.Name
|
||||||
} else {
|
} else {
|
||||||
@@ -363,8 +362,8 @@ func (hs *HTTPServer) CallDatasourceResource(c *models.ReqContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[ds.Type]
|
plugin := hs.PluginManager.GetDataSource(ds.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
c.JsonApiErr(500, "Unable to find datasource plugin", err)
|
c.JsonApiErr(500, "Unable to find datasource plugin", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -428,8 +427,8 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo
|
|||||||
return response.Error(500, "Unable to load datasource metadata", err)
|
return response.Error(500, "Unable to load datasource metadata", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin, ok := hs.PluginManager.GetDatasource(ds.Type)
|
plugin := hs.PluginManager.GetDataSource(ds.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
return response.Error(500, "Unable to find datasource plugin", err)
|
return response.Error(500, "Unable to find datasource plugin", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ func TestDataSourcesProxy_userLoggedIn(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// handler func being tested
|
// handler func being tested
|
||||||
hs := &HTTPServer{Bus: bus.GetBus(), Cfg: setting.NewCfg()}
|
hs := &HTTPServer{
|
||||||
|
Bus: bus.GetBus(),
|
||||||
|
Cfg: setting.NewCfg(),
|
||||||
|
PluginManager: &fakePluginManager{},
|
||||||
|
}
|
||||||
sc.handlerFunc = hs.GetDataSources
|
sc.handlerFunc = hs.GetDataSources
|
||||||
sc.fakeReq("GET", "/api/datasources").exec()
|
sc.fakeReq("GET", "/api/datasources").exec()
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package dtos
|
|||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PluginSetting struct {
|
type PluginSetting struct {
|
||||||
@@ -64,6 +63,6 @@ type ImportDashboardCommand struct {
|
|||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Overwrite bool `json:"overwrite"`
|
Overwrite bool `json:"overwrite"`
|
||||||
Dashboard *simplejson.Json `json:"dashboard"`
|
Dashboard *simplejson.Json `json:"dashboard"`
|
||||||
Inputs []manager.ImportDashboardInput `json:"inputs"`
|
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
||||||
FolderId int64 `json:"folderId"`
|
FolderId int64 `json:"folderId"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/plugins"
|
||||||
|
|
||||||
|
type fakePluginManager struct {
|
||||||
|
plugins.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *fakePluginManager) GetPlugin(id string) *plugins.PluginBase {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *fakePluginManager) GetDataSource(id string) *plugins.DataSourcePlugin {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *fakePluginManager) Renderer() *plugins.RendererPlugin {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+16
-16
@@ -14,8 +14,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetFolders(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response {
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
folders, err := s.GetFolders(c.QueryInt64("limit"))
|
folders, err := s.GetFolders(c.QueryInt64("limit"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -35,10 +35,9 @@ func GetFolders(c *models.ReqContext) response.Response {
|
|||||||
return response.JSON(200, result)
|
return response.JSON(200, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFolderByUID(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response {
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
}
|
}
|
||||||
@@ -47,8 +46,8 @@ func GetFolderByUID(c *models.ReqContext) response.Response {
|
|||||||
return response.JSON(200, toFolderDto(g, folder))
|
return response.JSON(200, toFolderDto(g, folder))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFolderByID(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
folder, err := s.GetFolderByID(c.ParamsInt64(":id"))
|
folder, err := s.GetFolderByID(c.ParamsInt64(":id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
@@ -59,24 +58,25 @@ func GetFolderByID(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolderCommand) response.Response {
|
func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolderCommand) response.Response {
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
err := s.CreateFolder(&cmd)
|
folder, err := s.CreateFolder(cmd.Title, cmd.Uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.Cfg.EditorsCanAdmin {
|
if hs.Cfg.EditorsCanAdmin {
|
||||||
if err := dashboards.MakeUserAdmin(hs.Bus, c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil {
|
if err := s.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, folder.Id, true); err != nil {
|
||||||
hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err)
|
hs.log.Error("Could not make user admin", "folder", folder.Title, "user",
|
||||||
|
c.SignedInUser.UserId, "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g := guardian.New(cmd.Result.Id, c.OrgId, c.SignedInUser)
|
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
|
||||||
return response.JSON(200, toFolderDto(g, cmd.Result))
|
return response.JSON(200, toFolderDto(g, folder))
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response.Response {
|
func (hs *HTTPServer) UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response.Response {
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
err := s.UpdateFolder(c.Params(":uid"), &cmd)
|
err := s.UpdateFolder(c.Params(":uid"), &cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
@@ -87,7 +87,7 @@ func UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { // temporarily adding this function to HTTPServer, will be removed from HTTPServer when librarypanels featuretoggle is removed
|
func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { // temporarily adding this function to HTTPServer, will be removed from HTTPServer when librarypanels featuretoggle is removed
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||||
err := hs.LibraryPanelService.DeleteLibraryPanelsInFolder(c, c.Params(":uid"))
|
err := hs.LibraryPanelService.DeleteLibraryPanelsInFolder(c, c.Params(":uid"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
@@ -14,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Response {
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,9 +61,8 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
|||||||
return response.Error(400, err.Error(), err)
|
return response.Error(400, err.Error(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||||
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
}
|
}
|
||||||
@@ -79,11 +77,9 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
|||||||
return toFolderError(models.ErrFolderAccessDenied)
|
return toFolderError(models.ErrFolderAccessDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := models.UpdateDashboardAclCommand{}
|
var items []*models.DashboardAcl
|
||||||
cmd.DashboardID = folder.Id
|
|
||||||
|
|
||||||
for _, item := range apiCmd.Items {
|
for _, item := range apiCmd.Items {
|
||||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
items = append(items, &models.DashboardAcl{
|
||||||
OrgID: c.OrgId,
|
OrgID: c.OrgId,
|
||||||
DashboardID: folder.Id,
|
DashboardID: folder.Id,
|
||||||
UserID: item.UserID,
|
UserID: item.UserID,
|
||||||
@@ -99,9 +95,9 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Error while retrieving hidden permissions", err)
|
return response.Error(500, "Error while retrieving hidden permissions", err)
|
||||||
}
|
}
|
||||||
cmd.Items = append(cmd.Items, hiddenACL...)
|
items = append(items, hiddenACL...)
|
||||||
|
|
||||||
if okToUpdate, err := g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, cmd.Items); err != nil || !okToUpdate {
|
if okToUpdate, err := g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, items); err != nil || !okToUpdate {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, guardian.ErrGuardianPermissionExists) ||
|
if errors.Is(err, guardian.ErrGuardianPermissionExists) ||
|
||||||
errors.Is(err, guardian.ErrGuardianOverride) {
|
errors.Is(err, guardian.ErrGuardianOverride) {
|
||||||
@@ -114,7 +110,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
|||||||
return response.Error(403, "Cannot remove own admin permission for a folder", nil)
|
return response.Error(403, "Cannot remove own admin permission for a folder", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bus.Dispatch(&cmd); err != nil {
|
if err := updateDashboardACL(hs, folder.Id, items); err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardAclInfoMissing) {
|
if errors.Is(err, models.ErrDashboardAclInfoMissing) {
|
||||||
err = models.ErrFolderAclInfoMissing
|
err = models.ErrFolderAclInfoMissing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateFolderPermissions(sc)
|
callUpdateFolderPermissions(t, sc)
|
||||||
assert.Equal(t, 404, sc.resp.Code)
|
assert.Equal(t, 404, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -92,7 +92,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateFolderPermissions(sc)
|
callUpdateFolderPermissions(t, sc)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -153,7 +153,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateFolderPermissions(sc)
|
callUpdateFolderPermissions(t, sc)
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
@@ -205,7 +205,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateFolderPermissions(sc)
|
callUpdateFolderPermissions(t, sc)
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -233,7 +233,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateFolderPermissions(sc)
|
callUpdateFolderPermissions(t, sc)
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -279,7 +279,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
callUpdateFolderPermissions(sc)
|
callUpdateFolderPermissions(t, sc)
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
assert.Equal(t, 400, sc.resp.Code)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
@@ -355,13 +355,19 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
routePattern: "/api/folders/:uid/permissions",
|
routePattern: "/api/folders/:uid/permissions",
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
fn: func(sc *scenarioContext) {
|
fn: func(sc *scenarioContext) {
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
origUpdateDashboardACL := updateDashboardACL
|
||||||
assert.Len(t, cmd.Items, 4)
|
t.Cleanup(func() {
|
||||||
return nil
|
updateDashboardACL = origUpdateDashboardACL
|
||||||
})
|
})
|
||||||
|
var gotItems []*models.DashboardAcl
|
||||||
|
updateDashboardACL = func(hs *HTTPServer, dashID int64, items []*models.DashboardAcl) error {
|
||||||
|
gotItems = items
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
|
assert.Len(t, gotItems, 4)
|
||||||
},
|
},
|
||||||
}, hs)
|
}, hs)
|
||||||
})
|
})
|
||||||
@@ -372,10 +378,16 @@ func callGetFolderPermissions(sc *scenarioContext, hs *HTTPServer) {
|
|||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
func callUpdateFolderPermissions(sc *scenarioContext) {
|
func callUpdateFolderPermissions(t *testing.T, sc *scenarioContext) {
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
t.Helper()
|
||||||
return nil
|
|
||||||
|
origUpdateDashboardACL := updateDashboardACL
|
||||||
|
t.Cleanup(func() {
|
||||||
|
updateDashboardACL = origUpdateDashboardACL
|
||||||
})
|
})
|
||||||
|
updateDashboardACL = func(hs *HTTPServer, dashID int64, items []*models.DashboardAcl) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-5
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@@ -174,12 +175,16 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
|
|||||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||||
defer bus.ClearBusHandlers()
|
defer bus.ClearBusHandlers()
|
||||||
|
|
||||||
|
hs := HTTPServer{
|
||||||
|
Cfg: setting.NewCfg(),
|
||||||
|
}
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
sc.context = c
|
sc.context = c
|
||||||
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
|
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
|
||||||
|
|
||||||
return UpdateFolder(c, cmd)
|
return hs.UpdateFolder(c, cmd)
|
||||||
})
|
})
|
||||||
|
|
||||||
origNewFolderService := dashboards.NewFolderService
|
origNewFolderService := dashboards.NewFolderService
|
||||||
@@ -195,6 +200,8 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
|
|||||||
}
|
}
|
||||||
|
|
||||||
type fakeFolderService struct {
|
type fakeFolderService struct {
|
||||||
|
dashboards.FolderService
|
||||||
|
|
||||||
GetFoldersResult []*models.Folder
|
GetFoldersResult []*models.Folder
|
||||||
GetFoldersError error
|
GetFoldersError error
|
||||||
GetFolderByUIDResult *models.Folder
|
GetFolderByUIDResult *models.Folder
|
||||||
@@ -222,9 +229,8 @@ func (s *fakeFolderService) GetFolderByUID(uid string) (*models.Folder, error) {
|
|||||||
return s.GetFolderByUIDResult, s.GetFolderByUIDError
|
return s.GetFolderByUIDResult, s.GetFolderByUIDError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fakeFolderService) CreateFolder(cmd *models.CreateFolderCommand) error {
|
func (s *fakeFolderService) CreateFolder(title, uid string) (*models.Folder, error) {
|
||||||
cmd.Result = s.CreateFolderResult
|
return s.CreateFolderResult, s.CreateFolderError
|
||||||
return s.CreateFolderError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *fakeFolderService) UpdateFolder(existingUID string, cmd *models.UpdateFolderCommand) error {
|
func (s *fakeFolderService) UpdateFolder(existingUID string, cmd *models.UpdateFolderCommand) error {
|
||||||
@@ -238,7 +244,8 @@ func (s *fakeFolderService) DeleteFolder(uid string) (*models.Folder, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mockFolderService(mock *fakeFolderService) {
|
func mockFolderService(mock *fakeFolderService) {
|
||||||
dashboards.NewFolderService = func(orgId int64, user *models.SignedInUser) dashboards.FolderService {
|
dashboards.NewFolderService = func(orgId int64, user *models.SignedInUser,
|
||||||
|
dashboardStore dboards.Store) dashboards.FolderService {
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ func ReadSourceMapFromFS(dir string, path string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SourceMapStore struct {
|
type SourceMapStore struct {
|
||||||
|
sync.Mutex
|
||||||
cache map[string]*sourceMap
|
cache map[string]*sourceMap
|
||||||
cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
readSourceMap ReadSourceMapFn
|
readSourceMap ReadSourceMapFn
|
||||||
sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSourceMapStore(cfg *setting.Cfg, readSourceMap ReadSourceMapFn) *SourceMapStore {
|
func NewSourceMapStore(cfg *setting.Cfg, readSourceMap ReadSourceMapFn) *SourceMapStore {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@@ -110,12 +109,12 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins *plu
|
|||||||
|
|
||||||
// add data sources that are built in (meaning they are not added via data sources page, nor have any entry in
|
// add data sources that are built in (meaning they are not added via data sources page, nor have any entry in
|
||||||
// the datasource table)
|
// the datasource table)
|
||||||
for _, ds := range manager.DataSources {
|
for _, ds := range hs.PluginManager.DataSources() {
|
||||||
if ds.BuiltIn {
|
if ds.BuiltIn {
|
||||||
dataSources[ds.Name] = map[string]interface{}{
|
dataSources[ds.Name] = map[string]interface{}{
|
||||||
"type": ds.Type,
|
"type": ds.Type,
|
||||||
"name": ds.Name,
|
"name": ds.Name,
|
||||||
"meta": manager.DataSources[ds.Id],
|
"meta": hs.PluginManager.GetDataSource(ds.Id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,8 +225,8 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
|||||||
"commit": commit,
|
"commit": commit,
|
||||||
"buildstamp": buildstamp,
|
"buildstamp": buildstamp,
|
||||||
"edition": hs.License.Edition(),
|
"edition": hs.License.Edition(),
|
||||||
"latestVersion": hs.PluginManager.GrafanaLatestVersion,
|
"latestVersion": hs.PluginManager.GrafanaLatestVersion(),
|
||||||
"hasUpdate": hs.PluginManager.GrafanaHasUpdate,
|
"hasUpdate": hs.PluginManager.GrafanaHasUpdate(),
|
||||||
"env": setting.Env,
|
"env": setting.Env,
|
||||||
"isEnterprise": hs.License.HasValidLicense(),
|
"isEnterprise": hs.License.HasValidLicense(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,18 +39,21 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg) (*macaron.Macaron, *HT
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.ClearBusHandlers()
|
sqlStore := sqlstore.InitTestDB(t)
|
||||||
bus.AddHandler("sql", sqlstore.GetPluginSettings)
|
pm := &manager.PluginManager{Cfg: cfg, SQLStore: sqlStore}
|
||||||
t.Cleanup(bus.ClearBusHandlers)
|
|
||||||
|
|
||||||
r := &rendering.RenderingService{Cfg: cfg}
|
r := &rendering.RenderingService{
|
||||||
|
Cfg: cfg,
|
||||||
|
PluginManager: pm,
|
||||||
|
}
|
||||||
|
|
||||||
hs := &HTTPServer{
|
hs := &HTTPServer{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Bus: bus.GetBus(),
|
Bus: bus.GetBus(),
|
||||||
License: &licensing.OSSLicensingService{Cfg: cfg},
|
License: &licensing.OSSLicensingService{Cfg: cfg},
|
||||||
RenderService: r,
|
RenderService: r,
|
||||||
PluginManager: &manager.PluginManager{Cfg: cfg},
|
SQLStore: sqlStore,
|
||||||
|
PluginManager: pm,
|
||||||
}
|
}
|
||||||
|
|
||||||
m := macaron.New()
|
m := macaron.New()
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/live"
|
"github.com/grafana/grafana/pkg/services/live"
|
||||||
"github.com/grafana/grafana/pkg/services/search"
|
"github.com/grafana/grafana/pkg/services/search"
|
||||||
@@ -79,7 +80,7 @@ type HTTPServer struct {
|
|||||||
License models.Licensing `inject:""`
|
License models.Licensing `inject:""`
|
||||||
BackendPluginManager backendplugin.Manager `inject:""`
|
BackendPluginManager backendplugin.Manager `inject:""`
|
||||||
PluginRequestValidator models.PluginRequestValidator `inject:""`
|
PluginRequestValidator models.PluginRequestValidator `inject:""`
|
||||||
PluginManager *manager.PluginManager `inject:""`
|
PluginManager plugins.Manager `inject:""`
|
||||||
SearchService *search.SearchService `inject:""`
|
SearchService *search.SearchService `inject:""`
|
||||||
ShortURLService *shorturls.ShortURLService `inject:""`
|
ShortURLService *shorturls.ShortURLService `inject:""`
|
||||||
Live *live.GrafanaLive `inject:""`
|
Live *live.GrafanaLive `inject:""`
|
||||||
|
|||||||
+2
-2
@@ -400,8 +400,8 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
|||||||
GoogleTagManagerId: setting.GoogleTagManagerId,
|
GoogleTagManagerId: setting.GoogleTagManagerId,
|
||||||
BuildVersion: setting.BuildVersion,
|
BuildVersion: setting.BuildVersion,
|
||||||
BuildCommit: setting.BuildCommit,
|
BuildCommit: setting.BuildCommit,
|
||||||
NewGrafanaVersion: hs.PluginManager.GrafanaLatestVersion,
|
NewGrafanaVersion: hs.PluginManager.GrafanaLatestVersion(),
|
||||||
NewGrafanaVersionExists: hs.PluginManager.GrafanaHasUpdate,
|
NewGrafanaVersionExists: hs.PluginManager.GrafanaHasUpdate(),
|
||||||
AppName: setting.ApplicationName,
|
AppName: setting.ApplicationName,
|
||||||
AppNameBodyClass: getAppNameBodyClass(hs.License.HasValidLicense()),
|
AppNameBodyClass: getAppNameBodyClass(hs.License.HasValidLicense()),
|
||||||
FavIcon: "public/img/fav32.png",
|
FavIcon: "public/img/fav32.png",
|
||||||
|
|||||||
+10
-11
@@ -25,8 +25,8 @@ var ErrPluginNotFound error = errors.New("plugin not found, no installed plugin
|
|||||||
|
|
||||||
func (hs *HTTPServer) getPluginContext(pluginID string, user *models.SignedInUser) (backend.PluginContext, error) {
|
func (hs *HTTPServer) getPluginContext(pluginID string, user *models.SignedInUser) (backend.PluginContext, error) {
|
||||||
pc := backend.PluginContext{}
|
pc := backend.PluginContext{}
|
||||||
plugin, exists := manager.Plugins[pluginID]
|
plugin := hs.PluginManager.GetPlugin(pluginID)
|
||||||
if !exists {
|
if plugin == nil {
|
||||||
return pc, ErrPluginNotFound
|
return pc, ErrPluginNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +74,12 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pluginSettingsMap, err := hs.PluginManager.GetPluginSettings(c.OrgId)
|
pluginSettingsMap, err := hs.PluginManager.GetPluginSettings(c.OrgId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to get list of plugins", err)
|
return response.Error(500, "Failed to get list of plugins", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(dtos.PluginList, 0)
|
result := make(dtos.PluginList, 0)
|
||||||
for _, pluginDef := range manager.Plugins {
|
for _, pluginDef := range hs.PluginManager.Plugins() {
|
||||||
// filter out app sub plugins
|
// filter out app sub plugins
|
||||||
if embeddedFilter == "0" && pluginDef.IncludedInAppId != "" {
|
if embeddedFilter == "0" && pluginDef.IncludedInAppId != "" {
|
||||||
continue
|
continue
|
||||||
@@ -130,7 +129,7 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// filter out built in data sources
|
// filter out built in data sources
|
||||||
if ds, exists := manager.DataSources[pluginDef.Id]; exists {
|
if ds := hs.PluginManager.GetDataSource(pluginDef.Id); ds != nil {
|
||||||
if ds.BuiltIn {
|
if ds.BuiltIn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -143,11 +142,11 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
|||||||
return response.JSON(200, result)
|
return response.JSON(200, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPluginSettingByID(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Response {
|
||||||
pluginID := c.Params(":pluginId")
|
pluginID := c.Params(":pluginId")
|
||||||
|
|
||||||
def, exists := manager.Plugins[pluginID]
|
def := hs.PluginManager.GetPlugin(pluginID)
|
||||||
if !exists {
|
if def == nil {
|
||||||
return response.Error(404, "Plugin not found, no installed plugin with that id", nil)
|
return response.Error(404, "Plugin not found, no installed plugin with that id", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +255,7 @@ func (hs *HTTPServer) ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDa
|
|||||||
dashInfo, err := hs.PluginManager.ImportDashboard(apiCmd.PluginId, apiCmd.Path, c.OrgId, apiCmd.FolderId,
|
dashInfo, err := hs.PluginManager.ImportDashboard(apiCmd.PluginId, apiCmd.Path, c.OrgId, apiCmd.FolderId,
|
||||||
apiCmd.Dashboard, apiCmd.Overwrite, apiCmd.Inputs, c.SignedInUser, hs.DataService)
|
apiCmd.Dashboard, apiCmd.Overwrite, apiCmd.Inputs, c.SignedInUser, hs.DataService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dashboardSaveErrorToApiResponse(err)
|
return hs.dashboardSaveErrorToApiResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.JSON(200, dashInfo)
|
return response.JSON(200, dashInfo)
|
||||||
@@ -267,8 +266,8 @@ func (hs *HTTPServer) ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDa
|
|||||||
// /api/plugins/:pluginId/metrics
|
// /api/plugins/:pluginId/metrics
|
||||||
func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) response.Response {
|
||||||
pluginID := c.Params("pluginId")
|
pluginID := c.Params("pluginId")
|
||||||
plugin, exists := manager.Plugins[pluginID]
|
plugin := hs.PluginManager.GetPlugin(pluginID)
|
||||||
if !exists {
|
if plugin == nil {
|
||||||
return response.Error(404, "Plugin not found", nil)
|
return response.Error(404, "Plugin not found", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-14
@@ -7,19 +7,19 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
"github.com/grafana/grafana/pkg/services/teamguardian"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// POST /api/teams
|
// POST /api/teams
|
||||||
func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamCommand) response.Response {
|
func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamCommand) response.Response {
|
||||||
cmd.OrgId = c.OrgId
|
|
||||||
|
|
||||||
if c.OrgRole == models.ROLE_VIEWER {
|
if c.OrgRole == models.ROLE_VIEWER {
|
||||||
return response.Error(403, "Not allowed to create team.", nil)
|
return response.Error(403, "Not allowed to create team.", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hs.Bus.Dispatch(&cmd); err != nil {
|
team, err := createTeam(hs.SQLStore, cmd.Name, cmd.Email, c.OrgId)
|
||||||
|
if err != nil {
|
||||||
if errors.Is(err, models.ErrTeamNameTaken) {
|
if errors.Is(err, models.ErrTeamNameTaken) {
|
||||||
return response.Error(409, "Team name taken", err)
|
return response.Error(409, "Team name taken", err)
|
||||||
}
|
}
|
||||||
@@ -31,23 +31,17 @@ func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamComm
|
|||||||
// the SignedInUser is an empty struct therefore
|
// the SignedInUser is an empty struct therefore
|
||||||
// an additional check whether it is an actual user is required
|
// an additional check whether it is an actual user is required
|
||||||
if c.SignedInUser.IsRealUser() {
|
if c.SignedInUser.IsRealUser() {
|
||||||
addMemberCmd := models.AddTeamMemberCommand{
|
if err := addTeamMember(hs.SQLStore, c.SignedInUser.UserId, c.OrgId, team.Id, false,
|
||||||
UserId: c.SignedInUser.UserId,
|
models.PERMISSION_ADMIN); err != nil {
|
||||||
OrgId: cmd.OrgId,
|
c.Logger.Error("Could not add creator to team", "error", err)
|
||||||
TeamId: cmd.Result.Id,
|
|
||||||
Permission: models.PERMISSION_ADMIN,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hs.Bus.Dispatch(&addMemberCmd); err != nil {
|
|
||||||
c.Logger.Error("Could not add creator to team.", "error", err)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Logger.Warn("Could not add creator to team because is not a real user.")
|
c.Logger.Warn("Could not add creator to team because is not a real user")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.JSON(200, &util.DynMap{
|
return response.JSON(200, &util.DynMap{
|
||||||
"teamId": cmd.Result.Id,
|
"teamId": team.Id,
|
||||||
"message": "Team created",
|
"message": "Team created",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -175,3 +169,18 @@ func (hs *HTTPServer) UpdateTeamPreferences(c *models.ReqContext, dtoCmd dtos.Up
|
|||||||
|
|
||||||
return updatePreferencesFor(orgId, 0, teamId, &dtoCmd)
|
return updatePreferencesFor(orgId, 0, teamId, &dtoCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createTeam creates a team.
|
||||||
|
//
|
||||||
|
// Stubbable by tests.
|
||||||
|
var createTeam = func(sqlStore *sqlstore.SQLStore, name, email string, orgID int64) (models.Team, error) {
|
||||||
|
return sqlStore.CreateTeam(name, email, orgID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addTeamMember adds a team member.
|
||||||
|
//
|
||||||
|
// Stubbable by tests.
|
||||||
|
var addTeamMember = func(sqlStore *sqlstore.SQLStore, userID, orgID, teamID int64, isExternal bool,
|
||||||
|
permission models.PermissionType) error {
|
||||||
|
return sqlStore.AddTeamMember(userID, orgID, teamID, isExternal, permission)
|
||||||
|
}
|
||||||
|
|||||||
+20
-10
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
macaron "gopkg.in/macaron.v1"
|
macaron "gopkg.in/macaron.v1"
|
||||||
|
|
||||||
@@ -98,20 +99,29 @@ func TestTeamAPIEndpoint(t *testing.T) {
|
|||||||
|
|
||||||
teamName := "team foo"
|
teamName := "team foo"
|
||||||
|
|
||||||
createTeamCalled := 0
|
// TODO: Use a fake SQLStore when it's represented by an interface
|
||||||
bus.AddHandler("test", func(cmd *models.CreateTeamCommand) error {
|
origCreateTeam := createTeam
|
||||||
createTeamCalled += 1
|
origAddTeamMember := addTeamMember
|
||||||
cmd.Result = models.Team{Name: teamName, Id: 42}
|
t.Cleanup(func() {
|
||||||
return nil
|
createTeam = origCreateTeam
|
||||||
|
addTeamMember = origAddTeamMember
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createTeamCalled := 0
|
||||||
|
createTeam = func(sqlStore *sqlstore.SQLStore, name, email string, orgID int64) (models.Team, error) {
|
||||||
|
createTeamCalled++
|
||||||
|
return models.Team{Name: teamName, Id: 42}, nil
|
||||||
|
}
|
||||||
|
|
||||||
addTeamMemberCalled := 0
|
addTeamMemberCalled := 0
|
||||||
bus.AddHandler("test", func(cmd *models.AddTeamMemberCommand) error {
|
addTeamMember = func(sqlStore *sqlstore.SQLStore, userID, orgID, teamID int64, isExternal bool,
|
||||||
addTeamMemberCalled += 1
|
permission models.PermissionType) error {
|
||||||
|
addTeamMemberCalled++
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
req, _ := http.NewRequest("POST", "/api/teams", nil)
|
req, err := http.NewRequest("POST", "/api/teams", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("with no real signed in user", func(t *testing.T) {
|
t.Run("with no real signed in user", func(t *testing.T) {
|
||||||
stub := &testLogger{}
|
stub := &testLogger{}
|
||||||
@@ -128,7 +138,7 @@ func TestTeamAPIEndpoint(t *testing.T) {
|
|||||||
assert.Equal(t, createTeamCalled, 1)
|
assert.Equal(t, createTeamCalled, 1)
|
||||||
assert.Equal(t, addTeamMemberCalled, 0)
|
assert.Equal(t, addTeamMemberCalled, 0)
|
||||||
assert.True(t, stub.warnCalled)
|
assert.True(t, stub.warnCalled)
|
||||||
assert.Equal(t, stub.warnMessage, "Could not add creator to team because is not a real user.")
|
assert.Equal(t, stub.warnMessage, "Could not add creator to team because is not a real user")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("with real signed in user", func(t *testing.T) {
|
t.Run("with real signed in user", func(t *testing.T) {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dashboards
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/models"
|
||||||
|
|
||||||
|
// Store is a dashboard store.
|
||||||
|
type Store interface {
|
||||||
|
// ValidateDashboardBeforeSave validates a dashboard before save.
|
||||||
|
ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (bool, error)
|
||||||
|
GetProvisionedDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error)
|
||||||
|
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
||||||
|
SaveProvisionedDashboard(cmd models.SaveDashboardCommand, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
||||||
|
SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error)
|
||||||
|
UpdateDashboardACL(uid int64, items []*models.DashboardAcl) error
|
||||||
|
// SaveAlerts saves dashboard alerts.
|
||||||
|
SaveAlerts(dashID int64, alerts []*models.Alert) error
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/login/social"
|
"github.com/grafana/grafana/pkg/login/social"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ type UsageStatsService struct {
|
|||||||
SQLStore *sqlstore.SQLStore `inject:""`
|
SQLStore *sqlstore.SQLStore `inject:""`
|
||||||
AlertingUsageStats alerting.UsageStatsQuerier `inject:""`
|
AlertingUsageStats alerting.UsageStatsQuerier `inject:""`
|
||||||
License models.Licensing `inject:""`
|
License models.Licensing `inject:""`
|
||||||
|
PluginManager plugins.Manager `inject:""`
|
||||||
|
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport,
|
|||||||
metrics["stats.playlist.count"] = statsQuery.Result.Playlists
|
metrics["stats.playlist.count"] = statsQuery.Result.Playlists
|
||||||
metrics["stats.plugins.apps.count"] = len(manager.Apps)
|
metrics["stats.plugins.apps.count"] = len(manager.Apps)
|
||||||
metrics["stats.plugins.panels.count"] = len(manager.Panels)
|
metrics["stats.plugins.panels.count"] = len(manager.Panels)
|
||||||
metrics["stats.plugins.datasources.count"] = len(manager.DataSources)
|
metrics["stats.plugins.datasources.count"] = uss.PluginManager.DataSourceCount()
|
||||||
metrics["stats.alerts.count"] = statsQuery.Result.Alerts
|
metrics["stats.alerts.count"] = statsQuery.Result.Alerts
|
||||||
metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers
|
metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers
|
||||||
metrics["stats.datasources.count"] = statsQuery.Result.Datasources
|
metrics["stats.datasources.count"] = statsQuery.Result.Datasources
|
||||||
@@ -329,8 +329,8 @@ func (uss *UsageStatsService) updateTotalStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (uss *UsageStatsService) shouldBeReported(dsType string) bool {
|
func (uss *UsageStatsService) shouldBeReported(dsType string) bool {
|
||||||
ds, ok := manager.DataSources[dsType]
|
ds := uss.PluginManager.GetDataSource(dsType)
|
||||||
if !ok {
|
if ds == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ func TestMetrics(t *testing.T) {
|
|||||||
assert.Equal(t, getSystemStatsQuery.Result.Playlists, metrics.Get("stats.playlist.count").MustInt64())
|
assert.Equal(t, getSystemStatsQuery.Result.Playlists, metrics.Get("stats.playlist.count").MustInt64())
|
||||||
assert.Equal(t, len(manager.Apps), metrics.Get("stats.plugins.apps.count").MustInt())
|
assert.Equal(t, len(manager.Apps), metrics.Get("stats.plugins.apps.count").MustInt())
|
||||||
assert.Equal(t, len(manager.Panels), metrics.Get("stats.plugins.panels.count").MustInt())
|
assert.Equal(t, len(manager.Panels), metrics.Get("stats.plugins.panels.count").MustInt())
|
||||||
assert.Equal(t, len(manager.DataSources), metrics.Get("stats.plugins.datasources.count").MustInt())
|
assert.Equal(t, uss.PluginManager.DataSourceCount(), metrics.Get("stats.plugins.datasources.count").MustInt())
|
||||||
assert.Equal(t, getSystemStatsQuery.Result.Alerts, metrics.Get("stats.alerts.count").MustInt64())
|
assert.Equal(t, getSystemStatsQuery.Result.Alerts, metrics.Get("stats.alerts.count").MustInt64())
|
||||||
assert.Equal(t, getSystemStatsQuery.Result.ActiveUsers, metrics.Get("stats.active_users.count").MustInt64())
|
assert.Equal(t, getSystemStatsQuery.Result.ActiveUsers, metrics.Get("stats.active_users.count").MustInt64())
|
||||||
assert.Equal(t, getSystemStatsQuery.Result.Datasources, metrics.Get("stats.datasources.count").MustInt64())
|
assert.Equal(t, getSystemStatsQuery.Result.Datasources, metrics.Get("stats.datasources.count").MustInt64())
|
||||||
@@ -542,37 +542,51 @@ func (aum *alertingUsageMock) QueryUsageStats() (*alerting.UsageStats, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakePluginManager struct {
|
||||||
|
manager.PluginManager
|
||||||
|
|
||||||
|
dataSources map[string]*plugins.DataSourcePlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm fakePluginManager) DataSourceCount() int {
|
||||||
|
return len(pm.dataSources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm fakePluginManager) GetDataSource(id string) *plugins.DataSourcePlugin {
|
||||||
|
return pm.dataSources[id]
|
||||||
|
}
|
||||||
|
|
||||||
func setupSomeDataSourcePlugins(t *testing.T, uss *UsageStatsService) {
|
func setupSomeDataSourcePlugins(t *testing.T, uss *UsageStatsService) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
originalDataSources := manager.DataSources
|
uss.PluginManager = &fakePluginManager{
|
||||||
t.Cleanup(func() { manager.DataSources = originalDataSources })
|
dataSources: map[string]*plugins.DataSourcePlugin{
|
||||||
manager.DataSources = map[string]*plugins.DataSourcePlugin{
|
models.DS_ES: {
|
||||||
models.DS_ES: {
|
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
PluginBase: plugins.PluginBase{
|
||||||
PluginBase: plugins.PluginBase{
|
Signature: "internal",
|
||||||
Signature: "internal",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
models.DS_PROMETHEUS: {
|
||||||
models.DS_PROMETHEUS: {
|
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
PluginBase: plugins.PluginBase{
|
||||||
PluginBase: plugins.PluginBase{
|
Signature: "internal",
|
||||||
Signature: "internal",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
models.DS_GRAPHITE: {
|
||||||
models.DS_GRAPHITE: {
|
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
PluginBase: plugins.PluginBase{
|
||||||
PluginBase: plugins.PluginBase{
|
Signature: "internal",
|
||||||
Signature: "internal",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
models.DS_MYSQL: {
|
||||||
models.DS_MYSQL: {
|
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
PluginBase: plugins.PluginBase{
|
||||||
PluginBase: plugins.PluginBase{
|
Signature: "internal",
|
||||||
Signature: "internal",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -595,5 +609,6 @@ func createService(t *testing.T, cfg setting.Cfg) *UsageStatsService {
|
|||||||
License: &licensing.OSSLicensingService{},
|
License: &licensing.OSSLicensingService{},
|
||||||
AlertingUsageStats: &alertingUsageMock{},
|
AlertingUsageStats: &alertingUsageMock{},
|
||||||
externalMetrics: make(map[string]MetricFunc),
|
externalMetrics: make(map[string]MetricFunc),
|
||||||
|
PluginManager: &fakePluginManager{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,18 +209,3 @@ type AlertStateInfoDTO struct {
|
|||||||
State AlertStateType `json:"state"`
|
State AlertStateType `json:"state"`
|
||||||
NewStateDate time.Time `json:"newStateDate"`
|
NewStateDate time.Time `json:"newStateDate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Internal" commands
|
|
||||||
|
|
||||||
type UpdateDashboardAlertsCommand struct {
|
|
||||||
OrgId int64
|
|
||||||
Dashboard *Dashboard
|
|
||||||
User *SignedInUser
|
|
||||||
}
|
|
||||||
|
|
||||||
type ValidateDashboardAlertsCommand struct {
|
|
||||||
UserId int64
|
|
||||||
OrgId int64
|
|
||||||
Dashboard *Dashboard
|
|
||||||
User *SignedInUser
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -95,15 +95,6 @@ func (dto *DashboardAclInfoDTO) IsDuplicateOf(other *DashboardAclInfoDTO) bool {
|
|||||||
return dto.hasSameRoleAs(other) || dto.hasSameUserAs(other) || dto.hasSameTeamAs(other)
|
return dto.hasSameRoleAs(other) || dto.hasSameUserAs(other) || dto.hasSameTeamAs(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// COMMANDS
|
|
||||||
//
|
|
||||||
|
|
||||||
type UpdateDashboardAclCommand struct {
|
|
||||||
DashboardID int64
|
|
||||||
Items []*DashboardAcl
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// QUERIES
|
// QUERIES
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -367,13 +367,6 @@ type DeleteDashboardCommand struct {
|
|||||||
OrgId int64
|
OrgId int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateDashboardBeforeSaveCommand struct {
|
|
||||||
OrgId int64
|
|
||||||
Dashboard *Dashboard
|
|
||||||
Overwrite bool
|
|
||||||
Result *ValidateDashboardBeforeSaveResult
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteOrphanedProvisionedDashboardsCommand struct {
|
type DeleteOrphanedProvisionedDashboardsCommand struct {
|
||||||
ReaderNames []string
|
ReaderNames []string
|
||||||
}
|
}
|
||||||
@@ -430,11 +423,6 @@ type GetProvisionedDashboardDataByIdQuery struct {
|
|||||||
Result *DashboardProvisioning
|
Result *DashboardProvisioning
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProvisionedDashboardDataQuery struct {
|
|
||||||
Name string
|
|
||||||
Result []*DashboardProvisioning
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetDashboardsBySlugQuery struct {
|
type GetDashboardsBySlugQuery struct {
|
||||||
OrgId int64
|
OrgId int64
|
||||||
Slug string
|
Slug string
|
||||||
|
|||||||
@@ -32,23 +32,6 @@ type Folder struct {
|
|||||||
HasAcl bool
|
HasAcl bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDashboardModel turns the command into the saveable model
|
|
||||||
func (cmd *CreateFolderCommand) GetDashboardModel(orgId int64, userId int64) *Dashboard {
|
|
||||||
dashFolder := NewDashboardFolder(strings.TrimSpace(cmd.Title))
|
|
||||||
dashFolder.OrgId = orgId
|
|
||||||
dashFolder.SetUid(strings.TrimSpace(cmd.Uid))
|
|
||||||
|
|
||||||
if userId == 0 {
|
|
||||||
userId = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
dashFolder.CreatedBy = userId
|
|
||||||
dashFolder.UpdatedBy = userId
|
|
||||||
dashFolder.UpdateSlug()
|
|
||||||
|
|
||||||
return dashFolder
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDashboardModel updates an existing model from command into model for update
|
// UpdateDashboardModel updates an existing model from command into model for update
|
||||||
func (cmd *UpdateFolderCommand) UpdateDashboardModel(dashFolder *Dashboard, orgId int64, userId int64) {
|
func (cmd *UpdateFolderCommand) UpdateDashboardModel(dashFolder *Dashboard, orgId int64, userId int64) {
|
||||||
dashFolder.OrgId = orgId
|
dashFolder.OrgId = orgId
|
||||||
|
|||||||
@@ -53,10 +53,6 @@ func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() securejsondata.SecureJ
|
|||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// QUERIES
|
// QUERIES
|
||||||
type GetPluginSettingsQuery struct {
|
|
||||||
OrgId int64
|
|
||||||
Result []*PluginSettingInfoDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginSettingInfoDTO struct {
|
type PluginSettingInfoDTO struct {
|
||||||
OrgId int64
|
OrgId int64
|
||||||
|
|||||||
@@ -3,10 +3,55 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Manager is the plugin manager service interface.
|
||||||
|
type Manager interface {
|
||||||
|
// Renderer gets the renderer plugin.
|
||||||
|
Renderer() *RendererPlugin
|
||||||
|
// GetDataSource gets a data source plugin with a certain ID.
|
||||||
|
GetDataSource(id string) *DataSourcePlugin
|
||||||
|
// GetDataPlugin gets a data plugin with a certain ID.
|
||||||
|
GetDataPlugin(id string) DataPlugin
|
||||||
|
// GetPlugin gets a plugin with a certain ID.
|
||||||
|
GetPlugin(id string) *PluginBase
|
||||||
|
// DataSourceCount gets the number of data sources.
|
||||||
|
DataSourceCount() int
|
||||||
|
// DataSources gets all data sources.
|
||||||
|
DataSources() []*DataSourcePlugin
|
||||||
|
// GetEnabledPlugins gets enabled plugins.
|
||||||
|
GetEnabledPlugins(orgID int64) (*EnabledPlugins, error)
|
||||||
|
// GrafanaLatestVersion gets the latest Grafana version.
|
||||||
|
GrafanaLatestVersion() string
|
||||||
|
// GrafanaHasUpdate returns whether Grafana has an update.
|
||||||
|
GrafanaHasUpdate() bool
|
||||||
|
// Plugins gets all plugins.
|
||||||
|
Plugins() []*PluginBase
|
||||||
|
// GetPluginSettings gets settings for a certain plugin.
|
||||||
|
GetPluginSettings(orgID int64) (map[string]*models.PluginSettingInfoDTO, error)
|
||||||
|
// GetPluginDashboards gets dashboards for a certain org/plugin.
|
||||||
|
GetPluginDashboards(orgID int64, pluginID string) ([]*PluginDashboardInfoDTO, error)
|
||||||
|
// GetPluginMarkdown gets markdown for a certain plugin/name.
|
||||||
|
GetPluginMarkdown(pluginID string, name string) ([]byte, error)
|
||||||
|
// ImportDashboard imports a dashboard.
|
||||||
|
ImportDashboard(pluginID, path string, orgID, folderID int64, dashboardModel *simplejson.Json,
|
||||||
|
overwrite bool, inputs []ImportDashboardInput, user *models.SignedInUser,
|
||||||
|
requestHandler DataRequestHandler) (PluginDashboardInfoDTO, error)
|
||||||
|
// ScanningErrors returns plugin scanning errors encountered.
|
||||||
|
ScanningErrors() []PluginError
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImportDashboardInput struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
PluginId string `json:"pluginId"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
// DataRequestHandler is a data request handler interface.
|
// DataRequestHandler is a data request handler interface.
|
||||||
type DataRequestHandler interface {
|
type DataRequestHandler interface {
|
||||||
|
// HandleRequest handles a data request.
|
||||||
HandleRequest(context.Context, *models.DataSource, DataQuery) (DataResponse, error)
|
HandleRequest(context.Context, *models.DataSource, DataQuery) (DataResponse, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ import (
|
|||||||
|
|
||||||
var varRegex = regexp.MustCompile(`(\$\{.+?\})`)
|
var varRegex = regexp.MustCompile(`(\$\{.+?\})`)
|
||||||
|
|
||||||
type ImportDashboardInput struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
PluginId string `json:"pluginId"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DashboardInputMissingError struct {
|
type DashboardInputMissingError struct {
|
||||||
VariableName string
|
VariableName string
|
||||||
}
|
}
|
||||||
@@ -29,7 +22,7 @@ func (e DashboardInputMissingError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PluginManager) ImportDashboard(pluginID, path string, orgID, folderID int64, dashboardModel *simplejson.Json,
|
func (pm *PluginManager) ImportDashboard(pluginID, path string, orgID, folderID int64, dashboardModel *simplejson.Json,
|
||||||
overwrite bool, inputs []ImportDashboardInput, user *models.SignedInUser,
|
overwrite bool, inputs []plugins.ImportDashboardInput, user *models.SignedInUser,
|
||||||
requestHandler plugins.DataRequestHandler) (plugins.PluginDashboardInfoDTO, error) {
|
requestHandler plugins.DataRequestHandler) (plugins.PluginDashboardInfoDTO, error) {
|
||||||
var dashboard *models.Dashboard
|
var dashboard *models.Dashboard
|
||||||
if pluginID != "" {
|
if pluginID != "" {
|
||||||
@@ -67,7 +60,7 @@ func (pm *PluginManager) ImportDashboard(pluginID, path string, orgID, folderID
|
|||||||
User: user,
|
User: user,
|
||||||
}
|
}
|
||||||
|
|
||||||
savedDash, err := dashboards.NewService().ImportDashboard(dto)
|
savedDash, err := dashboards.NewService(pm.SQLStore).ImportDashboard(dto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugins.PluginDashboardInfoDTO{}, err
|
return plugins.PluginDashboardInfoDTO{}, err
|
||||||
}
|
}
|
||||||
@@ -89,12 +82,12 @@ func (pm *PluginManager) ImportDashboard(pluginID, path string, orgID, folderID
|
|||||||
|
|
||||||
type DashTemplateEvaluator struct {
|
type DashTemplateEvaluator struct {
|
||||||
template *simplejson.Json
|
template *simplejson.Json
|
||||||
inputs []ImportDashboardInput
|
inputs []plugins.ImportDashboardInput
|
||||||
variables map[string]string
|
variables map[string]string
|
||||||
result *simplejson.Json
|
result *simplejson.Json
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DashTemplateEvaluator) findInput(varName string, varType string) *ImportDashboardInput {
|
func (e *DashTemplateEvaluator) findInput(varName string, varType string) *plugins.ImportDashboardInput {
|
||||||
for _, input := range e.inputs {
|
for _, input := range e.inputs {
|
||||||
if varType == input.Type && (input.Name == varName || input.Name == "*") {
|
if varType == input.Type && (input.Name == varName || input.Name == "*") {
|
||||||
return &input
|
return &input
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -21,7 +22,7 @@ func TestDashboardImport(t *testing.T) {
|
|||||||
dashboards.MockDashboardService(mock)
|
dashboards.MockDashboardService(mock)
|
||||||
|
|
||||||
info, err := pm.ImportDashboard("test-app", "dashboards/connections.json", 1, 0, nil, false,
|
info, err := pm.ImportDashboard("test-app", "dashboards/connections.json", 1, 0, nil, false,
|
||||||
[]ImportDashboardInput{
|
[]plugins.ImportDashboardInput{
|
||||||
{Name: "*", Type: "datasource", Value: "graphite"},
|
{Name: "*", Type: "datasource", Value: "graphite"},
|
||||||
}, &models.SignedInUser{UserId: 1, OrgRole: models.ROLE_ADMIN}, nil)
|
}, &models.SignedInUser{UserId: 1, OrgRole: models.ROLE_ADMIN}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -58,7 +59,7 @@ func TestDashboardImport(t *testing.T) {
|
|||||||
|
|
||||||
evaluator := &DashTemplateEvaluator{
|
evaluator := &DashTemplateEvaluator{
|
||||||
template: template,
|
template: template,
|
||||||
inputs: []ImportDashboardInput{
|
inputs: []plugins.ImportDashboardInput{
|
||||||
{Name: "*", Type: "datasource", Value: "my-server"},
|
{Name: "*", Type: "datasource", Value: "my-server"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (pm *PluginManager) GetPluginDashboards(orgId int64, pluginId string) ([]*plugins.PluginDashboardInfoDTO, error) {
|
func (pm *PluginManager) GetPluginDashboards(orgId int64, pluginId string) ([]*plugins.PluginDashboardInfoDTO, error) {
|
||||||
plugin, exists := Plugins[pluginId]
|
plugin, exists := pm.plugins[pluginId]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ func (pm *PluginManager) GetPluginDashboards(orgId int64, pluginId string) ([]*p
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PluginManager) LoadPluginDashboard(pluginId, path string) (*models.Dashboard, error) {
|
func (pm *PluginManager) LoadPluginDashboard(pluginId, path string) (*models.Dashboard, error) {
|
||||||
plugin, exists := Plugins[pluginId]
|
plugin, exists := pm.plugins[pluginId]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,19 +21,17 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DataSources map[string]*plugins.DataSourcePlugin
|
|
||||||
Panels map[string]*plugins.PanelPlugin
|
Panels map[string]*plugins.PanelPlugin
|
||||||
StaticRoutes []*plugins.PluginStaticRoute
|
StaticRoutes []*plugins.PluginStaticRoute
|
||||||
Apps map[string]*plugins.AppPlugin
|
Apps map[string]*plugins.AppPlugin
|
||||||
Plugins map[string]*plugins.PluginBase
|
|
||||||
PluginTypes map[string]interface{}
|
PluginTypes map[string]interface{}
|
||||||
Renderer *plugins.RendererPlugin
|
|
||||||
|
|
||||||
plog log.Logger
|
plog log.Logger
|
||||||
)
|
)
|
||||||
@@ -54,30 +52,36 @@ type PluginScanner struct {
|
|||||||
type PluginManager struct {
|
type PluginManager struct {
|
||||||
BackendPluginManager backendplugin.Manager `inject:""`
|
BackendPluginManager backendplugin.Manager `inject:""`
|
||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
|
SQLStore *sqlstore.SQLStore `inject:""`
|
||||||
log log.Logger
|
log log.Logger
|
||||||
scanningErrors []error
|
scanningErrors []error
|
||||||
|
|
||||||
// AllowUnsignedPluginsCondition changes the policy for allowing unsigned plugins. Signature validation only runs when plugins are starting
|
// AllowUnsignedPluginsCondition changes the policy for allowing unsigned plugins. Signature validation only runs when plugins are starting
|
||||||
// and running plugins will not be terminated if they violate the new policy.
|
// and running plugins will not be terminated if they violate the new policy.
|
||||||
AllowUnsignedPluginsCondition unsignedPluginConditionFunc
|
AllowUnsignedPluginsCondition unsignedPluginConditionFunc
|
||||||
GrafanaLatestVersion string
|
grafanaLatestVersion string
|
||||||
GrafanaHasUpdate bool
|
grafanaHasUpdate bool
|
||||||
pluginScanningErrors map[string]plugins.PluginError
|
pluginScanningErrors map[string]plugins.PluginError
|
||||||
|
|
||||||
|
renderer *plugins.RendererPlugin
|
||||||
|
dataSources map[string]*plugins.DataSourcePlugin
|
||||||
|
plugins map[string]*plugins.PluginBase
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.RegisterService(&PluginManager{})
|
registry.RegisterService(&PluginManager{
|
||||||
|
dataSources: map[string]*plugins.DataSourcePlugin{},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PluginManager) Init() error {
|
func (pm *PluginManager) Init() error {
|
||||||
pm.log = log.New("plugins")
|
pm.log = log.New("plugins")
|
||||||
plog = log.New("plugins")
|
plog = log.New("plugins")
|
||||||
|
|
||||||
DataSources = map[string]*plugins.DataSourcePlugin{}
|
|
||||||
StaticRoutes = []*plugins.PluginStaticRoute{}
|
StaticRoutes = []*plugins.PluginStaticRoute{}
|
||||||
Panels = map[string]*plugins.PanelPlugin{}
|
Panels = map[string]*plugins.PanelPlugin{}
|
||||||
Apps = map[string]*plugins.AppPlugin{}
|
Apps = map[string]*plugins.AppPlugin{}
|
||||||
Plugins = map[string]*plugins.PluginBase{}
|
pm.plugins = map[string]*plugins.PluginBase{}
|
||||||
PluginTypes = map[string]interface{}{
|
PluginTypes = map[string]interface{}{
|
||||||
"panel": plugins.PanelPlugin{},
|
"panel": plugins.PanelPlugin{},
|
||||||
"datasource": plugins.DataSourcePlugin{},
|
"datasource": plugins.DataSourcePlugin{},
|
||||||
@@ -134,22 +138,22 @@ func (pm *PluginManager) Init() error {
|
|||||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ds := range DataSources {
|
for _, ds := range pm.dataSources {
|
||||||
staticRoutes := ds.InitFrontendPlugin(pm.Cfg)
|
staticRoutes := ds.InitFrontendPlugin(pm.Cfg)
|
||||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, app := range Apps {
|
for _, app := range Apps {
|
||||||
staticRoutes := app.InitApp(Panels, DataSources, pm.Cfg)
|
staticRoutes := app.InitApp(Panels, pm.dataSources, pm.Cfg)
|
||||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if Renderer != nil {
|
if pm.renderer != nil {
|
||||||
staticRoutes := Renderer.InitFrontendPlugin(pm.Cfg)
|
staticRoutes := pm.renderer.InitFrontendPlugin(pm.Cfg)
|
||||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range Plugins {
|
for _, p := range pm.plugins {
|
||||||
if p.IsCorePlugin {
|
if p.IsCorePlugin {
|
||||||
p.Signature = plugins.PluginSignatureInternal
|
p.Signature = plugins.PluginSignatureInternal
|
||||||
} else {
|
} else {
|
||||||
@@ -178,6 +182,48 @@ func (pm *PluginManager) Run(ctx context.Context) error {
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) Renderer() *plugins.RendererPlugin {
|
||||||
|
return pm.renderer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) GetDataSource(id string) *plugins.DataSourcePlugin {
|
||||||
|
return pm.dataSources[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) DataSources() []*plugins.DataSourcePlugin {
|
||||||
|
var rslt []*plugins.DataSourcePlugin
|
||||||
|
for _, ds := range pm.dataSources {
|
||||||
|
rslt = append(rslt, ds)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rslt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) DataSourceCount() int {
|
||||||
|
return len(pm.dataSources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) Plugins() []*plugins.PluginBase {
|
||||||
|
var rslt []*plugins.PluginBase
|
||||||
|
for _, p := range pm.plugins {
|
||||||
|
rslt = append(rslt, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rslt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) GetPlugin(id string) *plugins.PluginBase {
|
||||||
|
return pm.plugins[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) GrafanaLatestVersion() string {
|
||||||
|
return pm.grafanaLatestVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PluginManager) GrafanaHasUpdate() bool {
|
||||||
|
return pm.grafanaHasUpdate
|
||||||
|
}
|
||||||
|
|
||||||
// scanPluginPaths scans configured plugin paths.
|
// scanPluginPaths scans configured plugin paths.
|
||||||
func (pm *PluginManager) scanPluginPaths() error {
|
func (pm *PluginManager) scanPluginPaths() error {
|
||||||
for pluginID, settings := range pm.Cfg.PluginSettings {
|
for pluginID, settings := range pm.Cfg.PluginSettings {
|
||||||
@@ -315,13 +361,13 @@ func (pm *PluginManager) loadPlugin(jsonParser *json.Decoder, pluginBase *plugin
|
|||||||
var pb *plugins.PluginBase
|
var pb *plugins.PluginBase
|
||||||
switch p := plug.(type) {
|
switch p := plug.(type) {
|
||||||
case *plugins.DataSourcePlugin:
|
case *plugins.DataSourcePlugin:
|
||||||
DataSources[p.Id] = p
|
pm.dataSources[p.Id] = p
|
||||||
pb = &p.PluginBase
|
pb = &p.PluginBase
|
||||||
case *plugins.PanelPlugin:
|
case *plugins.PanelPlugin:
|
||||||
Panels[p.Id] = p
|
Panels[p.Id] = p
|
||||||
pb = &p.PluginBase
|
pb = &p.PluginBase
|
||||||
case *plugins.RendererPlugin:
|
case *plugins.RendererPlugin:
|
||||||
Renderer = p
|
pm.renderer = p
|
||||||
pb = &p.PluginBase
|
pb = &p.PluginBase
|
||||||
case *plugins.AppPlugin:
|
case *plugins.AppPlugin:
|
||||||
Apps[p.Id] = p
|
Apps[p.Id] = p
|
||||||
@@ -330,7 +376,7 @@ func (pm *PluginManager) loadPlugin(jsonParser *json.Decoder, pluginBase *plugin
|
|||||||
panic(fmt.Sprintf("Unrecognized plugin type %T", plug))
|
panic(fmt.Sprintf("Unrecognized plugin type %T", plug))
|
||||||
}
|
}
|
||||||
|
|
||||||
if p, exists := Plugins[pb.Id]; exists {
|
if p, exists := pm.plugins[pb.Id]; exists {
|
||||||
pm.log.Warn("Plugin is duplicate", "id", pb.Id)
|
pm.log.Warn("Plugin is duplicate", "id", pb.Id)
|
||||||
scanner.errors = append(scanner.errors, plugins.DuplicatePluginError{Plugin: pb, ExistingPlugin: p})
|
scanner.errors = append(scanner.errors, plugins.DuplicatePluginError{Plugin: pb, ExistingPlugin: p})
|
||||||
return nil
|
return nil
|
||||||
@@ -360,20 +406,11 @@ func (pm *PluginManager) loadPlugin(jsonParser *json.Decoder, pluginBase *plugin
|
|||||||
pb.SignatureType = pluginBase.SignatureType
|
pb.SignatureType = pluginBase.SignatureType
|
||||||
pb.SignatureOrg = pluginBase.SignatureOrg
|
pb.SignatureOrg = pluginBase.SignatureOrg
|
||||||
|
|
||||||
Plugins[pb.Id] = pb
|
pm.plugins[pb.Id] = pb
|
||||||
pm.log.Debug("Successfully added plugin", "id", pb.Id)
|
pm.log.Debug("Successfully added plugin", "id", pb.Id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDatasource returns a datasource based on passed pluginID if it exists
|
|
||||||
//
|
|
||||||
// This function fetches the datasource from the global variable DataSources in this package.
|
|
||||||
// Rather then refactor all dependencies on the global variable we can use this as an transition.
|
|
||||||
func (pm *PluginManager) GetDatasource(pluginID string) (*plugins.DataSourcePlugin, bool) {
|
|
||||||
ds, exists := DataSources[pluginID]
|
|
||||||
return ds, exists
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PluginScanner) walker(currentPath string, f os.FileInfo, err error) error {
|
func (s *PluginScanner) walker(currentPath string, f os.FileInfo, err error) error {
|
||||||
// We scan all the subfolders for plugin.json (with some exceptions) so that we also load embedded plugins, for
|
// We scan all the subfolders for plugin.json (with some exceptions) so that we also load embedded plugins, for
|
||||||
// example https://github.com/raintank/worldping-app/tree/master/dist/grafana-worldmap-panel worldmap panel plugin
|
// example https://github.com/raintank/worldping-app/tree/master/dist/grafana-worldmap-panel worldmap panel plugin
|
||||||
@@ -545,7 +582,7 @@ func (pm *PluginManager) ScanningErrors() []plugins.PluginError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PluginManager) GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
|
func (pm *PluginManager) GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
|
||||||
plug, exists := Plugins[pluginId]
|
plug, exists := pm.plugins[pluginId]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
||||||
}
|
}
|
||||||
@@ -600,14 +637,14 @@ func collectPluginFilesWithin(rootDir string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDataPlugin gets a DataPlugin with a certain name. If none is found, nil is returned.
|
// GetDataPlugin gets a DataPlugin with a certain name. If none is found, nil is returned.
|
||||||
func (pm *PluginManager) GetDataPlugin(pluginID string) plugins.DataPlugin {
|
func (pm *PluginManager) GetDataPlugin(id string) plugins.DataPlugin {
|
||||||
if p, exists := DataSources[pluginID]; exists && p.CanHandleDataQueries() {
|
if p, exists := pm.dataSources[id]; exists && p.CanHandleDataQueries() {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Might other plugins implement DataPlugin?
|
// XXX: Might other plugins implement DataPlugin?
|
||||||
|
|
||||||
p := pm.BackendPluginManager.GetDataPlugin(pluginID)
|
p := pm.BackendPluginManager.GetDataPlugin(id)
|
||||||
if p != nil {
|
if p != nil {
|
||||||
return p.(plugins.DataPlugin)
|
return p.(plugins.DataPlugin)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ func TestPluginManager_Init(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Empty(t, pm.scanningErrors)
|
assert.Empty(t, pm.scanningErrors)
|
||||||
assert.Greater(t, len(DataSources), 1)
|
assert.Greater(t, len(pm.dataSources), 1)
|
||||||
assert.Greater(t, len(Panels), 1)
|
assert.Greater(t, len(Panels), 1)
|
||||||
assert.Equal(t, "app/plugins/datasource/graphite/module", DataSources["graphite"].Module)
|
assert.Equal(t, "app/plugins/datasource/graphite/module", pm.dataSources["graphite"].Module)
|
||||||
assert.NotEmpty(t, Apps)
|
assert.NotEmpty(t, Apps)
|
||||||
assert.Equal(t, "public/plugins/test-app/img/logo_large.png", Apps["test-app"].Info.Logos.Large)
|
assert.Equal(t, "public/plugins/test-app/img/logo_large.png", Apps["test-app"].Info.Logos.Large)
|
||||||
assert.Equal(t, "public/plugins/test-app/img/screenshot2.png", Apps["test-app"].Info.Screenshots[1].Path)
|
assert.Equal(t, "public/plugins/test-app/img/screenshot2.png", Apps["test-app"].Info.Screenshots[1].Path)
|
||||||
@@ -114,15 +114,15 @@ func TestPluginManager_Init(t *testing.T) {
|
|||||||
require.Empty(t, pm.scanningErrors)
|
require.Empty(t, pm.scanningErrors)
|
||||||
|
|
||||||
const pluginID = "test"
|
const pluginID = "test"
|
||||||
assert.NotNil(t, Plugins[pluginID])
|
assert.NotNil(t, pm.plugins[pluginID])
|
||||||
assert.Equal(t, "datasource", Plugins[pluginID].Type)
|
assert.Equal(t, "datasource", pm.plugins[pluginID].Type)
|
||||||
assert.Equal(t, "Test", Plugins[pluginID].Name)
|
assert.Equal(t, "Test", pm.plugins[pluginID].Name)
|
||||||
assert.Equal(t, pluginID, Plugins[pluginID].Id)
|
assert.Equal(t, pluginID, pm.plugins[pluginID].Id)
|
||||||
assert.Equal(t, "1.0.0", Plugins[pluginID].Info.Version)
|
assert.Equal(t, "1.0.0", pm.plugins[pluginID].Info.Version)
|
||||||
assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginID].Signature)
|
assert.Equal(t, plugins.PluginSignatureValid, pm.plugins[pluginID].Signature)
|
||||||
assert.Equal(t, plugins.GrafanaType, Plugins[pluginID].SignatureType)
|
assert.Equal(t, plugins.GrafanaType, pm.plugins[pluginID].SignatureType)
|
||||||
assert.Equal(t, "Grafana Labs", Plugins[pluginID].SignatureOrg)
|
assert.Equal(t, "Grafana Labs", pm.plugins[pluginID].SignatureOrg)
|
||||||
assert.False(t, Plugins[pluginID].IsCorePlugin)
|
assert.False(t, pm.plugins[pluginID].IsCorePlugin)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With back-end plugin with invalid v2 private signature (mismatched root URL)", func(t *testing.T) {
|
t.Run("With back-end plugin with invalid v2 private signature (mismatched root URL)", func(t *testing.T) {
|
||||||
@@ -139,7 +139,7 @@ func TestPluginManager_Init(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, []error{fmt.Errorf(`plugin "test" has an invalid signature`)}, pm.scanningErrors)
|
assert.Equal(t, []error{fmt.Errorf(`plugin "test" has an invalid signature`)}, pm.scanningErrors)
|
||||||
assert.Nil(t, Plugins[("test")])
|
assert.Nil(t, pm.plugins[("test")])
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With back-end plugin with valid v2 private signature", func(t *testing.T) {
|
t.Run("With back-end plugin with valid v2 private signature", func(t *testing.T) {
|
||||||
@@ -157,15 +157,15 @@ func TestPluginManager_Init(t *testing.T) {
|
|||||||
require.Empty(t, pm.scanningErrors)
|
require.Empty(t, pm.scanningErrors)
|
||||||
|
|
||||||
const pluginID = "test"
|
const pluginID = "test"
|
||||||
assert.NotNil(t, Plugins[pluginID])
|
assert.NotNil(t, pm.plugins[pluginID])
|
||||||
assert.Equal(t, "datasource", Plugins[pluginID].Type)
|
assert.Equal(t, "datasource", pm.plugins[pluginID].Type)
|
||||||
assert.Equal(t, "Test", Plugins[pluginID].Name)
|
assert.Equal(t, "Test", pm.plugins[pluginID].Name)
|
||||||
assert.Equal(t, pluginID, Plugins[pluginID].Id)
|
assert.Equal(t, pluginID, pm.plugins[pluginID].Id)
|
||||||
assert.Equal(t, "1.0.0", Plugins[pluginID].Info.Version)
|
assert.Equal(t, "1.0.0", pm.plugins[pluginID].Info.Version)
|
||||||
assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginID].Signature)
|
assert.Equal(t, plugins.PluginSignatureValid, pm.plugins[pluginID].Signature)
|
||||||
assert.Equal(t, plugins.PrivateType, Plugins[pluginID].SignatureType)
|
assert.Equal(t, plugins.PrivateType, pm.plugins[pluginID].SignatureType)
|
||||||
assert.Equal(t, "Will Browne", Plugins[pluginID].SignatureOrg)
|
assert.Equal(t, "Will Browne", pm.plugins[pluginID].SignatureOrg)
|
||||||
assert.False(t, Plugins[pluginID].IsCorePlugin)
|
assert.False(t, pm.plugins[pluginID].IsCorePlugin)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With back-end plugin with modified v2 signature (missing file from plugin dir)", func(t *testing.T) {
|
t.Run("With back-end plugin with modified v2 signature (missing file from plugin dir)", func(t *testing.T) {
|
||||||
@@ -181,7 +181,7 @@ func TestPluginManager_Init(t *testing.T) {
|
|||||||
err := pm.Init()
|
err := pm.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
|
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
|
||||||
assert.Nil(t, Plugins[("test")])
|
assert.Nil(t, pm.plugins[("test")])
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With back-end plugin with modified v2 signature (unaccounted file in plugin dir)", func(t *testing.T) {
|
t.Run("With back-end plugin with modified v2 signature (unaccounted file in plugin dir)", func(t *testing.T) {
|
||||||
@@ -197,7 +197,7 @@ func TestPluginManager_Init(t *testing.T) {
|
|||||||
err := pm.Init()
|
err := pm.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
|
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
|
||||||
assert.Nil(t, Plugins[("test")])
|
assert.Nil(t, pm.plugins[("test")])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +260,7 @@ func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager {
|
|||||||
StaticRootPath: staticRootPath,
|
StaticRootPath: staticRootPath,
|
||||||
},
|
},
|
||||||
BackendPluginManager: &fakeBackendPluginManager{},
|
BackendPluginManager: &fakeBackendPluginManager{},
|
||||||
|
dataSources: map[string]*plugins.DataSourcePlugin{},
|
||||||
}
|
}
|
||||||
for _, cb := range cbs {
|
for _, cb := range cbs {
|
||||||
cb(pm)
|
cb(pm)
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
package manager
|
package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (pm *PluginManager) GetPluginSettings(orgId int64) (map[string]*models.PluginSettingInfoDTO, error) {
|
func (pm *PluginManager) GetPluginSettings(orgID int64) (map[string]*models.PluginSettingInfoDTO, error) {
|
||||||
query := models.GetPluginSettingsQuery{OrgId: orgId}
|
pluginSettings, err := pm.SQLStore.GetPluginSettings(orgID)
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginMap := make(map[string]*models.PluginSettingInfoDTO)
|
pluginMap := make(map[string]*models.PluginSettingInfoDTO)
|
||||||
for _, plug := range query.Result {
|
for _, plug := range pluginSettings {
|
||||||
pluginMap[plug.PluginId] = plug
|
pluginMap[plug.PluginId] = plug
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pluginDef := range Plugins {
|
for _, pluginDef := range pm.plugins {
|
||||||
// ignore entries that exists
|
// ignore entries that exists
|
||||||
if _, ok := pluginMap[pluginDef.Id]; ok {
|
if _, ok := pluginMap[pluginDef.Id]; ok {
|
||||||
continue
|
continue
|
||||||
@@ -26,7 +25,7 @@ func (pm *PluginManager) GetPluginSettings(orgId int64) (map[string]*models.Plug
|
|||||||
// default to enabled true
|
// default to enabled true
|
||||||
opt := &models.PluginSettingInfoDTO{
|
opt := &models.PluginSettingInfoDTO{
|
||||||
PluginId: pluginDef.Id,
|
PluginId: pluginDef.Id,
|
||||||
OrgId: orgId,
|
OrgId: orgID,
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ func (pm *PluginManager) GetEnabledPlugins(orgID int64) (*plugins.EnabledPlugins
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add all plugins that are not part of an App.
|
// add all plugins that are not part of an App.
|
||||||
for dsID, ds := range DataSources {
|
for dsID, ds := range pm.dataSources {
|
||||||
if _, exists := pluginSettingMap[ds.Id]; exists {
|
if _, exists := pluginSettingMap[ds.Id]; exists {
|
||||||
enabledPlugins.DataSources[dsID] = ds
|
enabledPlugins.DataSources[dsID] = ds
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,19 @@ var (
|
|||||||
httpClient = http.Client{Timeout: 10 * time.Second}
|
httpClient = http.Client{Timeout: 10 * time.Second}
|
||||||
)
|
)
|
||||||
|
|
||||||
type GrafanaNetPlugin struct {
|
type grafanaNetPlugin struct {
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GithubLatest struct {
|
type gitHubLatest struct {
|
||||||
Stable string `json:"stable"`
|
Stable string `json:"stable"`
|
||||||
Testing string `json:"testing"`
|
Testing string `json:"testing"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllExternalPluginSlugs() string {
|
func (pm *PluginManager) getAllExternalPluginSlugs() string {
|
||||||
var result []string
|
var result []string
|
||||||
for _, plug := range Plugins {
|
for _, plug := range pm.plugins {
|
||||||
if plug.IsCorePlugin {
|
if plug.IsCorePlugin {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ func (pm *PluginManager) checkForUpdates() {
|
|||||||
|
|
||||||
pm.log.Debug("Checking for updates")
|
pm.log.Debug("Checking for updates")
|
||||||
|
|
||||||
pluginSlugs := getAllExternalPluginSlugs()
|
pluginSlugs := pm.getAllExternalPluginSlugs()
|
||||||
resp, err := httpClient.Get("https://grafana.com/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion)
|
resp, err := httpClient.Get("https://grafana.com/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tracef("Failed to get plugins repo from grafana.com, %v", err.Error())
|
log.Tracef("Failed to get plugins repo from grafana.com, %v", err.Error())
|
||||||
@@ -64,14 +64,14 @@ func (pm *PluginManager) checkForUpdates() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gNetPlugins := []GrafanaNetPlugin{}
|
gNetPlugins := []grafanaNetPlugin{}
|
||||||
err = json.Unmarshal(body, &gNetPlugins)
|
err = json.Unmarshal(body, &gNetPlugins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tracef("Failed to unmarshal plugin repo, reading response from grafana.com, %v", err.Error())
|
log.Tracef("Failed to unmarshal plugin repo, reading response from grafana.com, %v", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, plug := range Plugins {
|
for _, plug := range pm.plugins {
|
||||||
for _, gplug := range gNetPlugins {
|
for _, gplug := range gNetPlugins {
|
||||||
if gplug.Slug == plug.Id {
|
if gplug.Slug == plug.Id {
|
||||||
plug.GrafanaNetVersion = gplug.Version
|
plug.GrafanaNetVersion = gplug.Version
|
||||||
@@ -104,24 +104,24 @@ func (pm *PluginManager) checkForUpdates() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var githubLatest GithubLatest
|
var latest gitHubLatest
|
||||||
err = json.Unmarshal(body, &githubLatest)
|
err = json.Unmarshal(body, &latest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tracef("Failed to unmarshal github.com latest, reading response from github.com: %v", err.Error())
|
log.Tracef("Failed to unmarshal github.com latest, reading response from github.com: %v", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(setting.BuildVersion, "-") {
|
if strings.Contains(setting.BuildVersion, "-") {
|
||||||
pm.GrafanaLatestVersion = githubLatest.Testing
|
pm.grafanaLatestVersion = latest.Testing
|
||||||
pm.GrafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
|
pm.grafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, latest.Testing)
|
||||||
} else {
|
} else {
|
||||||
pm.GrafanaLatestVersion = githubLatest.Stable
|
pm.grafanaLatestVersion = latest.Stable
|
||||||
pm.GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
|
pm.grafanaHasUpdate = latest.Stable != setting.BuildVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
currVersion, err1 := version.NewVersion(setting.BuildVersion)
|
currVersion, err1 := version.NewVersion(setting.BuildVersion)
|
||||||
latestVersion, err2 := version.NewVersion(pm.GrafanaLatestVersion)
|
latestVersion, err2 := version.NewVersion(pm.grafanaLatestVersion)
|
||||||
if err1 == nil && err2 == nil {
|
if err1 == nil && err2 == nil {
|
||||||
pm.GrafanaHasUpdate = currVersion.LessThan(latestVersion)
|
pm.grafanaHasUpdate = currVersion.LessThan(latestVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ func init() {
|
|||||||
type Service struct {
|
type Service struct {
|
||||||
DataService *tsdb.Service `inject:""`
|
DataService *tsdb.Service `inject:""`
|
||||||
PluginManager *manager.PluginManager `inject:""`
|
PluginManager *manager.PluginManager `inject:""`
|
||||||
|
SQLStore *sqlstore.SQLStore `inject:""`
|
||||||
|
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
}
|
}
|
||||||
@@ -40,19 +42,19 @@ func (s *Service) Run(ctx context.Context) error {
|
|||||||
func (s *Service) updateAppDashboards() {
|
func (s *Service) updateAppDashboards() {
|
||||||
s.logger.Debug("Looking for app dashboard updates")
|
s.logger.Debug("Looking for app dashboard updates")
|
||||||
|
|
||||||
query := models.GetPluginSettingsQuery{OrgId: 0}
|
pluginSettings, err := s.SQLStore.GetPluginSettings(0)
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get all plugin settings", "error", err)
|
s.logger.Error("Failed to get all plugin settings", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pluginSetting := range query.Result {
|
for _, pluginSetting := range pluginSettings {
|
||||||
// ignore disabled plugins
|
// ignore disabled plugins
|
||||||
if !pluginSetting.Enabled {
|
if !pluginSetting.Enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if pluginDef, exists := manager.Plugins[pluginSetting.PluginId]; exists {
|
if pluginDef := s.PluginManager.GetPlugin(pluginSetting.PluginId); pluginDef != nil {
|
||||||
if pluginDef.Info.Version != pluginSetting.PluginVersion {
|
if pluginDef.Info.Version != pluginSetting.PluginVersion {
|
||||||
s.syncPluginDashboards(pluginDef, pluginSetting.OrgId)
|
s.syncPluginDashboards(pluginDef, pluginSetting.OrgId)
|
||||||
}
|
}
|
||||||
@@ -117,7 +119,7 @@ func (s *Service) handlePluginStateChanged(event *models.PluginStateChangedEvent
|
|||||||
s.logger.Info("Plugin state changed", "pluginId", event.PluginId, "enabled", event.Enabled)
|
s.logger.Info("Plugin state changed", "pluginId", event.PluginId, "enabled", event.Enabled)
|
||||||
|
|
||||||
if event.Enabled {
|
if event.Enabled {
|
||||||
s.syncPluginDashboards(manager.Plugins[event.PluginId], event.OrgId)
|
s.syncPluginDashboards(s.PluginManager.GetPlugin(event.PluginId), event.OrgId)
|
||||||
} else {
|
} else {
|
||||||
query := models.GetDashboardsByPluginIdQuery{PluginId: event.PluginId, OrgId: event.OrgId}
|
query := models.GetDashboardsByPluginIdQuery{PluginId: event.PluginId, OrgId: event.OrgId}
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package alerting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
bus.AddHandler("alerting", updateDashboardAlerts)
|
|
||||||
bus.AddHandler("alerting", validateDashboardAlerts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateDashboardAlerts(cmd *models.ValidateDashboardAlertsCommand) error {
|
|
||||||
extractor := NewDashAlertExtractor(cmd.Dashboard, cmd.OrgId, cmd.User)
|
|
||||||
|
|
||||||
return extractor.ValidateAlerts()
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateDashboardAlerts(cmd *models.UpdateDashboardAlertsCommand) error {
|
|
||||||
saveAlerts := models.SaveAlertsCommand{
|
|
||||||
OrgId: cmd.OrgId,
|
|
||||||
UserId: cmd.User.UserId,
|
|
||||||
DashboardId: cmd.Dashboard.Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
extractor := NewDashAlertExtractor(cmd.Dashboard, cmd.OrgId, cmd.User)
|
|
||||||
|
|
||||||
alerts, err := extractor.GetAlerts()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAlerts.Alerts = alerts
|
|
||||||
|
|
||||||
return bus.Dispatch(&saveAlerts)
|
|
||||||
}
|
|
||||||
@@ -10,10 +10,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/rendering"
|
"github.com/grafana/grafana/pkg/services/rendering"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
"github.com/opentracing/opentracing-go/ext"
|
||||||
tlog "github.com/opentracing/opentracing-go/log"
|
tlog "github.com/opentracing/opentracing-go/log"
|
||||||
@@ -27,7 +27,7 @@ type AlertEngine struct {
|
|||||||
RenderService rendering.Service `inject:""`
|
RenderService rendering.Service `inject:""`
|
||||||
Bus bus.Bus `inject:""`
|
Bus bus.Bus `inject:""`
|
||||||
RequestValidator models.PluginRequestValidator `inject:""`
|
RequestValidator models.PluginRequestValidator `inject:""`
|
||||||
DataService *tsdb.Service `inject:""`
|
DataService plugins.DataRequestHandler `inject:""`
|
||||||
|
|
||||||
execQueue chan *Job
|
execQueue chan *Job
|
||||||
ticker *Ticker
|
ticker *Ticker
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func findPanelQueryByRefID(panel *simplejson.Json, refID string) *simplejson.Jso
|
|||||||
func copyJSON(in json.Marshaler) (*simplejson.Json, error) {
|
func copyJSON(in json.Marshaler) (*simplejson.Json, error) {
|
||||||
rawJSON, err := in.MarshalJSON()
|
rawJSON, err := in.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("JSON marshaling failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return simplejson.NewJson(rawJSON)
|
return simplejson.NewJson(rawJSON)
|
||||||
@@ -242,6 +242,8 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert
|
|||||||
// ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id
|
// ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id
|
||||||
// in the first validation pass.
|
// in the first validation pass.
|
||||||
func (e *DashAlertExtractor) ValidateAlerts() error {
|
func (e *DashAlertExtractor) ValidateAlerts() error {
|
||||||
_, err := e.extractAlerts(func(alert *models.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 }, false)
|
_, err := e.extractAlerts(func(alert *models.Alert) bool {
|
||||||
|
return alert.OrgId != 0 && alert.PanelId != 0
|
||||||
|
}, false)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ package dashboards
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeUserAdmin(bus bus.Bus, orgID int64, userID int64, dashboardID int64, setViewAndEditPermissions bool) error {
|
func (dr *dashboardServiceImpl) MakeUserAdmin(orgID int64, userID int64, dashboardID int64, setViewAndEditPermissions bool) error {
|
||||||
rtEditor := models.ROLE_EDITOR
|
rtEditor := models.ROLE_EDITOR
|
||||||
rtViewer := models.ROLE_VIEWER
|
rtViewer := models.ROLE_VIEWER
|
||||||
|
|
||||||
@@ -43,12 +42,7 @@ func MakeUserAdmin(bus bus.Bus, orgID int64, userID int64, dashboardID int64, se
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
aclCmd := &models.UpdateDashboardAclCommand{
|
if err := dr.dashboardStore.UpdateDashboardACL(dashboardID, items); err != nil {
|
||||||
DashboardID: dashboardID,
|
|
||||||
Items: items,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bus.Dispatch(aclCmd); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package dashboards
|
package dashboards
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/gtime"
|
"github.com/grafana/grafana/pkg/components/gtime"
|
||||||
|
"github.com/grafana/grafana/pkg/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
@@ -20,6 +23,7 @@ type DashboardService interface {
|
|||||||
SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error)
|
SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error)
|
||||||
ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
|
ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error)
|
||||||
DeleteDashboard(dashboardId int64, orgId int64) error
|
DeleteDashboard(dashboardId int64, orgId int64) error
|
||||||
|
MakeUserAdmin(orgID int64, userID, dashboardID int64, setViewAndEditPermissions bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// DashboardProvisioningService is a service for operating on provisioned dashboards.
|
// DashboardProvisioningService is a service for operating on provisioned dashboards.
|
||||||
@@ -27,21 +31,22 @@ type DashboardProvisioningService interface {
|
|||||||
SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
||||||
SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
|
SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
|
||||||
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
||||||
GetProvisionedDashboardDataByDashboardID(DashboardId int64) (*models.DashboardProvisioning, error)
|
GetProvisionedDashboardDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error)
|
||||||
UnprovisionDashboard(dashboardId int64) error
|
UnprovisionDashboard(dashboardID int64) error
|
||||||
DeleteProvisionedDashboard(dashboardId int64, orgId int64) error
|
DeleteProvisionedDashboard(dashboardID int64, orgID int64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService is a factory for creating a new dashboard service.
|
// NewService is a factory for creating a new dashboard service.
|
||||||
var NewService = func() DashboardService {
|
var NewService = func(store dashboards.Store) DashboardService {
|
||||||
return &dashboardServiceImpl{
|
return &dashboardServiceImpl{
|
||||||
log: log.New("dashboard-service"),
|
dashboardStore: store,
|
||||||
|
log: log.New("dashboard-service"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProvisioningService is a factory for creating a new dashboard provisioning service.
|
// NewProvisioningService is a factory for creating a new dashboard provisioning service.
|
||||||
var NewProvisioningService = func() DashboardProvisioningService {
|
var NewProvisioningService = func(store dashboards.Store) DashboardProvisioningService {
|
||||||
return NewService().(*dashboardServiceImpl)
|
return NewService(store).(*dashboardServiceImpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SaveDashboardDTO struct {
|
type SaveDashboardDTO struct {
|
||||||
@@ -54,35 +59,32 @@ type SaveDashboardDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type dashboardServiceImpl struct {
|
type dashboardServiceImpl struct {
|
||||||
orgId int64
|
dashboardStore dashboards.Store
|
||||||
user *models.SignedInUser
|
orgId int64
|
||||||
log log.Logger
|
user *models.SignedInUser
|
||||||
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
||||||
cmd := &models.GetProvisionedDashboardDataQuery{Name: name}
|
return dr.dashboardStore.GetProvisionedDashboardData(name)
|
||||||
err := bus.Dispatch(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(dashboardId int64) (*models.DashboardProvisioning, error) {
|
// GetProvisionedData gets provisioned dashboard data.
|
||||||
cmd := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashboardId}
|
//
|
||||||
err := bus.Dispatch(cmd)
|
// Stubbable by tests.
|
||||||
if err != nil {
|
var GetProvisionedData = func(store dashboards.Store, dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||||
return nil, err
|
return store.GetProvisionedDataByDashboardID(dashboardID)
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool,
|
func (dr *dashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||||
|
return GetProvisionedData(dr.dashboardStore, dashboardID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, shouldValidateAlerts bool,
|
||||||
validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
|
validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
|
||||||
dash := dto.Dashboard
|
dash := dto.Dashboard
|
||||||
|
|
||||||
|
dash.OrgId = dto.OrgId
|
||||||
dash.Title = strings.TrimSpace(dash.Title)
|
dash.Title = strings.TrimSpace(dash.Title)
|
||||||
dash.Data.Set("title", dash.Title)
|
dash.Data.Set("title", dash.Title)
|
||||||
dash.SetUid(strings.TrimSpace(dash.Uid))
|
dash.SetUid(strings.TrimSpace(dash.Uid))
|
||||||
@@ -109,29 +111,18 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if validateAlerts {
|
if shouldValidateAlerts {
|
||||||
validateAlertsCmd := models.ValidateDashboardAlertsCommand{
|
if err := validateAlerts(dash, dto.User); err != nil {
|
||||||
OrgId: dto.OrgId,
|
|
||||||
Dashboard: dash,
|
|
||||||
User: dto.User,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validateBeforeSaveCmd := models.ValidateDashboardBeforeSaveCommand{
|
isParentFolderChanged, err := dr.dashboardStore.ValidateDashboardBeforeSave(dash, dto.Overwrite)
|
||||||
OrgId: dto.OrgId,
|
if err != nil {
|
||||||
Dashboard: dash,
|
|
||||||
Overwrite: dto.Overwrite,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bus.Dispatch(&validateBeforeSaveCmd); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if validateBeforeSaveCmd.Result.IsParentFolderChanged {
|
if isParentFolderChanged {
|
||||||
folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
|
folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
|
||||||
if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
|
if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,6 +169,11 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
|||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
|
extractor := alerting.NewDashAlertExtractor(dash, dash.OrgId, user)
|
||||||
|
return extractor.ValidateAlerts()
|
||||||
|
}
|
||||||
|
|
||||||
func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||||
if setting.MinRefreshInterval == "" {
|
if setting.MinRefreshInterval == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -191,11 +187,11 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
|||||||
|
|
||||||
minRefreshInterval, err := gtime.ParseDuration(setting.MinRefreshInterval)
|
minRefreshInterval, err := gtime.ParseDuration(setting.MinRefreshInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parsing min refresh interval %q failed: %w", setting.MinRefreshInterval, err)
|
||||||
}
|
}
|
||||||
d, err := gtime.ParseDuration(refresh)
|
d, err := gtime.ParseDuration(refresh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parsing refresh duration %q failed: %w", refresh, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d < minRefreshInterval {
|
if d < minRefreshInterval {
|
||||||
@@ -205,14 +201,17 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error {
|
// UpdateAlerting updates alerting.
|
||||||
alertCmd := models.UpdateDashboardAlertsCommand{
|
//
|
||||||
OrgId: dto.OrgId,
|
// Stubbable by tests.
|
||||||
Dashboard: cmd.Result,
|
var UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||||
User: dto.User,
|
extractor := alerting.NewDashAlertExtractor(dashboard, orgID, user)
|
||||||
|
alerts, err := extractor.GetAlerts()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return bus.Dispatch(&alertCmd)
|
return store.SaveAlerts(dashboard.Id, alerts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
||||||
@@ -234,24 +233,18 @@ func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCmd := &models.SaveProvisionedDashboardCommand{
|
|
||||||
DashboardCmd: cmd,
|
|
||||||
DashboardProvisioning: provisioning,
|
|
||||||
}
|
|
||||||
|
|
||||||
// dashboard
|
// dashboard
|
||||||
err = bus.Dispatch(saveCmd)
|
dash, err := dr.dashboardStore.SaveProvisionedDashboard(*cmd, provisioning)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// alerts
|
// alerts
|
||||||
err = dr.updateAlerting(cmd, dto)
|
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.Result, nil
|
return dash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
||||||
@@ -264,22 +257,23 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bus.Dispatch(cmd)
|
dash, err := dr.dashboardStore.SaveDashboard(*cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dr.updateAlerting(cmd, dto)
|
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.Result, nil
|
return dash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
||||||
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
||||||
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval", "dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title, "minRefreshInterval", setting.MinRefreshInterval)
|
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval",
|
||||||
|
"dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title, "minRefreshInterval",
|
||||||
|
setting.MinRefreshInterval)
|
||||||
dto.Dashboard.Data.Set("refresh", setting.MinRefreshInterval)
|
dto.Dashboard.Data.Set("refresh", setting.MinRefreshInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,17 +282,16 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpda
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bus.Dispatch(cmd)
|
dash, err := dr.dashboardStore.SaveDashboard(*cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("saving dashboard failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dr.updateAlerting(cmd, dto)
|
return dash, nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDashboard removes dashboard from the DB. Errors out if the dashboard was provisioned. Should be used for
|
// DeleteDashboard removes dashboard from the DB. Errors out if the dashboard was provisioned. Should be used for
|
||||||
@@ -327,9 +320,12 @@ func (dr *dashboardServiceImpl) deleteDashboard(dashboardId int64, orgId int64,
|
|||||||
return bus.Dispatch(cmd)
|
return bus.Dispatch(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (
|
||||||
|
*models.Dashboard, error) {
|
||||||
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
||||||
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval", "dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title, "minRefreshInterval", setting.MinRefreshInterval)
|
dr.log.Warn("Changing refresh interval for imported dashboard to minimum refresh interval",
|
||||||
|
"dashboardUid", dto.Dashboard.Uid, "dashboardTitle", dto.Dashboard.Title,
|
||||||
|
"minRefreshInterval", setting.MinRefreshInterval)
|
||||||
dto.Dashboard.Data.Set("refresh", setting.MinRefreshInterval)
|
dto.Dashboard.Data.Set("refresh", setting.MinRefreshInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,6 +355,7 @@ type FakeDashboardService struct {
|
|||||||
SaveDashboardResult *models.Dashboard
|
SaveDashboardResult *models.Dashboard
|
||||||
SaveDashboardError error
|
SaveDashboardError error
|
||||||
SavedDashboards []*SaveDashboardDTO
|
SavedDashboards []*SaveDashboardDTO
|
||||||
|
ProvisionedDashData *models.DashboardProvisioning
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FakeDashboardService) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
func (s *FakeDashboardService) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
||||||
@@ -385,8 +382,12 @@ func (s *FakeDashboardService) DeleteDashboard(dashboardId int64, orgId int64) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *FakeDashboardService) GetProvisionedDashboardDataByDashboardID(id int64) (*models.DashboardProvisioning, error) {
|
||||||
|
return s.ProvisionedDashData, nil
|
||||||
|
}
|
||||||
|
|
||||||
func MockDashboardService(mock *FakeDashboardService) {
|
func MockDashboardService(mock *FakeDashboardService) {
|
||||||
NewService = func() DashboardService {
|
NewService = func(dashboards.Store) DashboardService {
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,928 @@
|
|||||||
|
// +build integration
|
||||||
|
|
||||||
|
package dashboards
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testOrgID int64 = 1
|
||||||
|
|
||||||
|
func TestIntegratedDashboardService(t *testing.T) {
|
||||||
|
t.Run("Given saved folders and dashboards in organization A", func(t *testing.T) {
|
||||||
|
origUpdateAlerting := UpdateAlerting
|
||||||
|
t.Cleanup(func() {
|
||||||
|
UpdateAlerting = origUpdateAlerting
|
||||||
|
})
|
||||||
|
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation tests
|
||||||
|
|
||||||
|
permissionScenario(t, "When saving a dashboard with non-existing id", true,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": float64(123412321),
|
||||||
|
"title": "Expect error",
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardNotFound, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Given other organization
|
||||||
|
|
||||||
|
t.Run("Given organization B", func(t *testing.T) {
|
||||||
|
const otherOrgId int64 = 2
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard with same id as dashboard in organization A",
|
||||||
|
true, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: otherOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInFolder.Id,
|
||||||
|
"title": "Expect error",
|
||||||
|
}),
|
||||||
|
Overwrite: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardNotFound, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard with same uid as dashboard in organization A, it should create a new dashboard in org B",
|
||||||
|
true, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
const otherOrgId int64 = 2
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: otherOrgId,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "Dash with existing uid in other org",
|
||||||
|
}),
|
||||||
|
Overwrite: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(0, otherOrgId, sc.savedDashInFolder.Uid, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NotEqual(t, sc.savedDashInFolder.Id, dash.Id)
|
||||||
|
assert.Equal(t, res.Id, dash.Id)
|
||||||
|
assert.Equal(t, otherOrgId, dash.OrgId)
|
||||||
|
assert.Equal(t, sc.savedDashInFolder.Uid, dash.Uid)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Given user has no permission to save", func(t *testing.T) {
|
||||||
|
const canSave = false
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a new dashboard in the General folder", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
sqlStore := sqlstore.InitTestDB(t)
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a new dashboard in other folder, it should create dashboard guardian for other folder with correct arguments and rsult in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: sc.otherSavedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a new dashboard by existing title in folder, it should create dashboard guardian for folder with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.savedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a new dashboard by existing UID in folder, it should create dashboard guardian for folder with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "New dash",
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.savedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating a dashboard by existing id in the General folder, it should create dashboard guardian for dashboard with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInGeneralFolder.Id,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedDashInGeneralFolder.FolderId,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.savedDashInGeneralFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating a dashboard by existing id in other folder, it should create dashboard guardian for dashboard with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInFolder.Id,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedDashInFolder.FolderId,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.savedDashInFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When moving a dashboard by existing ID to other folder from General folder, it should create dashboard guardian for other folder with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInGeneralFolder.Id,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: sc.otherSavedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When moving a dashboard by existing id to the General folder from other folder, it should create dashboard guardian for General folder with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInFolder.Id,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When moving a dashboard by existing uid to other folder from General folder, it should create dashboard guardian for other folder with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInGeneralFolder.Uid,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: sc.otherSavedFolder.Id,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, sc.otherSavedFolder.Id, sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When moving a dashboard by existing UID to the General folder from other folder, it should create dashboard guardian for General folder with correct arguments and result in access denied error",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "Dash",
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
UserId: 10000,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
require.Equal(t, models.ErrDashboardUpdateAccessDenied, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(0), sc.dashboardGuardianMock.DashId)
|
||||||
|
assert.Equal(t, cmd.OrgId, sc.dashboardGuardianMock.OrgId)
|
||||||
|
assert.Equal(t, cmd.UserId, sc.dashboardGuardianMock.User.UserId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Given user has permission to save", func(t *testing.T) {
|
||||||
|
const canSave = true
|
||||||
|
|
||||||
|
t.Run("and overwrite flag is set to false", func(t *testing.T) {
|
||||||
|
const shouldOverwrite = false
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard in General folder with same name as dashboard in other folder",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, res.Id, dash.Id)
|
||||||
|
assert.Equal(t, int64(0), dash.FolderId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard in other folder with same name as dashboard in General folder",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedFolder.Id,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
assert.NotEqual(t, sc.savedDashInGeneralFolder.Id, res.Id)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, sc.savedFolder.Id, dash.FolderId)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a folder with same name as dashboard in other folder",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
IsFolder: true,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
assert.NotEqual(t, sc.savedDashInGeneralFolder.Id, res.Id)
|
||||||
|
assert.True(t, res.IsFolder)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(0), dash.FolderId)
|
||||||
|
assert.True(t, dash.IsFolder)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When saving a dashboard without id and uid and unique title in folder",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": "Dash without id and uid",
|
||||||
|
}),
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
assert.Greater(t, res.Id, int64(0))
|
||||||
|
assert.NotEmpty(t, res.Uid)
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, res.Id, dash.Id)
|
||||||
|
assert.Equal(t, res.Uid, dash.Uid)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When saving a dashboard when dashboard id is zero ", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": 0,
|
||||||
|
"title": "Dash with zero id",
|
||||||
|
}),
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, res.Id, dash.Id)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When saving a dashboard in non-existing folder", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": "Expect error",
|
||||||
|
}),
|
||||||
|
FolderId: 123412321,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardFolderNotFound, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInGeneralFolder.Id,
|
||||||
|
"title": "test dash 23",
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedFolder.Id,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardVersionMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating an existing dashboard by id with current version", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInGeneralFolder.Id,
|
||||||
|
"title": "Updated title",
|
||||||
|
"version": sc.savedDashInGeneralFolder.Version,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedFolder.Id,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInGeneralFolder.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "Updated title", dash.Title)
|
||||||
|
assert.Equal(t, sc.savedFolder.Id, dash.FolderId)
|
||||||
|
assert.Greater(t, dash.Version, sc.savedDashInGeneralFolder.Version)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating an existing dashboard by uid without current version", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "test dash 23",
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardVersionMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating an existing dashboard by uid with current version", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "Updated title",
|
||||||
|
"version": sc.savedDashInFolder.Version,
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInFolder.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "Updated title", dash.Title)
|
||||||
|
assert.Equal(t, int64(0), dash.FolderId)
|
||||||
|
assert.Greater(t, dash.Version, sc.savedDashInFolder.Version)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard with same name as dashboard in other folder",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedDashInFolder.FolderId,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard with same name as dashboard in General folder",
|
||||||
|
canSave, func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedDashInGeneralFolder.FolderId,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a folder with same name as existing folder", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedFolder.Title,
|
||||||
|
}),
|
||||||
|
IsFolder: true,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardWithSameNameInFolderExists, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("and overwrite flag is set to true", func(t *testing.T) {
|
||||||
|
const shouldOverwrite = true
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating an existing dashboard by id without current version", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInGeneralFolder.Id,
|
||||||
|
"title": "Updated title",
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedFolder.Id,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInGeneralFolder.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "Updated title", dash.Title)
|
||||||
|
assert.Equal(t, sc.savedFolder.Id, dash.FolderId)
|
||||||
|
assert.Greater(t, dash.Version, sc.savedDashInGeneralFolder.Version)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating an existing dashboard by uid without current version", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "Updated title",
|
||||||
|
}),
|
||||||
|
FolderId: 0,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInFolder.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "Updated title", dash.Title)
|
||||||
|
assert.Equal(t, int64(0), dash.FolderId)
|
||||||
|
assert.Greater(t, dash.Version, sc.savedDashInFolder.Version)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating uid for existing dashboard using id", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInFolder.Id,
|
||||||
|
"uid": "new-uid",
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
assert.Equal(t, sc.savedDashInFolder.Id, res.Id)
|
||||||
|
assert.Equal(t, "new-uid", res.Uid)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(sc.savedDashInFolder.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "new-uid", dash.Uid)
|
||||||
|
assert.Greater(t, dash.Version, sc.savedDashInFolder.Version)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating uid to an existing uid for existing dashboard using id", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInFolder.Id,
|
||||||
|
"uid": sc.savedDashInGeneralFolder.Uid,
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardWithSameUIDExists, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard with same name as dashboard in other folder", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedDashInFolder.FolderId,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
assert.Equal(t, sc.savedDashInFolder.Id, res.Id)
|
||||||
|
assert.Equal(t, sc.savedDashInFolder.Uid, res.Uid)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, res.Id, dash.Id)
|
||||||
|
assert.Equal(t, res.Uid, dash.Uid)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When creating a dashboard with same name as dashboard in General folder", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: testOrgID,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
|
}),
|
||||||
|
FolderId: sc.savedDashInGeneralFolder.FolderId,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
res := callSaveWithResult(t, cmd, sc.sqlStore)
|
||||||
|
require.NotNil(t, res)
|
||||||
|
assert.Equal(t, sc.savedDashInGeneralFolder.Id, res.Id)
|
||||||
|
assert.Equal(t, sc.savedDashInGeneralFolder.Uid, res.Uid)
|
||||||
|
|
||||||
|
dash, err := sc.sqlStore.GetDashboard(res.Id, cmd.OrgId, "", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, res.Id, dash.Id)
|
||||||
|
assert.Equal(t, res.Uid, dash.Uid)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating existing folder to a dashboard using id", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedFolder.Id,
|
||||||
|
"title": "new title",
|
||||||
|
}),
|
||||||
|
IsFolder: false,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating existing dashboard to a folder using id", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": sc.savedDashInFolder.Id,
|
||||||
|
"title": "new folder title",
|
||||||
|
}),
|
||||||
|
IsFolder: true,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating existing folder to a dashboard using uid", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedFolder.Uid,
|
||||||
|
"title": "new title",
|
||||||
|
}),
|
||||||
|
IsFolder: false,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating existing dashboard to a folder using uid", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"uid": sc.savedDashInFolder.Uid,
|
||||||
|
"title": "new folder title",
|
||||||
|
}),
|
||||||
|
IsFolder: true,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardTypeMismatch, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating existing folder to a dashboard using title", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": sc.savedFolder.Title,
|
||||||
|
}),
|
||||||
|
IsFolder: false,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardWithSameNameAsFolder, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
permissionScenario(t, "When updating existing dashboard to a folder using title", canSave,
|
||||||
|
func(t *testing.T, sc *permissionScenarioContext) {
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: 1,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": sc.savedDashInGeneralFolder.Title,
|
||||||
|
}),
|
||||||
|
IsFolder: true,
|
||||||
|
Overwrite: shouldOverwrite,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := callSaveWithError(cmd, sc.sqlStore)
|
||||||
|
assert.Equal(t, models.ErrDashboardFolderWithSameNameAsDashboard, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type permissionScenarioContext struct {
|
||||||
|
dashboardGuardianMock *guardian.FakeDashboardGuardian
|
||||||
|
sqlStore *sqlstore.SQLStore
|
||||||
|
savedFolder *models.Dashboard
|
||||||
|
savedDashInFolder *models.Dashboard
|
||||||
|
otherSavedFolder *models.Dashboard
|
||||||
|
savedDashInGeneralFolder *models.Dashboard
|
||||||
|
}
|
||||||
|
|
||||||
|
type permissionScenarioFunc func(t *testing.T, sc *permissionScenarioContext)
|
||||||
|
|
||||||
|
func permissionScenario(t *testing.T, desc string, canSave bool, fn permissionScenarioFunc) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
mock := &guardian.FakeDashboardGuardian{
|
||||||
|
CanSaveValue: canSave,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(desc, func(t *testing.T) {
|
||||||
|
sqlStore := sqlstore.InitTestDB(t)
|
||||||
|
|
||||||
|
savedFolder := saveTestFolder(t, "Saved folder", testOrgID, sqlStore)
|
||||||
|
savedDashInFolder := saveTestDashboard(t, "Saved dash in folder", testOrgID, savedFolder.Id, sqlStore)
|
||||||
|
saveTestDashboard(t, "Other saved dash in folder", testOrgID, savedFolder.Id, sqlStore)
|
||||||
|
savedDashInGeneralFolder := saveTestDashboard(t, "Saved dashboard in general folder", testOrgID, 0, sqlStore)
|
||||||
|
otherSavedFolder := saveTestFolder(t, "Other saved folder", testOrgID, sqlStore)
|
||||||
|
|
||||||
|
require.Equal(t, "Saved folder", savedFolder.Title)
|
||||||
|
require.Equal(t, "saved-folder", savedFolder.Slug)
|
||||||
|
require.NotEqual(t, int64(0), savedFolder.Id)
|
||||||
|
require.True(t, savedFolder.IsFolder)
|
||||||
|
require.Equal(t, int64(0), savedFolder.FolderId)
|
||||||
|
require.NotEmpty(t, savedFolder.Uid)
|
||||||
|
|
||||||
|
require.Equal(t, "Saved dash in folder", savedDashInFolder.Title)
|
||||||
|
require.Equal(t, "saved-dash-in-folder", savedDashInFolder.Slug)
|
||||||
|
require.NotEqual(t, int64(0), savedDashInFolder.Id)
|
||||||
|
require.False(t, savedDashInFolder.IsFolder)
|
||||||
|
require.Equal(t, savedFolder.Id, savedDashInFolder.FolderId)
|
||||||
|
require.NotEmpty(t, savedDashInFolder.Uid)
|
||||||
|
|
||||||
|
origNewDashboardGuardian := guardian.New
|
||||||
|
t.Cleanup(func() {
|
||||||
|
guardian.New = origNewDashboardGuardian
|
||||||
|
})
|
||||||
|
guardian.MockDashboardGuardian(mock)
|
||||||
|
|
||||||
|
sc := &permissionScenarioContext{
|
||||||
|
dashboardGuardianMock: mock,
|
||||||
|
sqlStore: sqlStore,
|
||||||
|
savedDashInFolder: savedDashInFolder,
|
||||||
|
otherSavedFolder: otherSavedFolder,
|
||||||
|
savedDashInGeneralFolder: savedDashInGeneralFolder,
|
||||||
|
savedFolder: savedFolder,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(t, sc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func callSaveWithResult(t *testing.T, cmd models.SaveDashboardCommand, sqlStore *sqlstore.SQLStore) *models.Dashboard {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
dto := toSaveDashboardDto(cmd)
|
||||||
|
res, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func callSaveWithError(cmd models.SaveDashboardCommand, sqlStore *sqlstore.SQLStore) error {
|
||||||
|
dto := toSaveDashboardDto(cmd)
|
||||||
|
_, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlStore *sqlstore.SQLStore) *models.Dashboard {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: orgID,
|
||||||
|
FolderId: folderID,
|
||||||
|
IsFolder: false,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": title,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
dto := SaveDashboardDTO{
|
||||||
|
OrgId: orgID,
|
||||||
|
Dashboard: cmd.GetDashboardModel(),
|
||||||
|
User: &models.SignedInUser{
|
||||||
|
UserId: 1,
|
||||||
|
OrgRole: models.ROLE_ADMIN,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore *sqlstore.SQLStore) *models.Dashboard {
|
||||||
|
t.Helper()
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: orgID,
|
||||||
|
FolderId: 0,
|
||||||
|
IsFolder: true,
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"id": nil,
|
||||||
|
"title": title,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
dto := SaveDashboardDTO{
|
||||||
|
OrgId: orgID,
|
||||||
|
Dashboard: cmd.GetDashboardModel(),
|
||||||
|
User: &models.SignedInUser{
|
||||||
|
UserId: 1,
|
||||||
|
OrgRole: models.ROLE_ADMIN,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := NewService(sqlStore).SaveDashboard(&dto, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSaveDashboardDto(cmd models.SaveDashboardCommand) SaveDashboardDTO {
|
||||||
|
dash := (&cmd).GetDashboardModel()
|
||||||
|
|
||||||
|
return SaveDashboardDTO{
|
||||||
|
Dashboard: dash,
|
||||||
|
Message: cmd.Message,
|
||||||
|
OrgId: cmd.OrgId,
|
||||||
|
User: &models.SignedInUser{UserId: cmd.UserId},
|
||||||
|
Overwrite: cmd.Overwrite,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
@@ -17,8 +18,10 @@ func TestDashboardService(t *testing.T) {
|
|||||||
Convey("Dashboard service tests", t, func() {
|
Convey("Dashboard service tests", t, func() {
|
||||||
bus.ClearBusHandlers()
|
bus.ClearBusHandlers()
|
||||||
|
|
||||||
|
fakeStore := fakeDashboardStore{}
|
||||||
service := &dashboardServiceImpl{
|
service := &dashboardServiceImpl{
|
||||||
log: log.New("test.logger"),
|
log: log.New("test.logger"),
|
||||||
|
dashboardStore: &fakeStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
origNewDashboardGuardian := guardian.New
|
origNewDashboardGuardian := guardian.New
|
||||||
@@ -51,19 +54,13 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("When saving a dashboard should validate uid", func() {
|
Convey("When saving a dashboard should validate uid", func() {
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
|
||||||
cmd.Result = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Uid string
|
Uid string
|
||||||
@@ -89,27 +86,23 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||||
provisioningValidated := false
|
t.Cleanup(func() {
|
||||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
fakeStore.provisionedData = nil
|
||||||
provisioningValidated = true
|
|
||||||
cmd.Result = &models.DashboardProvisioning{}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
dto.Dashboard = models.NewDashboard("Dash")
|
dto.Dashboard = models.NewDashboard("Dash")
|
||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
dto.User = &models.SignedInUser{UserId: 1}
|
dto.User = &models.SignedInUser{UserId: 1}
|
||||||
_, err := service.SaveDashboard(dto, false)
|
_, err := service.SaveDashboard(dto, false)
|
||||||
So(provisioningValidated, ShouldBeTrue)
|
|
||||||
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -121,21 +114,20 @@ func TestDashboardService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
dto.Dashboard = models.NewDashboard("Dash")
|
dto.Dashboard = models.NewDashboard("Dash")
|
||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
dto.User = &models.SignedInUser{UserId: 1}
|
dto.User = &models.SignedInUser{UserId: 1}
|
||||||
_, err := service.SaveDashboard(dto, true)
|
_, err := service.SaveDashboard(dto, true)
|
||||||
So(provisioningValidated, ShouldBeFalse)
|
So(provisioningValidated, ShouldBeFalse)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should return validation error if alert data is invalid", func() {
|
Convey("Should return validation error if alert data is invalid", func() {
|
||||||
@@ -144,9 +136,13 @@ func TestDashboardService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
return fmt.Errorf("alert validation error")
|
t.Cleanup(func() {
|
||||||
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
|
return fmt.Errorf("alert validation error")
|
||||||
|
}
|
||||||
|
|
||||||
dto.Dashboard = models.NewDashboard("Dash")
|
dto.Dashboard = models.NewDashboard("Dash")
|
||||||
_, err := service.SaveDashboard(dto, false)
|
_, err := service.SaveDashboard(dto, false)
|
||||||
@@ -165,23 +161,27 @@ func TestDashboardService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origUpdateAlerting := UpdateAlerting
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
UpdateAlerting = origUpdateAlerting
|
||||||
})
|
})
|
||||||
|
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||||
|
user *models.SignedInUser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
t.Cleanup(func() {
|
||||||
return nil
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
dto.Dashboard = models.NewDashboard("Dash")
|
dto.Dashboard = models.NewDashboard("Dash")
|
||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
dto.User = &models.SignedInUser{UserId: 1}
|
dto.User = &models.SignedInUser{UserId: 1}
|
||||||
@@ -200,22 +200,26 @@ func TestDashboardService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
origUpdateAlerting := UpdateAlerting
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
UpdateAlerting = origUpdateAlerting
|
||||||
})
|
})
|
||||||
|
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||||
|
user *models.SignedInUser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
dto.Dashboard = models.NewDashboard("Dash")
|
dto.Dashboard = models.NewDashboard("Dash")
|
||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
@@ -231,41 +235,42 @@ func TestDashboardService(t *testing.T) {
|
|||||||
dto := &SaveDashboardDTO{}
|
dto := &SaveDashboardDTO{}
|
||||||
|
|
||||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||||
provisioningValidated := false
|
t.Cleanup(func() {
|
||||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
fakeStore.provisionedData = nil
|
||||||
provisioningValidated = true
|
|
||||||
cmd.Result = &models.DashboardProvisioning{}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origValidateAlerts := validateAlerts
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
validateAlerts = origValidateAlerts
|
||||||
})
|
})
|
||||||
|
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
origUpdateAlerting := UpdateAlerting
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
UpdateAlerting = origUpdateAlerting
|
||||||
})
|
})
|
||||||
|
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||||
|
user *models.SignedInUser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
dto.Dashboard = models.NewDashboard("Dash")
|
dto.Dashboard = models.NewDashboard("Dash")
|
||||||
dto.Dashboard.SetId(3)
|
dto.Dashboard.SetId(3)
|
||||||
dto.User = &models.SignedInUser{UserId: 1}
|
dto.User = &models.SignedInUser{UserId: 1}
|
||||||
_, err := service.ImportDashboard(dto)
|
_, err := service.ImportDashboard(dto)
|
||||||
So(provisioningValidated, ShouldBeTrue)
|
|
||||||
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given provisioned dashboard", func() {
|
Convey("Given provisioned dashboard", func() {
|
||||||
result := setupDeleteHandlers(true)
|
result := setupDeleteHandlers(t, &fakeStore, true)
|
||||||
|
|
||||||
Convey("DeleteProvisionedDashboard should delete it", func() {
|
Convey("DeleteProvisionedDashboard should delete it", func() {
|
||||||
err := service.DeleteProvisionedDashboard(1, 1)
|
err := service.DeleteProvisionedDashboard(1, 1)
|
||||||
@@ -281,7 +286,7 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given non provisioned dashboard", func() {
|
Convey("Given non provisioned dashboard", func() {
|
||||||
result := setupDeleteHandlers(false)
|
result := setupDeleteHandlers(t, &fakeStore, false)
|
||||||
|
|
||||||
Convey("DeleteProvisionedDashboard should delete it", func() {
|
Convey("DeleteProvisionedDashboard should delete it", func() {
|
||||||
err := service.DeleteProvisionedDashboard(1, 1)
|
err := service.DeleteProvisionedDashboard(1, 1)
|
||||||
@@ -306,15 +311,15 @@ type Result struct {
|
|||||||
deleteWasCalled bool
|
deleteWasCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDeleteHandlers(provisioned bool) *Result {
|
func setupDeleteHandlers(t *testing.T, fakeStore *fakeDashboardStore, provisioned bool) *Result {
|
||||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
t.Helper()
|
||||||
if provisioned {
|
|
||||||
cmd.Result = &models.DashboardProvisioning{}
|
t.Cleanup(func() {
|
||||||
} else {
|
fakeStore.provisionedData = nil
|
||||||
cmd.Result = nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
if provisioned {
|
||||||
|
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||||
|
}
|
||||||
|
|
||||||
result := &Result{}
|
result := &Result{}
|
||||||
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
||||||
@@ -326,3 +331,32 @@ func setupDeleteHandlers(provisioned bool) *Result {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeDashboardStore struct {
|
||||||
|
dashboards.Store
|
||||||
|
|
||||||
|
validationError error
|
||||||
|
provisionedData *models.DashboardProvisioning
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeDashboardStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (
|
||||||
|
bool, error) {
|
||||||
|
return false, s.validationError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeDashboardStore) GetProvisionedDataByDashboardID(int64) (*models.DashboardProvisioning, error) {
|
||||||
|
return s.provisionedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeDashboardStore) SaveProvisionedDashboard(models.SaveDashboardCommand,
|
||||||
|
*models.DashboardProvisioning) (*models.Dashboard, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeDashboardStore) SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) {
|
||||||
|
return cmd.GetDashboardModel(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeDashboardStore) SaveAlerts(dashID int64, alerts []*models.Alert) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,28 +2,32 @@ package dashboards
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/search"
|
"github.com/grafana/grafana/pkg/services/search"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FolderService service for operating on folders
|
// FolderService is a service for operating on folders.
|
||||||
type FolderService interface {
|
type FolderService interface {
|
||||||
GetFolders(limit int64) ([]*models.Folder, error)
|
GetFolders(limit int64) ([]*models.Folder, error)
|
||||||
GetFolderByID(id int64) (*models.Folder, error)
|
GetFolderByID(id int64) (*models.Folder, error)
|
||||||
GetFolderByUID(uid string) (*models.Folder, error)
|
GetFolderByUID(uid string) (*models.Folder, error)
|
||||||
CreateFolder(cmd *models.CreateFolderCommand) error
|
CreateFolder(title, uid string) (*models.Folder, error)
|
||||||
UpdateFolder(uid string, cmd *models.UpdateFolderCommand) error
|
UpdateFolder(uid string, cmd *models.UpdateFolderCommand) error
|
||||||
DeleteFolder(uid string) (*models.Folder, error)
|
DeleteFolder(uid string) (*models.Folder, error)
|
||||||
|
MakeUserAdmin(orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFolderService factory for creating a new folder service
|
// NewFolderService is a factory for creating a new folder service.
|
||||||
var NewFolderService = func(orgId int64, user *models.SignedInUser) FolderService {
|
var NewFolderService = func(orgID int64, user *models.SignedInUser, store dashboards.Store) FolderService {
|
||||||
return &dashboardServiceImpl{
|
return &dashboardServiceImpl{
|
||||||
orgId: orgId,
|
orgId: orgID,
|
||||||
user: user,
|
user: user,
|
||||||
|
dashboardStore: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +62,6 @@ func (dr *dashboardServiceImpl) GetFolders(limit int64) ([]*models.Folder, error
|
|||||||
func (dr *dashboardServiceImpl) GetFolderByID(id int64) (*models.Folder, error) {
|
func (dr *dashboardServiceImpl) GetFolderByID(id int64) (*models.Folder, error) {
|
||||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: id}
|
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: id}
|
||||||
dashFolder, err := getFolder(query)
|
dashFolder, err := getFolder(query)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
@@ -93,8 +96,17 @@ func (dr *dashboardServiceImpl) GetFolderByUID(uid string) (*models.Folder, erro
|
|||||||
return dashToFolder(dashFolder), nil
|
return dashToFolder(dashFolder), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) error {
|
func (dr *dashboardServiceImpl) CreateFolder(title, uid string) (*models.Folder, error) {
|
||||||
dashFolder := cmd.GetDashboardModel(dr.orgId, dr.user.UserId)
|
dashFolder := models.NewDashboardFolder(title)
|
||||||
|
dashFolder.OrgId = dr.orgId
|
||||||
|
dashFolder.SetUid(strings.TrimSpace(uid))
|
||||||
|
userID := dr.user.UserId
|
||||||
|
if userID == 0 {
|
||||||
|
userID = -1
|
||||||
|
}
|
||||||
|
dashFolder.CreatedBy = userID
|
||||||
|
dashFolder.UpdatedBy = userID
|
||||||
|
dashFolder.UpdateSlug()
|
||||||
|
|
||||||
dto := &SaveDashboardDTO{
|
dto := &SaveDashboardDTO{
|
||||||
Dashboard: dashFolder,
|
Dashboard: dashFolder,
|
||||||
@@ -104,23 +116,21 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er
|
|||||||
|
|
||||||
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false, false)
|
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bus.Dispatch(saveDashboardCmd)
|
dash, err := dr.dashboardStore.SaveDashboard(*saveDashboardCmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id}
|
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: dash.Id}
|
||||||
dashFolder, err = getFolder(query)
|
dashFolder, err = getFolder(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return nil, toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Result = dashToFolder(dashFolder)
|
return dashToFolder(dashFolder), nil
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.UpdateFolderCommand) error {
|
func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.UpdateFolderCommand) error {
|
||||||
@@ -144,12 +154,12 @@ func (dr *dashboardServiceImpl) UpdateFolder(existingUid string, cmd *models.Upd
|
|||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bus.Dispatch(saveDashboardCmd)
|
dash, err := dr.dashboardStore.SaveDashboard(*saveDashboardCmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
query = models.GetDashboardQuery{OrgId: dr.orgId, Id: saveDashboardCmd.Result.Id}
|
query = models.GetDashboardQuery{OrgId: dr.orgId, Id: dash.Id}
|
||||||
dashFolder, err = getFolder(query)
|
dashFolder, err = getFolder(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return toFolderError(err)
|
return toFolderError(err)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
@@ -15,8 +16,9 @@ import (
|
|||||||
func TestFolderService(t *testing.T) {
|
func TestFolderService(t *testing.T) {
|
||||||
Convey("Folder service tests", t, func() {
|
Convey("Folder service tests", t, func() {
|
||||||
service := dashboardServiceImpl{
|
service := dashboardServiceImpl{
|
||||||
orgId: 1,
|
orgId: 1,
|
||||||
user: &models.SignedInUser{UserId: 1},
|
user: &models.SignedInUser{UserId: 1},
|
||||||
|
dashboardStore: &fakeDashboardStore{},
|
||||||
}
|
}
|
||||||
|
|
||||||
Convey("Given user has no permissions", func() {
|
Convey("Given user has no permissions", func() {
|
||||||
@@ -28,32 +30,26 @@ func TestFolderService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origStore := service.dashboardStore
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
})
|
service.dashboardStore = origStore
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return models.ErrDashboardUpdateAccessDenied
|
|
||||||
})
|
})
|
||||||
|
service.dashboardStore = &fakeDashboardStore{
|
||||||
|
validationError: models.ErrDashboardUpdateAccessDenied,
|
||||||
|
}
|
||||||
|
|
||||||
Convey("When get folder by id should return access denied error", func() {
|
Convey("When get folder by id should return access denied error", func() {
|
||||||
_, err := service.GetFolderByID(1)
|
_, err := service.GetFolderByID(1)
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When get folder by uid should return access denied error", func() {
|
Convey("When get folder by uid should return access denied error", func() {
|
||||||
_, err := service.GetFolderByUID("uid")
|
_, err := service.GetFolderByUID("uid")
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When creating folder should return access denied error", func() {
|
Convey("When creating folder should return access denied error", func() {
|
||||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
_, err := service.CreateFolder("Folder", "")
|
||||||
Title: "Folder",
|
|
||||||
})
|
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -62,7 +58,6 @@ func TestFolderService(t *testing.T) {
|
|||||||
Uid: "uid",
|
Uid: "uid",
|
||||||
Title: "Folder",
|
Title: "Folder",
|
||||||
})
|
})
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -89,18 +84,14 @@ func TestFolderService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origUpdateAlerting := UpdateAlerting
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
UpdateAlerting = origUpdateAlerting
|
||||||
})
|
})
|
||||||
|
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
user *models.SignedInUser) error {
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.SaveDashboardCommand) error {
|
bus.AddHandler("test", func(cmd *models.SaveDashboardCommand) error {
|
||||||
cmd.Result = dash
|
cmd.Result = dash
|
||||||
@@ -120,9 +111,7 @@ func TestFolderService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("When creating folder should not return access denied error", func() {
|
Convey("When creating folder should not return access denied error", func() {
|
||||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
_, err := service.CreateFolder("Folder", "")
|
||||||
Title: "Folder",
|
|
||||||
})
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(provisioningValidated, ShouldBeFalse)
|
So(provisioningValidated, ShouldBeFalse)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ func (lps *LibraryPanelService) createLibraryPanel(c *models.ReqContext, cmd cre
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||||
if err := requirePermissionsOnFolder(c.SignedInUser, cmd.FolderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(c.SignedInUser, cmd.FolderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := session.Insert(&libraryPanel); err != nil {
|
if _, err := session.Insert(&libraryPanel); err != nil {
|
||||||
@@ -108,12 +107,22 @@ func (lps *LibraryPanelService) createLibraryPanel(c *models.ReqContext, cmd cre
|
|||||||
return dto, err
|
return dto, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectDashboard(session *sqlstore.DBSession, dialect migrator.Dialect, user *models.SignedInUser, uid string, dashboardID int64) error {
|
// connectDashboard adds a connection between a Library Panel and a Dashboard.
|
||||||
|
func (lps *LibraryPanelService) connectDashboard(c *models.ReqContext, uid string, dashboardID int64) error {
|
||||||
|
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||||
|
return lps.implConnectDashboard(session, c.SignedInUser, uid, dashboardID)
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lps *LibraryPanelService) implConnectDashboard(session *sqlstore.DBSession, user *models.SignedInUser,
|
||||||
|
uid string, dashboardID int64) error {
|
||||||
panel, err := getLibraryPanel(session, uid, user.OrgId)
|
panel, err := getLibraryPanel(session, uid, user.OrgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := requirePermissionsOnFolder(user, panel.FolderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(user, panel.FolderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +135,7 @@ func connectDashboard(session *sqlstore.DBSession, dialect migrator.Dialect, use
|
|||||||
CreatedBy: user.UserId,
|
CreatedBy: user.UserId,
|
||||||
}
|
}
|
||||||
if _, err := session.Insert(&libraryPanelDashboard); err != nil {
|
if _, err := session.Insert(&libraryPanelDashboard); err != nil {
|
||||||
if dialect.IsUniqueConstraintViolation(err) {
|
if lps.SQLStore.Dialect.IsUniqueConstraintViolation(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -134,15 +143,6 @@ func connectDashboard(session *sqlstore.DBSession, dialect migrator.Dialect, use
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// connectDashboard adds a connection between a Library Panel and a Dashboard.
|
|
||||||
func (lps *LibraryPanelService) connectDashboard(c *models.ReqContext, uid string, dashboardID int64) error {
|
|
||||||
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
|
||||||
return connectDashboard(session, lps.SQLStore.Dialect, c.SignedInUser, uid, dashboardID)
|
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// connectLibraryPanelsForDashboard adds connections for all Library Panels in a Dashboard.
|
// connectLibraryPanelsForDashboard adds connections for all Library Panels in a Dashboard.
|
||||||
func (lps *LibraryPanelService) connectLibraryPanelsForDashboard(c *models.ReqContext, uids []string, dashboardID int64) error {
|
func (lps *LibraryPanelService) connectLibraryPanelsForDashboard(c *models.ReqContext, uids []string, dashboardID int64) error {
|
||||||
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||||
@@ -151,7 +151,7 @@ func (lps *LibraryPanelService) connectLibraryPanelsForDashboard(c *models.ReqCo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, uid := range uids {
|
for _, uid := range uids {
|
||||||
err := connectDashboard(session, lps.SQLStore.Dialect, c.SignedInUser, uid, dashboardID)
|
err := lps.implConnectDashboard(session, c.SignedInUser, uid, dashboardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ func (lps *LibraryPanelService) deleteLibraryPanel(c *models.ReqContext, uid str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := session.Exec("DELETE FROM library_panel_dashboard WHERE librarypanel_id=?", panel.ID); err != nil {
|
if _, err := session.Exec("DELETE FROM library_panel_dashboard WHERE librarypanel_id=?", panel.ID); err != nil {
|
||||||
@@ -197,7 +197,7 @@ func (lps *LibraryPanelService) disconnectDashboard(c *models.ReqContext, uid st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ func (lps *LibraryPanelService) deleteLibraryPanelsInFolder(c *models.ReqContext
|
|||||||
}
|
}
|
||||||
folderID := folderUIDs[0].ID
|
folderID := folderUIDs[0].ID
|
||||||
|
|
||||||
if err := requirePermissionsOnFolder(c.SignedInUser, folderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(c.SignedInUser, folderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var dashIDs []struct {
|
var dashIDs []struct {
|
||||||
@@ -496,7 +496,8 @@ func (lps *LibraryPanelService) getLibraryPanelsForDashboardID(c *models.ReqCont
|
|||||||
return libraryPanelMap, err
|
return libraryPanelMap, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleFolderIDPatches(panelToPatch *LibraryPanel, fromFolderID int64, toFolderID int64, user *models.SignedInUser) error {
|
func (lps *LibraryPanelService) handleFolderIDPatches(panelToPatch *LibraryPanel, fromFolderID int64,
|
||||||
|
toFolderID int64, user *models.SignedInUser) error {
|
||||||
// FolderID was not provided in the PATCH request
|
// FolderID was not provided in the PATCH request
|
||||||
if toFolderID == -1 {
|
if toFolderID == -1 {
|
||||||
toFolderID = fromFolderID
|
toFolderID = fromFolderID
|
||||||
@@ -504,13 +505,13 @@ func handleFolderIDPatches(panelToPatch *LibraryPanel, fromFolderID int64, toFol
|
|||||||
|
|
||||||
// FolderID was provided in the PATCH request
|
// FolderID was provided in the PATCH request
|
||||||
if toFolderID != -1 && toFolderID != fromFolderID {
|
if toFolderID != -1 && toFolderID != fromFolderID {
|
||||||
if err := requirePermissionsOnFolder(user, toFolderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(user, toFolderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always check permissions for the folder where library panel resides
|
// Always check permissions for the folder where library panel resides
|
||||||
if err := requirePermissionsOnFolder(user, fromFolderID); err != nil {
|
if err := lps.requirePermissionsOnFolder(user, fromFolderID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +552,7 @@ func (lps *LibraryPanelService) patchLibraryPanel(c *models.ReqContext, cmd patc
|
|||||||
if cmd.Model == nil {
|
if cmd.Model == nil {
|
||||||
libraryPanel.Model = panelInDB.Model
|
libraryPanel.Model = panelInDB.Model
|
||||||
}
|
}
|
||||||
if err := handleFolderIDPatches(&libraryPanel, panelInDB.FolderID, cmd.FolderID, c.SignedInUser); err != nil {
|
if err := lps.handleFolderIDPatches(&libraryPanel, panelInDB.FolderID, cmd.FolderID, c.SignedInUser); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := syncTitleWithName(&libraryPanel); err != nil {
|
if err := syncTitleWithName(&libraryPanel); err != nil {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ func isGeneralFolder(folderID int64) bool {
|
|||||||
return folderID == 0
|
return folderID == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func requirePermissionsOnFolder(user *models.SignedInUser, folderID int64) error {
|
func (lps *LibraryPanelService) requirePermissionsOnFolder(user *models.SignedInUser, folderID int64) error {
|
||||||
if isGeneralFolder(folderID) && user.HasRole(models.ROLE_EDITOR) {
|
if isGeneralFolder(folderID) && user.HasRole(models.ROLE_EDITOR) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ func requirePermissionsOnFolder(user *models.SignedInUser, folderID int64) error
|
|||||||
return models.ErrFolderAccessDenied
|
return models.ErrFolderAccessDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
s := dashboards.NewFolderService(user.OrgId, user)
|
s := dashboards.NewFolderService(user.OrgId, user, lps.SQLStore)
|
||||||
folder, err := s.GetFolderByID(folderID)
|
folder, err := s.GetFolderByID(folderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ func TestGetConnectedDashboards(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that exists and has connections, it should return connected dashboard IDs",
|
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that exists and has connections, it should return connected dashboard IDs",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
firstDash := createDashboard(t, sc.user, "Dash 1", 0)
|
firstDash := createDashboard(t, sc.sqlStore, sc.user, "Dash 1", 0)
|
||||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": strconv.FormatInt(firstDash.Id, 10)})
|
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": strconv.FormatInt(firstDash.Id, 10)})
|
||||||
resp := sc.service.connectHandler(sc.reqContext)
|
resp := sc.service.connectHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
secondDash := createDashboard(t, sc.user, "Dash 2", 0)
|
secondDash := createDashboard(t, sc.sqlStore, sc.user, "Dash 2", 0)
|
||||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": strconv.FormatInt(secondDash.Id, 10)})
|
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": strconv.FormatInt(secondDash.Id, 10)})
|
||||||
resp = sc.service.connectHandler(sc.reqContext)
|
resp = sc.service.connectHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func TestPatchLibraryPanel(t *testing.T) {
|
|||||||
resp = sc.service.connectHandler(sc.reqContext)
|
resp = sc.service.connectHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
|
|
||||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||||
cmd := patchLibraryPanelCommand{
|
cmd := patchLibraryPanelCommand{
|
||||||
FolderID: newFolder.Id,
|
FolderID: newFolder.Id,
|
||||||
Name: "Panel - New name",
|
Name: "Panel - New name",
|
||||||
@@ -82,7 +82,7 @@ func TestPatchLibraryPanel(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result",
|
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||||
cmd := patchLibraryPanelCommand{
|
cmd := patchLibraryPanelCommand{
|
||||||
FolderID: newFolder.Id,
|
FolderID: newFolder.Id,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
@@ -174,7 +174,7 @@ func TestPatchLibraryPanel(t *testing.T) {
|
|||||||
|
|
||||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
|
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||||
command := getCreateCommand(newFolder.Id, "Text - Library Panel")
|
command := getCreateCommand(newFolder.Id, "Text - Library Panel")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
var result = validateAndUnMarshalResponse(t, resp)
|
var result = validateAndUnMarshalResponse(t, resp)
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
for _, testCase := range accessCases {
|
for _, testCase := range accessCases {
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to create a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to create a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||||
|
|
||||||
command := getCreateCommand(folder.Id, "Library Panel Name")
|
command := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
@@ -78,11 +78,11 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
fromFolder := createFolderWithACL(t, "Everyone", sc.user, everyonePermissions)
|
fromFolder := createFolderWithACL(t, sc.sqlStore, "Everyone", sc.user, everyonePermissions)
|
||||||
command := getCreateCommand(fromFolder.Id, "Library Panel Name")
|
command := getCreateCommand(fromFolder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
toFolder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||||
|
|
||||||
cmd := patchLibraryPanelCommand{FolderID: toFolder.Id, Version: 1}
|
cmd := patchLibraryPanelCommand{FolderID: toFolder.Id, Version: 1}
|
||||||
@@ -93,11 +93,11 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
fromFolder := createFolderWithACL(t, "Everyone", sc.user, testCase.items)
|
fromFolder := createFolderWithACL(t, sc.sqlStore, "Everyone", sc.user, testCase.items)
|
||||||
command := getCreateCommand(fromFolder.Id, "Library Panel Name")
|
command := getCreateCommand(fromFolder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
toFolder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||||
|
|
||||||
cmd := patchLibraryPanelCommand{FolderID: toFolder.Id, Version: 1}
|
cmd := patchLibraryPanelCommand{FolderID: toFolder.Id, Version: 1}
|
||||||
@@ -108,7 +108,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to delete a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to delete a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -121,8 +121,8 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to connect a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to connect a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
dashboard := createDashboard(t, sc.sqlStore, sc.user, "Some Folder Dash", folder.Id)
|
||||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -135,8 +135,8 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to disconnect a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to disconnect a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
dashboard := createDashboard(t, sc.sqlStore, sc.user, "Some Folder Dash", folder.Id)
|
||||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -152,7 +152,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to delete all library panels in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
testScenario(t, fmt.Sprintf("When %s tries to delete all library panels in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
validateAndUnMarshalResponse(t, resp)
|
validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -191,7 +191,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to the General folder, it should return correct status", testCase.role),
|
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to the General folder, it should return correct status", testCase.role),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||||
command := getCreateCommand(folder.Id, "Library Panel Name")
|
command := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -205,7 +205,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from the General folder, it should return correct status", testCase.role),
|
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from the General folder, it should return correct status", testCase.role),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||||
command := getCreateCommand(0, "Library Panel Name")
|
command := getCreateCommand(0, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -231,7 +231,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to connect a library panel in the General folder, it should return correct status", testCase.role),
|
testScenario(t, fmt.Sprintf("When %s tries to connect a library panel in the General folder, it should return correct status", testCase.role),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashboard := createDashboard(t, sc.user, "General Folder Dash", 0)
|
dashboard := createDashboard(t, sc.sqlStore, sc.user, "General Folder Dash", 0)
|
||||||
cmd := getCreateCommand(0, "Library Panel Name")
|
cmd := getCreateCommand(0, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -244,7 +244,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to disconnect a library panel in the General folder, it should return correct status", testCase.role),
|
testScenario(t, fmt.Sprintf("When %s tries to disconnect a library panel in the General folder, it should return correct status", testCase.role),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashboard := createDashboard(t, sc.user, "General Folder Dash", 0)
|
dashboard := createDashboard(t, sc.sqlStore, sc.user, "General Folder Dash", 0)
|
||||||
cmd := getCreateCommand(0, "Library Panel Name")
|
cmd := getCreateCommand(0, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -260,7 +260,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to get connected dashboards in the General folder for a library panel in the General folder, it should return correct status", testCase.role),
|
testScenario(t, fmt.Sprintf("When %s tries to get connected dashboards in the General folder for a library panel in the General folder, it should return correct status", testCase.role),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashboard := createDashboard(t, sc.user, "General Folder Dash", 0)
|
dashboard := createDashboard(t, sc.sqlStore, sc.user, "General Folder Dash", 0)
|
||||||
cmd := getCreateCommand(0, "Library Panel Name")
|
cmd := getCreateCommand(0, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -301,7 +301,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
|
|
||||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder that doesn't exist, it should fail", testCase.role),
|
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder that doesn't exist, it should fail", testCase.role),
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
folder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
|
||||||
command := getCreateCommand(folder.Id, "Library Panel Name")
|
command := getCreateCommand(folder.Id, "Library Panel Name")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -329,7 +329,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
var results []libraryPanel
|
var results []libraryPanel
|
||||||
for i, folderCase := range folderCases {
|
for i, folderCase := range folderCases {
|
||||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||||
cmd := getCreateCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
cmd := getCreateCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
@@ -401,8 +401,8 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
for i, folderCase := range folderCases {
|
for i, folderCase := range folderCases {
|
||||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
dashboard := createDashboard(t, sc.sqlStore, sc.user, "Some Folder Dash", folder.Id)
|
||||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||||
resp = sc.service.connectHandler(sc.reqContext)
|
resp = sc.service.connectHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
@@ -434,7 +434,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
|||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
var results []libraryPanel
|
var results []libraryPanel
|
||||||
for i, folderCase := range folderCases {
|
for i, folderCase := range folderCases {
|
||||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||||
cmd := getCreateCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
cmd := getCreateCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||||
result := validateAndUnMarshalResponse(t, resp)
|
result := validateAndUnMarshalResponse(t, resp)
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
"gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@@ -731,6 +731,7 @@ type scenarioContext struct {
|
|||||||
user models.SignedInUser
|
user models.SignedInUser
|
||||||
folder *models.Folder
|
folder *models.Folder
|
||||||
initialResult libraryPanelResult
|
initialResult libraryPanelResult
|
||||||
|
sqlStore *sqlstore.SQLStore
|
||||||
}
|
}
|
||||||
|
|
||||||
type folderACLItem struct {
|
type folderACLItem struct {
|
||||||
@@ -738,7 +739,8 @@ type folderACLItem struct {
|
|||||||
permission models.PermissionType
|
permission models.PermissionType
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDashboard(t *testing.T, user models.SignedInUser, title string, folderID int64) *models.Dashboard {
|
func createDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, user models.SignedInUser, title string,
|
||||||
|
folderID int64) *models.Dashboard {
|
||||||
dash := models.NewDashboard(title)
|
dash := models.NewDashboard(title)
|
||||||
dash.FolderId = folderID
|
dash.FolderId = folderID
|
||||||
dashItem := &dashboards.SaveDashboardDTO{
|
dashItem := &dashboards.SaveDashboardDTO{
|
||||||
@@ -748,53 +750,47 @@ func createDashboard(t *testing.T, user models.SignedInUser, title string, folde
|
|||||||
User: &user,
|
User: &user,
|
||||||
Overwrite: false,
|
Overwrite: false,
|
||||||
}
|
}
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
origUpdateAlerting := dashboards.UpdateAlerting
|
||||||
return nil
|
t.Cleanup(func() {
|
||||||
|
dashboards.UpdateAlerting = origUpdateAlerting
|
||||||
})
|
})
|
||||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
dashboards.UpdateAlerting = func(store dboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
user *models.SignedInUser) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
}
|
||||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
|
||||||
cmd.Result = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
dashboard, err := dashboards.NewService().SaveDashboard(dashItem, true)
|
dashboard, err := dashboards.NewService(sqlStore).SaveDashboard(dashItem, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return dashboard
|
return dashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFolderWithACL(t *testing.T, title string, user models.SignedInUser, items []folderACLItem) *models.Folder {
|
func createFolderWithACL(t *testing.T, sqlStore *sqlstore.SQLStore, title string, user models.SignedInUser,
|
||||||
s := dashboards.NewFolderService(user.OrgId, &user)
|
items []folderACLItem) *models.Folder {
|
||||||
folderCmd := models.CreateFolderCommand{
|
t.Helper()
|
||||||
Uid: title,
|
|
||||||
Title: title,
|
s := dashboards.NewFolderService(user.OrgId, &user, sqlStore)
|
||||||
}
|
t.Logf("Creating folder with title and UID %q", title)
|
||||||
err := s.CreateFolder(&folderCmd)
|
folder, err := s.CreateFolder(title, title)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
updateFolderACL(t, folderCmd.Result.Id, items)
|
updateFolderACL(t, sqlStore, folder.Id, items)
|
||||||
|
|
||||||
return folderCmd.Result
|
return folder
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFolderACL(t *testing.T, folderID int64, items []folderACLItem) {
|
func updateFolderACL(t *testing.T, sqlStore *sqlstore.SQLStore, folderID int64, items []folderACLItem) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := models.UpdateDashboardAclCommand{
|
var aclItems []*models.DashboardAcl
|
||||||
DashboardID: folderID,
|
|
||||||
}
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
role := item.roleType
|
role := item.roleType
|
||||||
permission := item.permission
|
permission := item.permission
|
||||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
aclItems = append(aclItems, &models.DashboardAcl{
|
||||||
DashboardID: folderID,
|
DashboardID: folderID,
|
||||||
Role: &role,
|
Role: &role,
|
||||||
Permission: permission,
|
Permission: permission,
|
||||||
@@ -803,11 +799,13 @@ func updateFolderACL(t *testing.T, folderID int64, items []folderACLItem) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := bus.Dispatch(&cmd)
|
err := sqlStore.UpdateDashboardACL(folderID, aclItems)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateAndUnMarshalResponse(t *testing.T, resp response.Response) libraryPanelResult {
|
func validateAndUnMarshalResponse(t *testing.T, resp response.Response) libraryPanelResult {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
require.Equal(t, 200, resp.Status())
|
require.Equal(t, 200, resp.Status())
|
||||||
|
|
||||||
var result = libraryPanelResult{}
|
var result = libraryPanelResult{}
|
||||||
@@ -818,6 +816,8 @@ func validateAndUnMarshalResponse(t *testing.T, resp response.Response) libraryP
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel")
|
command := getCreateCommand(sc.folder.Id, "Text - Library Panel")
|
||||||
resp := sc.service.createHandler(sc.reqContext, command)
|
resp := sc.service.createHandler(sc.reqContext, command)
|
||||||
@@ -865,25 +865,26 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||||||
// deliberate difference between signed in user and user in db to make it crystal clear
|
// deliberate difference between signed in user and user in db to make it crystal clear
|
||||||
// what to expect in the tests
|
// what to expect in the tests
|
||||||
// In the real world these are identical
|
// In the real world these are identical
|
||||||
cmd := &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: "user.in.db@test.com",
|
Email: "user.in.db@test.com",
|
||||||
Name: "User In DB",
|
Name: "User In DB",
|
||||||
Login: UserInDbName,
|
Login: UserInDbName,
|
||||||
}
|
}
|
||||||
err := sqlstore.CreateUser(context.Background(), cmd)
|
_, err := sqlStore.CreateUser(context.Background(), cmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sc := scenarioContext{
|
sc := scenarioContext{
|
||||||
user: user,
|
user: user,
|
||||||
ctx: &ctx,
|
ctx: &ctx,
|
||||||
service: &service,
|
service: &service,
|
||||||
|
sqlStore: sqlStore,
|
||||||
reqContext: &models.ReqContext{
|
reqContext: &models.ReqContext{
|
||||||
Context: &ctx,
|
Context: &ctx,
|
||||||
SignedInUser: &user,
|
SignedInUser: &user,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.folder = createFolderWithACL(t, "ScenarioFolder", sc.user, []folderACLItem{})
|
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||||
|
|
||||||
fn(t, sc)
|
fn(t, sc)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/live/features"
|
"github.com/grafana/grafana/pkg/services/live/features"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@@ -43,6 +43,7 @@ type GrafanaLive struct {
|
|||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
RouteRegister routing.RouteRegister `inject:""`
|
RouteRegister routing.RouteRegister `inject:""`
|
||||||
LogsService *cloudwatch.LogsService `inject:""`
|
LogsService *cloudwatch.LogsService `inject:""`
|
||||||
|
PluginManager plugins.Manager `inject:""`
|
||||||
node *centrifuge.Node
|
node *centrifuge.Node
|
||||||
|
|
||||||
// The websocket handler
|
// The websocket handler
|
||||||
@@ -222,8 +223,8 @@ func (g *GrafanaLive) GetChannelHandlerFactory(scope string, name string) (model
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p, ok := manager.Plugins[name]
|
p := g.PluginManager.GetPlugin(name)
|
||||||
if ok {
|
if p != nil {
|
||||||
h := &PluginHandler{
|
h := &PluginHandler{
|
||||||
Plugin: p,
|
Plugin: p,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -21,6 +22,7 @@ var (
|
|||||||
type TeamSyncFunc func(user *models.User, externalUser *models.ExternalUserInfo) error
|
type TeamSyncFunc func(user *models.User, externalUser *models.ExternalUserInfo) error
|
||||||
|
|
||||||
type LoginService struct {
|
type LoginService struct {
|
||||||
|
SQLStore *sqlstore.SQLStore `inject:""`
|
||||||
Bus bus.Bus `inject:""`
|
Bus bus.Bus `inject:""`
|
||||||
QuotaService *quota.QuotaService `inject:""`
|
QuotaService *quota.QuotaService `inject:""`
|
||||||
TeamSync TeamSyncFunc
|
TeamSync TeamSyncFunc
|
||||||
@@ -107,7 +109,7 @@ func (ls *LoginService) UpsertUser(cmd *models.UpsertUserCommand) error {
|
|||||||
|
|
||||||
// Sync isGrafanaAdmin permission
|
// Sync isGrafanaAdmin permission
|
||||||
if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin {
|
if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin {
|
||||||
if err := ls.Bus.Dispatch(&models.UpdateUserPermissionsCommand{UserId: cmd.Result.Id, IsGrafanaAdmin: *extUser.IsGrafanaAdmin}); err != nil {
|
if err := ls.SQLStore.UpdateUserPermissions(cmd.Result.Id, *extUser.IsGrafanaAdmin); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ type DashboardProvisioner interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DashboardProvisionerFactory creates DashboardProvisioners based on input
|
// DashboardProvisionerFactory creates DashboardProvisioners based on input
|
||||||
type DashboardProvisionerFactory func(string) (DashboardProvisioner, error)
|
type DashboardProvisionerFactory func(string, dashboards.Store, plugins.DataRequestHandler) (DashboardProvisioner, error)
|
||||||
|
|
||||||
// Provisioner is responsible for syncing dashboard from disk to Grafana's database.
|
// Provisioner is responsible for syncing dashboard from disk to Grafana's database.
|
||||||
type Provisioner struct {
|
type Provisioner struct {
|
||||||
@@ -32,16 +34,15 @@ type Provisioner struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new DashboardProvisioner
|
// New returns a new DashboardProvisioner
|
||||||
func New(configDirectory string) (*Provisioner, error) {
|
func New(configDirectory string, store dashboards.Store, reqHandler plugins.DataRequestHandler) (DashboardProvisioner, error) {
|
||||||
logger := log.New("provisioning.dashboard")
|
logger := log.New("provisioning.dashboard")
|
||||||
cfgReader := &configReader{path: configDirectory, log: logger}
|
cfgReader := &configReader{path: configDirectory, log: logger}
|
||||||
configs, err := cfgReader.readConfig()
|
configs, err := cfgReader.readConfig()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errutil.Wrap("Failed to read dashboards config", err)
|
return nil, errutil.Wrap("Failed to read dashboards config", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileReaders, err := getFileReaders(configs, logger)
|
fileReaders, err := getFileReaders(configs, logger, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errutil.Wrap("Failed to initialize file readers", err)
|
return nil, errutil.Wrap("Failed to initialize file readers", err)
|
||||||
}
|
}
|
||||||
@@ -115,13 +116,14 @@ func (provider *Provisioner) GetAllowUIUpdatesFromConfig(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileReaders(configs []*config, logger log.Logger) ([]*FileReader, error) {
|
func getFileReaders(configs []*config, logger log.Logger, store dashboards.Store) ([]*FileReader, error) {
|
||||||
var readers []*FileReader
|
var readers []*FileReader
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
switch config.Type {
|
switch config.Type {
|
||||||
case "file":
|
case "file":
|
||||||
fileReader, err := NewDashboardFileReader(config, logger.New("type", config.Type, "name", config.Name))
|
fileReader, err := NewDashboardFileReader(config, logger.New("type", config.Type, "name", config.Name),
|
||||||
|
store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errutil.Wrapf(err, "Failed to create file reader for config %v", config.Name)
|
return nil, errutil.Wrapf(err, "Failed to create file reader for config %v", config.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@@ -35,7 +36,7 @@ type FileReader struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewDashboardFileReader returns a new filereader based on `config`
|
// NewDashboardFileReader returns a new filereader based on `config`
|
||||||
func NewDashboardFileReader(cfg *config, log log.Logger) (*FileReader, error) {
|
func NewDashboardFileReader(cfg *config, log log.Logger, store dboards.Store) (*FileReader, error) {
|
||||||
var path string
|
var path string
|
||||||
path, ok := cfg.Options["path"].(string)
|
path, ok := cfg.Options["path"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -56,7 +57,7 @@ func NewDashboardFileReader(cfg *config, log log.Logger) (*FileReader, error) {
|
|||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Path: path,
|
Path: path,
|
||||||
log: log,
|
log: log,
|
||||||
dashboardProvisioningService: dashboards.NewProvisioningService(),
|
dashboardProvisioningService: dashboards.NewProvisioningService(store),
|
||||||
FoldersFromFilesStructure: foldersFromFilesStructure,
|
FoldersFromFilesStructure: foldersFromFilesStructure,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestProvisionedSymlinkedFolder(t *testing.T) {
|
|||||||
Options: map[string]interface{}{"path": symlinkedFolder},
|
Options: map[string]interface{}{"path": symlinkedFolder},
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
want, err := filepath.Abs(containingID)
|
want, err := filepath.Abs(containingID)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@@ -41,14 +42,14 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
|||||||
|
|
||||||
Convey("using path parameter", func() {
|
Convey("using path parameter", func() {
|
||||||
cfg.Options["path"] = defaultDashboards
|
cfg.Options["path"] = defaultDashboards
|
||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(reader.Path, ShouldNotEqual, "")
|
So(reader.Path, ShouldNotEqual, "")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("using folder as options", func() {
|
Convey("using folder as options", func() {
|
||||||
cfg.Options["folder"] = defaultDashboards
|
cfg.Options["folder"] = defaultDashboards
|
||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(reader.Path, ShouldNotEqual, "")
|
So(reader.Path, ShouldNotEqual, "")
|
||||||
})
|
})
|
||||||
@@ -56,7 +57,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
|||||||
Convey("using foldersFromFilesStructure as options", func() {
|
Convey("using foldersFromFilesStructure as options", func() {
|
||||||
cfg.Options["path"] = foldersFromFilesStructure
|
cfg.Options["path"] = foldersFromFilesStructure
|
||||||
cfg.Options["foldersFromFilesStructure"] = true
|
cfg.Options["foldersFromFilesStructure"] = true
|
||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(reader.Path, ShouldNotEqual, "")
|
So(reader.Path, ShouldNotEqual, "")
|
||||||
})
|
})
|
||||||
@@ -68,7 +69,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg.Options["folder"] = fullPath
|
cfg.Options["folder"] = fullPath
|
||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
So(reader.Path, ShouldEqual, fullPath)
|
So(reader.Path, ShouldEqual, fullPath)
|
||||||
@@ -77,7 +78,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
|||||||
|
|
||||||
Convey("using relative path", func() {
|
Convey("using relative path", func() {
|
||||||
cfg.Options["folder"] = defaultDashboards
|
cfg.Options["folder"] = defaultDashboards
|
||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
resolvedPath := reader.resolvedPath()
|
resolvedPath := reader.resolvedPath()
|
||||||
@@ -111,7 +112,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
cfg.Options["path"] = defaultDashboards
|
cfg.Options["path"] = defaultDashboards
|
||||||
cfg.Folder = "Team A"
|
cfg.Folder = "Team A"
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader.walkDisk()
|
err = reader.walkDisk()
|
||||||
@@ -142,7 +143,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
Slug: "grafana",
|
Slug: "grafana",
|
||||||
})
|
})
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader.walkDisk()
|
err = reader.walkDisk()
|
||||||
@@ -154,7 +155,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
Convey("Overrides id from dashboard.json files", func() {
|
Convey("Overrides id from dashboard.json files", func() {
|
||||||
cfg.Options["path"] = containingID
|
cfg.Options["path"] = containingID
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader.walkDisk()
|
err = reader.walkDisk()
|
||||||
@@ -167,7 +168,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
cfg.Options["path"] = foldersFromFilesStructure
|
cfg.Options["path"] = foldersFromFilesStructure
|
||||||
cfg.Options["foldersFromFilesStructure"] = true
|
cfg.Options["foldersFromFilesStructure"] = true
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader.walkDisk()
|
err = reader.walkDisk()
|
||||||
@@ -211,14 +212,14 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
Folder: "",
|
Folder: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := NewDashboardFileReader(cfg, logger)
|
_, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Broken dashboards should not cause error", func() {
|
Convey("Broken dashboards should not cause error", func() {
|
||||||
cfg.Options["path"] = brokenDashboards
|
cfg.Options["path"] = brokenDashboards
|
||||||
|
|
||||||
_, err := NewDashboardFileReader(cfg, logger)
|
_, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -226,13 +227,13 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
cfg1 := &config{Name: "1", Type: "file", OrgID: 1, Folder: "f1", Options: map[string]interface{}{"path": containingID}}
|
cfg1 := &config{Name: "1", Type: "file", OrgID: 1, Folder: "f1", Options: map[string]interface{}{"path": containingID}}
|
||||||
cfg2 := &config{Name: "2", Type: "file", OrgID: 1, Folder: "f2", Options: map[string]interface{}{"path": containingID}}
|
cfg2 := &config{Name: "2", Type: "file", OrgID: 1, Folder: "f2", Options: map[string]interface{}{"path": containingID}}
|
||||||
|
|
||||||
reader1, err := NewDashboardFileReader(cfg1, logger)
|
reader1, err := NewDashboardFileReader(cfg1, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader1.walkDisk()
|
err = reader1.walkDisk()
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
reader2, err := NewDashboardFileReader(cfg2, logger)
|
reader2, err := NewDashboardFileReader(cfg2, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader2.walkDisk()
|
err = reader2.walkDisk()
|
||||||
@@ -336,7 +337,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
Convey("Missing dashboard should be unprovisioned if DisableDeletion = true", func() {
|
Convey("Missing dashboard should be unprovisioned if DisableDeletion = true", func() {
|
||||||
cfg.DisableDeletion = true
|
cfg.DisableDeletion = true
|
||||||
|
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader.walkDisk()
|
err = reader.walkDisk()
|
||||||
@@ -347,7 +348,7 @@ func TestDashboardFileReader(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Missing dashboard should be deleted if DisableDeletion = false", func() {
|
Convey("Missing dashboard should be deleted if DisableDeletion = false", func() {
|
||||||
reader, err := NewDashboardFileReader(cfg, logger)
|
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = reader.walkDisk()
|
err = reader.walkDisk()
|
||||||
@@ -395,7 +396,7 @@ func mockDashboardProvisioningService() *fakeDashboardProvisioningService {
|
|||||||
mock := fakeDashboardProvisioningService{
|
mock := fakeDashboardProvisioningService{
|
||||||
provisioned: map[string][]*models.DashboardProvisioning{},
|
provisioned: map[string][]*models.DashboardProvisioning{},
|
||||||
}
|
}
|
||||||
dashboards.NewProvisioningService = func() dashboards.DashboardProvisioningService {
|
dashboards.NewProvisioningService = func(dboards.Store) dashboards.DashboardProvisioningService {
|
||||||
return &mock
|
return &mock
|
||||||
}
|
}
|
||||||
return &mock
|
return &mock
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func (cr *configReaderImpl) readConfig(path string) ([]*pluginsAsConfig, error)
|
|||||||
|
|
||||||
checkOrgIDAndOrgName(apps)
|
checkOrgIDAndOrgName(apps)
|
||||||
|
|
||||||
err = validatePluginsConfig(apps)
|
err = cr.validatePluginsConfig(apps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ func validateRequiredField(apps []*pluginsAsConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePluginsConfig(apps []*pluginsAsConfig) error {
|
func (cr *configReaderImpl) validatePluginsConfig(apps []*pluginsAsConfig) error {
|
||||||
for i := range apps {
|
for i := range apps {
|
||||||
if apps[i].Apps == nil {
|
if apps[i].Apps == nil {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
plugifaces "github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
|
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning/notifiers"
|
"github.com/grafana/grafana/pkg/services/provisioning/notifiers"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning/plugins"
|
"github.com/grafana/grafana/pkg/services/provisioning/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
)
|
)
|
||||||
@@ -27,10 +29,8 @@ type ProvisioningService interface {
|
|||||||
func init() {
|
func init() {
|
||||||
registry.Register(®istry.Descriptor{
|
registry.Register(®istry.Descriptor{
|
||||||
Name: "ProvisioningService",
|
Name: "ProvisioningService",
|
||||||
Instance: NewProvisioningServiceImpl(
|
Instance: newProvisioningServiceImpl(
|
||||||
func(path string) (dashboards.DashboardProvisioner, error) {
|
dashboards.New,
|
||||||
return dashboards.New(path)
|
|
||||||
},
|
|
||||||
notifiers.Provision,
|
notifiers.Provision,
|
||||||
datasources.Provision,
|
datasources.Provision,
|
||||||
plugins.Provision,
|
plugins.Provision,
|
||||||
@@ -39,7 +39,7 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProvisioningServiceImpl(
|
func newProvisioningServiceImpl(
|
||||||
newDashboardProvisioner dashboards.DashboardProvisionerFactory,
|
newDashboardProvisioner dashboards.DashboardProvisionerFactory,
|
||||||
provisionNotifiers func(string) error,
|
provisionNotifiers func(string) error,
|
||||||
provisionDatasources func(string) error,
|
provisionDatasources func(string) error,
|
||||||
@@ -55,7 +55,9 @@ func NewProvisioningServiceImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
type provisioningServiceImpl struct {
|
type provisioningServiceImpl struct {
|
||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
|
RequestHandler plugifaces.DataRequestHandler `inject:""`
|
||||||
|
SQLStore *sqlstore.SQLStore `inject:""`
|
||||||
log log.Logger
|
log log.Logger
|
||||||
pollingCtxCancel context.CancelFunc
|
pollingCtxCancel context.CancelFunc
|
||||||
newDashboardProvisioner dashboards.DashboardProvisionerFactory
|
newDashboardProvisioner dashboards.DashboardProvisionerFactory
|
||||||
@@ -134,7 +136,7 @@ func (ps *provisioningServiceImpl) ProvisionNotifications() error {
|
|||||||
|
|
||||||
func (ps *provisioningServiceImpl) ProvisionDashboards() error {
|
func (ps *provisioningServiceImpl) ProvisionDashboards() error {
|
||||||
dashboardPath := filepath.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
dashboardPath := filepath.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
||||||
dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath)
|
dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath, ps.SQLStore, ps.RequestHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errutil.Wrap("Failed to create provisioner", err)
|
return errutil.Wrap("Failed to create provisioner", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||||
|
plugifaces "github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
|
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -90,8 +92,8 @@ func setup() *serviceTestStruct {
|
|||||||
pollChangesChannel <- ctx
|
pollChangesChannel <- ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceTest.service = NewProvisioningServiceImpl(
|
serviceTest.service = newProvisioningServiceImpl(
|
||||||
func(path string) (dashboards.DashboardProvisioner, error) {
|
func(string, dboards.Store, plugifaces.DataRequestHandler) (dashboards.DashboardProvisioner, error) {
|
||||||
return serviceTest.mock, nil
|
return serviceTest.mock, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
@@ -50,6 +49,7 @@ type RenderingService struct {
|
|||||||
|
|
||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
RemoteCacheService *remotecache.RemoteCache `inject:""`
|
RemoteCacheService *remotecache.RemoteCache `inject:""`
|
||||||
|
PluginManager plugins.Manager `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *RenderingService) Init() error {
|
func (rs *RenderingService) Init() error {
|
||||||
@@ -87,7 +87,7 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
|||||||
|
|
||||||
if rs.pluginAvailable() {
|
if rs.pluginAvailable() {
|
||||||
rs.log = rs.log.New("renderer", "plugin")
|
rs.log = rs.log.New("renderer", "plugin")
|
||||||
rs.pluginInfo = manager.Renderer
|
rs.pluginInfo = rs.PluginManager.Renderer()
|
||||||
|
|
||||||
if err := rs.startPlugin(ctx); err != nil {
|
if err := rs.startPlugin(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -107,7 +107,7 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rs *RenderingService) pluginAvailable() bool {
|
func (rs *RenderingService) pluginAvailable() bool {
|
||||||
return manager.Renderer != nil
|
return rs.PluginManager.Renderer() != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *RenderingService) remoteAvailable() bool {
|
func (rs *RenderingService) remoteAvailable() bool {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -166,18 +167,18 @@ func deleteAlertDefinition(dashboardId int64, sess *DBSession) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveAlerts(cmd *models.SaveAlertsCommand) error {
|
func (ss *SQLStore) SaveAlerts(dashID int64, alerts []*models.Alert) error {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
|
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := updateAlerts(existingAlerts, cmd, sess); err != nil {
|
if err := updateAlerts(existingAlerts, alerts, sess); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deleteMissingAlerts(existingAlerts, cmd, sess); err != nil {
|
if err := deleteMissingAlerts(existingAlerts, alerts, sess); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,8 +186,27 @@ func SaveAlerts(cmd *models.SaveAlertsCommand) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateAlerts(existingAlerts []*models.Alert, cmd *models.SaveAlertsCommand, sess *DBSession) error {
|
func SaveAlerts(cmd *models.SaveAlertsCommand) error {
|
||||||
for _, alert := range cmd.Alerts {
|
return inTransaction(func(sess *DBSession) error {
|
||||||
|
existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateAlerts(existingAlerts, cmd.Alerts, sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := deleteMissingAlerts(existingAlerts, cmd.Alerts, sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *DBSession) error {
|
||||||
|
for _, alert := range alerts {
|
||||||
update := false
|
update := false
|
||||||
var alertToUpdate *models.Alert
|
var alertToUpdate *models.Alert
|
||||||
|
|
||||||
@@ -245,11 +265,11 @@ func updateAlerts(existingAlerts []*models.Alert, cmd *models.SaveAlertsCommand,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteMissingAlerts(alerts []*models.Alert, cmd *models.SaveAlertsCommand, sess *DBSession) error {
|
func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *DBSession) error {
|
||||||
for _, missingAlert := range alerts {
|
for _, missingAlert := range alerts {
|
||||||
missing := true
|
missing := true
|
||||||
|
|
||||||
for _, k := range cmd.Alerts {
|
for _, k := range existingAlerts {
|
||||||
if missingAlert.PanelId == k.PanelId {
|
if missingAlert.PanelId == k.PanelId {
|
||||||
missing = false
|
missing = false
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -30,10 +30,11 @@ func TestAlertingDataAccess(t *testing.T) {
|
|||||||
defer resetTimeNow()
|
defer resetTimeNow()
|
||||||
|
|
||||||
Convey("Testing Alerting data access", t, func() {
|
Convey("Testing Alerting data access", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
testDash := insertTestDashboard(t, "dashboard with alerts", 1, 0, false, "alert")
|
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
|
||||||
evalData, _ := simplejson.NewJson([]byte(`{"test": "test"}`))
|
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
items := []*models.Alert{
|
items := []*models.Alert{
|
||||||
{
|
{
|
||||||
PanelId: 1,
|
PanelId: 1,
|
||||||
@@ -54,7 +55,7 @@ func TestAlertingDataAccess(t *testing.T) {
|
|||||||
UserId: 1,
|
UserId: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveAlerts(&cmd)
|
err = SaveAlerts(&cmd)
|
||||||
|
|
||||||
Convey("Can create one alert", func() {
|
Convey("Can create one alert", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -271,10 +272,11 @@ func TestPausingAlerts(t *testing.T) {
|
|||||||
defer resetTimeNow()
|
defer resetTimeNow()
|
||||||
|
|
||||||
Convey("Given an alert", t, func() {
|
Convey("Given an alert", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
testDash := insertTestDashboard(t, "dashboard with alerts", 1, 0, false, "alert")
|
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
|
||||||
alert, _ := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New())
|
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New())
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
stateDateBeforePause := alert.NewStateDate
|
stateDateBeforePause := alert.NewStateDate
|
||||||
stateDateAfterPause := stateDateBeforePause
|
stateDateAfterPause := stateDateBeforePause
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package sqlstore
|
package sqlstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -24,7 +26,6 @@ var shadowSearchCounter = prometheus.NewCounterVec(
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("sql", SaveDashboard)
|
|
||||||
bus.AddHandler("sql", GetDashboard)
|
bus.AddHandler("sql", GetDashboard)
|
||||||
bus.AddHandler("sql", GetDashboards)
|
bus.AddHandler("sql", GetDashboards)
|
||||||
bus.AddHandler("sql", DeleteDashboard)
|
bus.AddHandler("sql", DeleteDashboard)
|
||||||
@@ -35,7 +36,6 @@ func init() {
|
|||||||
bus.AddHandler("sql", GetDashboardsByPluginId)
|
bus.AddHandler("sql", GetDashboardsByPluginId)
|
||||||
bus.AddHandler("sql", GetDashboardPermissionsForUser)
|
bus.AddHandler("sql", GetDashboardPermissionsForUser)
|
||||||
bus.AddHandler("sql", GetDashboardsBySlug)
|
bus.AddHandler("sql", GetDashboardsBySlug)
|
||||||
bus.AddHandler("sql", ValidateDashboardBeforeSave)
|
|
||||||
bus.AddHandler("sql", HasEditPermissionInFolders)
|
bus.AddHandler("sql", HasEditPermissionInFolders)
|
||||||
bus.AddHandler("sql", HasAdminPermissionInFolders)
|
bus.AddHandler("sql", HasAdminPermissionInFolders)
|
||||||
|
|
||||||
@@ -44,10 +44,11 @@ func init() {
|
|||||||
|
|
||||||
var generateNewUid func() string = util.GenerateShortUID
|
var generateNewUid func() string = util.GenerateShortUID
|
||||||
|
|
||||||
func SaveDashboard(cmd *models.SaveDashboardCommand) error {
|
func (ss *SQLStore) SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
return saveDashboard(sess, cmd)
|
return saveDashboard(sess, &cmd)
|
||||||
})
|
})
|
||||||
|
return cmd.Result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
|
func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
|
||||||
@@ -162,7 +163,7 @@ func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
|
|||||||
|
|
||||||
cmd.Result = dash
|
cmd.Result = dash
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
|
func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
|
||||||
@@ -182,6 +183,26 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
|
|||||||
return "", models.ErrDashboardFailedGenerateUniqueUid
|
return "", models.ErrDashboardFailedGenerateUniqueUid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDashboard gets a dashboard.
|
||||||
|
func (ss *SQLStore) GetDashboard(id, orgID int64, uid, slug string) (*models.Dashboard, error) {
|
||||||
|
if id == 0 && slug == "" && uid == "" {
|
||||||
|
return nil, models.ErrDashboardIdentifierNotSet
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboard := models.Dashboard{Slug: slug, OrgId: orgID, Id: id, Uid: uid}
|
||||||
|
has, err := ss.engine.Get(&dashboard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, models.ErrDashboardNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboard.SetId(dashboard.Id)
|
||||||
|
dashboard.SetUid(dashboard.Uid)
|
||||||
|
return &dashboard, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove me
|
||||||
func GetDashboard(query *models.GetDashboardQuery) error {
|
func GetDashboard(query *models.GetDashboardQuery) error {
|
||||||
if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {
|
if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {
|
||||||
return models.ErrDashboardIdentifierNotSet
|
return models.ErrDashboardIdentifierNotSet
|
||||||
@@ -573,20 +594,20 @@ func GetDashboardUIDById(query *models.GetDashboardRefByIdQuery) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
|
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, dash *models.Dashboard, overwrite bool) (bool, error) {
|
||||||
dash := cmd.Dashboard
|
|
||||||
|
|
||||||
dashWithIdExists := false
|
dashWithIdExists := false
|
||||||
|
isParentFolderChanged := false
|
||||||
var existingById models.Dashboard
|
var existingById models.Dashboard
|
||||||
|
|
||||||
if dash.Id > 0 {
|
if dash.Id > 0 {
|
||||||
|
var err error
|
||||||
dashWithIdExists, err = sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existingById)
|
dashWithIdExists, err = sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existingById)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by ID failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dashWithIdExists {
|
if !dashWithIdExists {
|
||||||
return models.ErrDashboardNotFound
|
return isParentFolderChanged, models.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if dash.Uid == "" {
|
if dash.Uid == "" {
|
||||||
@@ -598,30 +619,32 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.Validat
|
|||||||
var existingByUid models.Dashboard
|
var existingByUid models.Dashboard
|
||||||
|
|
||||||
if dash.Uid != "" {
|
if dash.Uid != "" {
|
||||||
|
var err error
|
||||||
dashWithUidExists, err = sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&existingByUid)
|
dashWithUidExists, err = sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&existingByUid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by UID failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dash.FolderId > 0 {
|
if dash.FolderId > 0 {
|
||||||
var existingFolder models.Dashboard
|
var existingFolder models.Dashboard
|
||||||
folderExists, folderErr := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId, dialect.BooleanStr(true)).Get(&existingFolder)
|
folderExists, err := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId,
|
||||||
if folderErr != nil {
|
dialect.BooleanStr(true)).Get(&existingFolder)
|
||||||
return folderErr
|
if err != nil {
|
||||||
|
return isParentFolderChanged, fmt.Errorf("SQL query for folder failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !folderExists {
|
if !folderExists {
|
||||||
return models.ErrDashboardFolderNotFound
|
return isParentFolderChanged, models.ErrDashboardFolderNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dashWithIdExists && !dashWithUidExists {
|
if !dashWithIdExists && !dashWithUidExists {
|
||||||
return nil
|
return isParentFolderChanged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
|
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
|
||||||
return models.ErrDashboardWithSameUIDExists
|
return isParentFolderChanged, models.ErrDashboardWithSameUIDExists
|
||||||
}
|
}
|
||||||
|
|
||||||
existing := existingById
|
existing := existingById
|
||||||
@@ -632,84 +655,92 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.Validat
|
|||||||
existing = existingByUid
|
existing = existingByUid
|
||||||
|
|
||||||
if !dash.IsFolder {
|
if !dash.IsFolder {
|
||||||
cmd.Result.IsParentFolderChanged = true
|
isParentFolderChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existing.IsFolder && !dash.IsFolder) ||
|
if (existing.IsFolder && !dash.IsFolder) ||
|
||||||
(!existing.IsFolder && dash.IsFolder) {
|
(!existing.IsFolder && dash.IsFolder) {
|
||||||
return models.ErrDashboardTypeMismatch
|
return isParentFolderChanged, models.ErrDashboardTypeMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dash.IsFolder && dash.FolderId != existing.FolderId {
|
if !dash.IsFolder && dash.FolderId != existing.FolderId {
|
||||||
cmd.Result.IsParentFolderChanged = true
|
isParentFolderChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for is someone else has written in between
|
// check for is someone else has written in between
|
||||||
if dash.Version != existing.Version {
|
if dash.Version != existing.Version {
|
||||||
if cmd.Overwrite {
|
if overwrite {
|
||||||
dash.SetVersion(existing.Version)
|
dash.SetVersion(existing.Version)
|
||||||
} else {
|
} else {
|
||||||
return models.ErrDashboardVersionMismatch
|
return isParentFolderChanged, models.ErrDashboardVersionMismatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not allow plugin dashboard updates without overwrite flag
|
// do not allow plugin dashboard updates without overwrite flag
|
||||||
if existing.PluginId != "" && !cmd.Overwrite {
|
if existing.PluginId != "" && !overwrite {
|
||||||
return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
return isParentFolderChanged, models.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return isParentFolderChanged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
func getExistingDashboardByTitleAndFolder(sess *DBSession, dash *models.Dashboard, overwrite,
|
||||||
dash := cmd.Dashboard
|
isParentFolderChanged bool) (bool, error) {
|
||||||
var existing models.Dashboard
|
var existing models.Dashboard
|
||||||
|
exists, err := sess.Where("org_id=? AND slug=? AND (is_folder=? OR folder_id=?)", dash.OrgId, dash.Slug,
|
||||||
exists, err := sess.Where("org_id=? AND slug=? AND (is_folder=? OR folder_id=?)", dash.OrgId, dash.Slug, dialect.BooleanStr(true), dash.FolderId).Get(&existing)
|
dialect.BooleanStr(true), dash.FolderId).Get(&existing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by org ID or folder ID failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists && dash.Id != existing.Id {
|
if exists && dash.Id != existing.Id {
|
||||||
if existing.IsFolder && !dash.IsFolder {
|
if existing.IsFolder && !dash.IsFolder {
|
||||||
return models.ErrDashboardWithSameNameAsFolder
|
return isParentFolderChanged, models.ErrDashboardWithSameNameAsFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
if !existing.IsFolder && dash.IsFolder {
|
if !existing.IsFolder && dash.IsFolder {
|
||||||
return models.ErrDashboardFolderWithSameNameAsDashboard
|
return isParentFolderChanged, models.ErrDashboardFolderWithSameNameAsDashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
|
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
|
||||||
cmd.Result.IsParentFolderChanged = true
|
isParentFolderChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Overwrite {
|
if overwrite {
|
||||||
dash.SetId(existing.Id)
|
dash.SetId(existing.Id)
|
||||||
dash.SetUid(existing.Uid)
|
dash.SetUid(existing.Uid)
|
||||||
dash.SetVersion(existing.Version)
|
dash.SetVersion(existing.Version)
|
||||||
} else {
|
} else {
|
||||||
return models.ErrDashboardWithSameNameInFolderExists
|
return isParentFolderChanged, models.ErrDashboardWithSameNameInFolderExists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return isParentFolderChanged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateDashboardBeforeSave(cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
|
func (ss *SQLStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (bool, error) {
|
||||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
isParentFolderChanged := false
|
||||||
|
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
var err error
|
||||||
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
|
isParentFolderChanged, err = getExistingDashboardByIdOrUidForUpdate(sess, dashboard, overwrite)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = getExistingDashboardByTitleAndFolder(sess, cmd); err != nil {
|
isParentFolderChanged, err = getExistingDashboardByTitleAndFolder(sess, dashboard, overwrite,
|
||||||
|
isParentFolderChanged)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return isParentFolderChanged, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) error {
|
func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) error {
|
||||||
@@ -719,7 +750,8 @@ func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder := &SQLBuilder{}
|
builder := &SQLBuilder{}
|
||||||
builder.Write("SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?", query.SignedInUser.OrgId, dialect.BooleanStr(true))
|
builder.Write("SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?",
|
||||||
|
query.SignedInUser.OrgId, dialect.BooleanStr(true))
|
||||||
builder.WriteDashboardPermissionFilter(query.SignedInUser, models.PERMISSION_EDIT)
|
builder.WriteDashboardPermissionFilter(query.SignedInUser, models.PERMISSION_EDIT)
|
||||||
|
|
||||||
type folderCount struct {
|
type folderCount struct {
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
package sqlstore
|
package sqlstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("sql", UpdateDashboardAcl)
|
|
||||||
bus.AddHandler("sql", GetDashboardAclInfoList)
|
bus.AddHandler("sql", GetDashboardAclInfoList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateDashboardAcl(cmd *models.UpdateDashboardAclCommand) error {
|
func (ss *SQLStore) UpdateDashboardACL(dashboardID int64, items []*models.DashboardAcl) error {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
// delete existing items
|
// delete existing items
|
||||||
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", cmd.DashboardID)
|
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", dashboardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("deleting from dashboard_acl failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range cmd.Items {
|
for _, item := range items {
|
||||||
if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) {
|
if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) {
|
||||||
return models.ErrDashboardAclInfoMissing
|
return models.ErrDashboardAclInfoMissing
|
||||||
}
|
}
|
||||||
@@ -35,7 +37,7 @@ func UpdateDashboardAcl(cmd *models.UpdateDashboardAclCommand) error {
|
|||||||
|
|
||||||
// Update dashboard HasAcl flag
|
// Update dashboard HasAcl flag
|
||||||
dashboard := models.Dashboard{HasAcl: true}
|
dashboard := models.Dashboard{HasAcl: true}
|
||||||
_, err = sess.Cols("has_acl").Where("id=?", cmd.DashboardID).Update(&dashboard)
|
_, err = sess.Cols("has_acl").Where("id=?", dashboardID).Update(&dashboard)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,15 @@ import (
|
|||||||
|
|
||||||
func TestDashboardAclDataAccess(t *testing.T) {
|
func TestDashboardAclDataAccess(t *testing.T) {
|
||||||
Convey("Testing DB", t, func() {
|
Convey("Testing DB", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Given a dashboard folder and a user", func() {
|
Convey("Given a dashboard folder and a user", func() {
|
||||||
currentUser := createUser(t, "viewer", "Viewer", false)
|
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||||
savedFolder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
savedFolder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||||
childDash := insertTestDashboard(t, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
|
childDash := insertTestDashboard(t, sqlStore, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
|
||||||
|
|
||||||
Convey("When adding dashboard permission with userId and teamId set to 0", func() {
|
Convey("When adding dashboard permission with userId and teamId set to 0", func() {
|
||||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
DashboardID: savedFolder.Id,
|
DashboardID: savedFolder.Id,
|
||||||
Permission: models.PERMISSION_EDIT,
|
Permission: models.PERMISSION_EDIT,
|
||||||
@@ -61,10 +62,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given dashboard folder with removed default permissions", func() {
|
Convey("Given dashboard folder with removed default permissions", func() {
|
||||||
err := UpdateDashboardAcl(&models.UpdateDashboardAclCommand{
|
err := sqlStore.UpdateDashboardACL(savedFolder.Id, nil)
|
||||||
DashboardID: savedFolder.Id,
|
|
||||||
Items: []*models.DashboardAcl{},
|
|
||||||
})
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("When reading dashboard acl should return no acl items", func() {
|
Convey("When reading dashboard acl should return no acl items", func() {
|
||||||
@@ -78,7 +76,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given dashboard folder permission", func() {
|
Convey("Given dashboard folder permission", func() {
|
||||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
UserID: currentUser.Id,
|
UserID: currentUser.Id,
|
||||||
DashboardID: savedFolder.Id,
|
DashboardID: savedFolder.Id,
|
||||||
@@ -97,7 +95,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given child dashboard permission", func() {
|
Convey("Given child dashboard permission", func() {
|
||||||
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
UserID: currentUser.Id,
|
UserID: currentUser.Id,
|
||||||
DashboardID: childDash.Id,
|
DashboardID: childDash.Id,
|
||||||
@@ -121,7 +119,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given child dashboard permission in folder with no permissions", func() {
|
Convey("Given child dashboard permission in folder with no permissions", func() {
|
||||||
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
UserID: currentUser.Id,
|
UserID: currentUser.Id,
|
||||||
DashboardID: childDash.Id,
|
DashboardID: childDash.Id,
|
||||||
@@ -149,7 +147,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to add dashboard permission", func() {
|
Convey("Should be able to add dashboard permission", func() {
|
||||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
UserID: currentUser.Id,
|
UserID: currentUser.Id,
|
||||||
DashboardID: savedFolder.Id,
|
DashboardID: savedFolder.Id,
|
||||||
@@ -169,7 +167,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
So(q1.Result[0].UserEmail, ShouldEqual, currentUser.Email)
|
So(q1.Result[0].UserEmail, ShouldEqual, currentUser.Email)
|
||||||
|
|
||||||
Convey("Should be able to delete an existing permission", func() {
|
Convey("Should be able to delete an existing permission", func() {
|
||||||
err := testHelperUpdateDashboardAcl(savedFolder.Id)
|
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
q3 := &models.GetDashboardAclInfoListQuery{DashboardID: savedFolder.Id, OrgID: 1}
|
q3 := &models.GetDashboardAclInfoListQuery{DashboardID: savedFolder.Id, OrgID: 1}
|
||||||
@@ -180,14 +178,13 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given a team", func() {
|
Convey("Given a team", func() {
|
||||||
group1 := models.CreateTeamCommand{Name: "group1 name", OrgId: 1}
|
team1, err := sqlStore.CreateTeam("group1 name", "", 1)
|
||||||
err := CreateTeam(&group1)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Should be able to add a user permission for a team", func() {
|
Convey("Should be able to add a user permission for a team", func() {
|
||||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
TeamID: group1.Result.Id,
|
TeamID: team1.Id,
|
||||||
DashboardID: savedFolder.Id,
|
DashboardID: savedFolder.Id,
|
||||||
Permission: models.PERMISSION_EDIT,
|
Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
@@ -198,13 +195,13 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
||||||
So(q1.Result[0].Permission, ShouldEqual, models.PERMISSION_EDIT)
|
So(q1.Result[0].Permission, ShouldEqual, models.PERMISSION_EDIT)
|
||||||
So(q1.Result[0].TeamId, ShouldEqual, group1.Result.Id)
|
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to update an existing permission for a team", func() {
|
Convey("Should be able to update an existing permission for a team", func() {
|
||||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
TeamID: group1.Result.Id,
|
TeamID: team1.Id,
|
||||||
DashboardID: savedFolder.Id,
|
DashboardID: savedFolder.Id,
|
||||||
Permission: models.PERMISSION_ADMIN,
|
Permission: models.PERMISSION_ADMIN,
|
||||||
})
|
})
|
||||||
@@ -216,7 +213,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
|||||||
So(len(q3.Result), ShouldEqual, 1)
|
So(len(q3.Result), ShouldEqual, 1)
|
||||||
So(q3.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
So(q3.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
||||||
So(q3.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
|
So(q3.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
|
||||||
So(q3.Result[0].TeamId, ShouldEqual, group1.Result.Id)
|
So(q3.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,21 +7,22 @@ import (
|
|||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/search"
|
"github.com/grafana/grafana/pkg/services/search"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDashboardFolderDataAccess(t *testing.T) {
|
func TestDashboardFolderDataAccess(t *testing.T) {
|
||||||
Convey("Testing DB", t, func() {
|
Convey("Testing DB", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Given one dashboard folder with two dashboards and one dashboard in the root folder", func() {
|
Convey("Given one dashboard folder with two dashboards and one dashboard in the root folder", func() {
|
||||||
folder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
folder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||||
dashInRoot := insertTestDashboard(t, "test dash 67", 1, 0, false, "prod", "webapp")
|
dashInRoot := insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod", "webapp")
|
||||||
childDash := insertTestDashboard(t, "test dash 23", 1, folder.Id, false, "prod", "webapp")
|
childDash := insertTestDashboard(t, sqlStore, "test dash 23", 1, folder.Id, false, "prod", "webapp")
|
||||||
insertTestDashboard(t, "test dash 45", 1, folder.Id, false, "prod")
|
insertTestDashboard(t, sqlStore, "test dash 45", 1, folder.Id, false, "prod")
|
||||||
|
|
||||||
currentUser := createUser(t, "viewer", "Viewer", false)
|
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||||
|
|
||||||
Convey("and no acls are set", func() {
|
Convey("and no acls are set", func() {
|
||||||
Convey("should return all dashboards", func() {
|
Convey("should return all dashboards", func() {
|
||||||
@@ -40,7 +41,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("and acl is set for dashboard folder", func() {
|
Convey("and acl is set for dashboard folder", func() {
|
||||||
var otherUser int64 = 999
|
var otherUser int64 = 999
|
||||||
err := testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id, models.DashboardAcl{
|
||||||
DashboardID: folder.Id,
|
DashboardID: folder.Id,
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
UserID: otherUser,
|
UserID: otherUser,
|
||||||
@@ -61,7 +62,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("when the user is given permission", func() {
|
Convey("when the user is given permission", func() {
|
||||||
err := testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id, models.DashboardAcl{
|
||||||
DashboardID: folder.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT,
|
DashboardID: folder.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -102,9 +103,9 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("and acl is set for dashboard child and folder has all permissions removed", func() {
|
Convey("and acl is set for dashboard child and folder has all permissions removed", func() {
|
||||||
var otherUser int64 = 999
|
var otherUser int64 = 999
|
||||||
err := testHelperUpdateDashboardAcl(folder.Id)
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
|
err = testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
|
||||||
DashboardID: folder.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
DashboardID: folder.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -120,7 +121,9 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("when the user is given permission to child", func() {
|
Convey("when the user is given permission to child", func() {
|
||||||
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{DashboardID: childDash.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT})
|
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
|
||||||
|
DashboardID: childDash.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT,
|
||||||
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("should be able to search for child dashboard but not folder", func() {
|
Convey("should be able to search for child dashboard but not folder", func() {
|
||||||
@@ -156,18 +159,24 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given two dashboard folders with one dashboard each and one dashboard in the root folder", func() {
|
Convey("Given two dashboard folders with one dashboard each and one dashboard in the root folder", func() {
|
||||||
folder1 := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod")
|
folder1 := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod")
|
||||||
folder2 := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod")
|
folder2 := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod")
|
||||||
dashInRoot := insertTestDashboard(t, "test dash 67", 1, 0, false, "prod")
|
dashInRoot := insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod")
|
||||||
childDash1 := insertTestDashboard(t, "child dash 1", 1, folder1.Id, false, "prod")
|
childDash1 := insertTestDashboard(t, sqlStore, "child dash 1", 1, folder1.Id, false, "prod")
|
||||||
childDash2 := insertTestDashboard(t, "child dash 2", 1, folder2.Id, false, "prod")
|
childDash2 := insertTestDashboard(t, sqlStore, "child dash 2", 1, folder2.Id, false, "prod")
|
||||||
|
|
||||||
currentUser := createUser(t, "viewer", "Viewer", false)
|
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||||
var rootFolderId int64 = 0
|
var rootFolderId int64 = 0
|
||||||
|
|
||||||
Convey("and one folder is expanded, the other collapsed", func() {
|
Convey("and one folder is expanded, the other collapsed", func() {
|
||||||
Convey("should return dashboards in root and expanded folder", func() {
|
Convey("should return dashboards in root and expanded folder", func() {
|
||||||
query := &search.FindPersistedDashboardsQuery{FolderIds: []int64{rootFolderId, folder1.Id}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1}
|
query := &search.FindPersistedDashboardsQuery{
|
||||||
|
FolderIds: []int64{
|
||||||
|
rootFolderId, folder1.Id}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id,
|
||||||
|
OrgId: 1, OrgRole: models.ROLE_VIEWER,
|
||||||
|
},
|
||||||
|
OrgId: 1,
|
||||||
|
}
|
||||||
err := SearchDashboards(query)
|
err := SearchDashboards(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(query.Result), ShouldEqual, 4)
|
So(len(query.Result), ShouldEqual, 4)
|
||||||
@@ -179,14 +188,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("and acl is set for one dashboard folder", func() {
|
Convey("and acl is set for one dashboard folder", func() {
|
||||||
var otherUser int64 = 999
|
const otherUser int64 = 999
|
||||||
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
|
||||||
DashboardID: folder1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
DashboardID: folder1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("and a dashboard is moved from folder without acl to the folder with an acl", func() {
|
Convey("and a dashboard is moved from folder without acl to the folder with an acl", func() {
|
||||||
moveDashboard(1, childDash2.Data, folder1.Id)
|
moveDashboard(t, sqlStore, 1, childDash2.Data, folder1.Id)
|
||||||
|
|
||||||
Convey("should not return folder with acl or its children", func() {
|
Convey("should not return folder with acl or its children", func() {
|
||||||
query := &search.FindPersistedDashboardsQuery{
|
query := &search.FindPersistedDashboardsQuery{
|
||||||
@@ -201,7 +210,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
Convey("and a dashboard is moved from folder with acl to the folder without an acl", func() {
|
Convey("and a dashboard is moved from folder with acl to the folder without an acl", func() {
|
||||||
moveDashboard(1, childDash1.Data, folder2.Id)
|
moveDashboard(t, sqlStore, 1, childDash1.Data, folder2.Id)
|
||||||
|
|
||||||
Convey("should return folder without acl and its children", func() {
|
Convey("should return folder without acl and its children", func() {
|
||||||
query := &search.FindPersistedDashboardsQuery{
|
query := &search.FindPersistedDashboardsQuery{
|
||||||
@@ -220,12 +229,12 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("and a dashboard with an acl is moved to the folder without an acl", func() {
|
Convey("and a dashboard with an acl is moved to the folder without an acl", func() {
|
||||||
err := testHelperUpdateDashboardAcl(childDash1.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash1.Id, models.DashboardAcl{
|
||||||
DashboardID: childDash1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
DashboardID: childDash1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
moveDashboard(1, childDash1.Data, folder2.Id)
|
moveDashboard(t, sqlStore, 1, childDash1.Data, folder2.Id)
|
||||||
|
|
||||||
Convey("should return folder without acl but not the dashboard with acl", func() {
|
Convey("should return folder without acl but not the dashboard with acl", func() {
|
||||||
query := &search.FindPersistedDashboardsQuery{
|
query := &search.FindPersistedDashboardsQuery{
|
||||||
@@ -246,13 +255,13 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given two dashboard folders", func() {
|
Convey("Given two dashboard folders", func() {
|
||||||
folder1 := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod")
|
folder1 := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod")
|
||||||
folder2 := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod")
|
folder2 := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod")
|
||||||
insertTestDashboard(t, "folder in another org", 2, 0, true, "prod")
|
insertTestDashboard(t, sqlStore, "folder in another org", 2, 0, true, "prod")
|
||||||
|
|
||||||
adminUser := createUser(t, "admin", "Admin", true)
|
adminUser := createUser(t, sqlStore, "admin", "Admin", true)
|
||||||
editorUser := createUser(t, "editor", "Editor", false)
|
editorUser := createUser(t, sqlStore, "editor", "Editor", false)
|
||||||
viewerUser := createUser(t, "viewer", "Viewer", false)
|
viewerUser := createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||||
|
|
||||||
Convey("Admin users", func() {
|
Convey("Admin users", func() {
|
||||||
Convey("Should have write access to all dashboard folders in their org", func() {
|
Convey("Should have write access to all dashboard folders in their org", func() {
|
||||||
@@ -343,7 +352,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should have write access to one dashboard folder if default role changed to view for one folder", func() {
|
Convey("Should have write access to one dashboard folder if default role changed to view for one folder", func() {
|
||||||
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
|
||||||
DashboardID: folder1.Id, OrgID: 1, UserID: editorUser.Id, Permission: models.PERMISSION_VIEW,
|
DashboardID: folder1.Id, OrgID: 1, UserID: editorUser.Id, Permission: models.PERMISSION_VIEW,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -407,7 +416,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to get one dashboard folder if default role changed to edit for one folder", func() {
|
Convey("Should be able to get one dashboard folder if default role changed to edit for one folder", func() {
|
||||||
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
|
||||||
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_EDIT,
|
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -438,7 +447,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("and admin permission is given for user with org role viewer in one dashboard folder", func() {
|
Convey("and admin permission is given for user with org role viewer in one dashboard folder", func() {
|
||||||
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
|
||||||
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_ADMIN,
|
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_ADMIN,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -454,7 +463,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("and edit permission is given for user with org role viewer in one dashboard folder", func() {
|
Convey("and edit permission is given for user with org role viewer in one dashboard folder", func() {
|
||||||
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
|
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
|
||||||
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_EDIT,
|
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -472,3 +481,19 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func moveDashboard(t *testing.T, sqlStore *SQLStore, orgId int64, dashboard *simplejson.Json,
|
||||||
|
newFolderId int64) *models.Dashboard {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
cmd := models.SaveDashboardCommand{
|
||||||
|
OrgId: orgId,
|
||||||
|
FolderId: newFolderId,
|
||||||
|
Dashboard: dashboard,
|
||||||
|
Overwrite: true,
|
||||||
|
}
|
||||||
|
dash, err := sqlStore.SaveDashboard(cmd)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
return dash
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlstore
|
package sqlstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
@@ -8,9 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("sql", GetProvisionedDashboardDataQuery)
|
|
||||||
bus.AddHandler("sql", SaveProvisionedDashboard)
|
|
||||||
bus.AddHandler("sql", GetProvisionedDataByDashboardId)
|
|
||||||
bus.AddHandler("sql", UnprovisionDashboard)
|
bus.AddHandler("sql", UnprovisionDashboard)
|
||||||
bus.AddHandler("sql", DeleteOrphanedProvisionedDashboards)
|
bus.AddHandler("sql", DeleteOrphanedProvisionedDashboards)
|
||||||
}
|
}
|
||||||
@@ -22,64 +20,62 @@ type DashboardExtras struct {
|
|||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
func (ss *SQLStore) GetProvisionedDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||||
result := &models.DashboardProvisioning{}
|
var data models.DashboardProvisioning
|
||||||
|
exists, err := x.Where("dashboard_id = ?", dashboardID).Get(&data)
|
||||||
exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if exist {
|
if exists {
|
||||||
cmd.Result = result
|
return &data, nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveProvisionedDashboard(cmd *models.SaveProvisionedDashboardCommand) error {
|
func (ss *SQLStore) SaveProvisionedDashboard(cmd models.SaveDashboardCommand,
|
||||||
return inTransaction(func(sess *DBSession) error {
|
provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
|
||||||
err := saveDashboard(sess, cmd.DashboardCmd)
|
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
if err != nil {
|
if err := saveDashboard(sess, &cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Result = cmd.DashboardCmd.Result
|
if provisioning.Updated == 0 {
|
||||||
if cmd.DashboardProvisioning.Updated == 0 {
|
provisioning.Updated = cmd.Result.Updated.Unix()
|
||||||
cmd.DashboardProvisioning.Updated = cmd.Result.Updated.Unix()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return saveProvisionedData(sess, cmd.DashboardProvisioning, cmd.Result)
|
return saveProvisionedData(sess, provisioning, cmd.Result)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return cmd.Result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveProvisionedData(sess *DBSession, cmd *models.DashboardProvisioning, dashboard *models.Dashboard) error {
|
func saveProvisionedData(sess *DBSession, provisioning *models.DashboardProvisioning, dashboard *models.Dashboard) error {
|
||||||
result := &models.DashboardProvisioning{}
|
result := &models.DashboardProvisioning{}
|
||||||
|
|
||||||
exist, err := sess.Where("dashboard_id=? AND name = ?", dashboard.Id, cmd.Name).Get(result)
|
exist, err := sess.Where("dashboard_id=? AND name = ?", dashboard.Id, provisioning.Name).Get(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Id = result.Id
|
provisioning.Id = result.Id
|
||||||
cmd.DashboardId = dashboard.Id
|
provisioning.DashboardId = dashboard.Id
|
||||||
|
|
||||||
if exist {
|
if exist {
|
||||||
_, err = sess.ID(result.Id).Update(cmd)
|
_, err = sess.ID(result.Id).Update(provisioning)
|
||||||
} else {
|
} else {
|
||||||
_, err = sess.Insert(cmd)
|
_, err = sess.Insert(provisioning)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetProvisionedDashboardDataQuery(cmd *models.GetProvisionedDashboardDataQuery) error {
|
func (ss *SQLStore) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
||||||
var result []*models.DashboardProvisioning
|
var result []*models.DashboardProvisioning
|
||||||
|
if err := ss.engine.Where("name = ?", name).Find(&result); err != nil {
|
||||||
if err := x.Where("name = ?", cmd.Name).Find(&result); err != nil {
|
return nil, err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Result = result
|
return result, nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnprovisionDashboard removes row in dashboard_provisioning for the dashboard making it seem as if manually created.
|
// UnprovisionDashboard removes row in dashboard_provisioning for the dashboard making it seem as if manually created.
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
|
|
||||||
func TestDashboardProvisioningTest(t *testing.T) {
|
func TestDashboardProvisioningTest(t *testing.T) {
|
||||||
Convey("Testing Dashboard provisioning", t, func() {
|
Convey("Testing Dashboard provisioning", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
folderCmd := &models.SaveDashboardCommand{
|
folderCmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
FolderId: 0,
|
FolderId: 0,
|
||||||
IsFolder: true,
|
IsFolder: true,
|
||||||
@@ -25,13 +25,13 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveDashboard(folderCmd)
|
dash, err := sqlStore.SaveDashboard(folderCmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
saveDashboardCmd := &models.SaveDashboardCommand{
|
saveDashboardCmd := models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
FolderId: folderCmd.Result.Id,
|
FolderId: dash.Id,
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
"id": nil,
|
"id": nil,
|
||||||
"title": "test dashboard",
|
"title": "test dashboard",
|
||||||
@@ -41,43 +41,38 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
Convey("Saving dashboards with provisioning meta data", func() {
|
Convey("Saving dashboards with provisioning meta data", func() {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
cmd := &models.SaveProvisionedDashboardCommand{
|
provisioning := &models.DashboardProvisioning{
|
||||||
DashboardCmd: saveDashboardCmd,
|
Name: "default",
|
||||||
DashboardProvisioning: &models.DashboardProvisioning{
|
ExternalId: "/var/grafana.json",
|
||||||
Name: "default",
|
Updated: now.Unix(),
|
||||||
ExternalId: "/var/grafana.json",
|
|
||||||
Updated: now.Unix(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveProvisionedDashboard(cmd)
|
dash, err := sqlStore.SaveProvisionedDashboard(saveDashboardCmd, provisioning)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(cmd.Result, ShouldNotBeNil)
|
So(dash, ShouldNotBeNil)
|
||||||
So(cmd.Result.Id, ShouldNotEqual, 0)
|
So(dash.Id, ShouldNotEqual, 0)
|
||||||
dashId := cmd.Result.Id
|
dashId := dash.Id
|
||||||
|
|
||||||
Convey("Deleting orphaned provisioned dashboards", func() {
|
Convey("Deleting orphaned provisioned dashboards", func() {
|
||||||
anotherCmd := &models.SaveProvisionedDashboardCommand{
|
saveCmd := models.SaveDashboardCommand{
|
||||||
DashboardCmd: &models.SaveDashboardCommand{
|
OrgId: 1,
|
||||||
OrgId: 1,
|
IsFolder: false,
|
||||||
IsFolder: false,
|
FolderId: dash.Id,
|
||||||
FolderId: folderCmd.Result.Id,
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
"id": nil,
|
||||||
"id": nil,
|
"title": "another_dashboard",
|
||||||
"title": "another_dashboard",
|
}),
|
||||||
}),
|
}
|
||||||
},
|
provisioning := &models.DashboardProvisioning{
|
||||||
DashboardProvisioning: &models.DashboardProvisioning{
|
Name: "another_reader",
|
||||||
Name: "another_reader",
|
ExternalId: "/var/grafana.json",
|
||||||
ExternalId: "/var/grafana.json",
|
Updated: now.Unix(),
|
||||||
Updated: now.Unix(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveProvisionedDashboard(anotherCmd)
|
anotherDash, err := sqlStore.SaveProvisionedDashboard(saveCmd, provisioning)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
query := &models.GetDashboardsQuery{DashboardIds: []int64{anotherCmd.Result.Id}}
|
query := &models.GetDashboardsQuery{DashboardIds: []int64{anotherDash.Id}}
|
||||||
err = GetDashboards(query)
|
err = GetDashboards(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldNotBeNil)
|
So(query.Result, ShouldNotBeNil)
|
||||||
@@ -85,7 +80,7 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
deleteCmd := &models.DeleteOrphanedProvisionedDashboardsCommand{ReaderNames: []string{"default"}}
|
deleteCmd := &models.DeleteOrphanedProvisionedDashboardsCommand{ReaderNames: []string{"default"}}
|
||||||
So(DeleteOrphanedProvisionedDashboards(deleteCmd), ShouldBeNil)
|
So(DeleteOrphanedProvisionedDashboards(deleteCmd), ShouldBeNil)
|
||||||
|
|
||||||
query = &models.GetDashboardsQuery{DashboardIds: []int64{cmd.Result.Id, anotherCmd.Result.Id}}
|
query = &models.GetDashboardsQuery{DashboardIds: []int64{dash.Id, anotherDash.Id}}
|
||||||
err = GetDashboards(query)
|
err = GetDashboards(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
@@ -94,45 +89,38 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can query for provisioned dashboards", func() {
|
Convey("Can query for provisioned dashboards", func() {
|
||||||
query := &models.GetProvisionedDashboardDataQuery{Name: "default"}
|
rslt, err := sqlStore.GetProvisionedDashboardData("default")
|
||||||
err := GetProvisionedDashboardDataQuery(query)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
So(len(query.Result), ShouldEqual, 1)
|
So(len(rslt), ShouldEqual, 1)
|
||||||
So(query.Result[0].DashboardId, ShouldEqual, dashId)
|
So(rslt[0].DashboardId, ShouldEqual, dashId)
|
||||||
So(query.Result[0].Updated, ShouldEqual, now.Unix())
|
So(rslt[0].Updated, ShouldEqual, now.Unix())
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can query for one provisioned dashboard", func() {
|
Convey("Can query for one provisioned dashboard", func() {
|
||||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
|
data, err := sqlStore.GetProvisionedDataByDashboardID(dash.Id)
|
||||||
|
|
||||||
err := GetProvisionedDataByDashboardId(query)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
So(query.Result, ShouldNotBeNil)
|
So(data, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can query for none provisioned dashboard", func() {
|
Convey("Can query for none provisioned dashboard", func() {
|
||||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: 3000}
|
data, err := sqlStore.GetProvisionedDataByDashboardID(3000)
|
||||||
|
|
||||||
err := GetProvisionedDataByDashboardId(query)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeNil)
|
So(data, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Deleting folder should delete provision meta data", func() {
|
Convey("Deleting folder should delete provision meta data", func() {
|
||||||
deleteCmd := &models.DeleteDashboardCommand{
|
deleteCmd := &models.DeleteDashboardCommand{
|
||||||
Id: folderCmd.Result.Id,
|
Id: dash.Id,
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
So(DeleteDashboard(deleteCmd), ShouldBeNil)
|
So(DeleteDashboard(deleteCmd), ShouldBeNil)
|
||||||
|
|
||||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
|
data, err := sqlStore.GetProvisionedDataByDashboardID(dash.Id)
|
||||||
|
|
||||||
err = GetProvisionedDataByDashboardId(query)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeNil)
|
So(data, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("UnprovisionDashboard should delete provisioning metadata", func() {
|
Convey("UnprovisionDashboard should delete provisioning metadata", func() {
|
||||||
@@ -142,11 +130,9 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
|
|
||||||
So(UnprovisionDashboard(unprovisionCmd), ShouldBeNil)
|
So(UnprovisionDashboard(unprovisionCmd), ShouldBeNil)
|
||||||
|
|
||||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashId}
|
data, err := sqlStore.GetProvisionedDataByDashboardID(dashId)
|
||||||
|
|
||||||
err = GetProvisionedDataByDashboardId(query)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeNil)
|
So(data, ShouldBeNil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,13 +22,13 @@ import (
|
|||||||
|
|
||||||
func TestDashboardDataAccess(t *testing.T) {
|
func TestDashboardDataAccess(t *testing.T) {
|
||||||
Convey("Testing DB", t, func() {
|
Convey("Testing DB", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Given saved dashboard", func() {
|
Convey("Given saved dashboard", func() {
|
||||||
savedFolder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
savedFolder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||||
savedDash := insertTestDashboard(t, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
savedDash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||||
insertTestDashboard(t, "test dash 45", 1, savedFolder.Id, false, "prod")
|
insertTestDashboard(t, sqlStore, "test dash 45", 1, savedFolder.Id, false, "prod")
|
||||||
insertTestDashboard(t, "test dash 67", 1, 0, false, "prod", "webapp")
|
insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod", "webapp")
|
||||||
|
|
||||||
Convey("Should return dashboard model", func() {
|
Convey("Should return dashboard model", func() {
|
||||||
So(savedDash.Title, ShouldEqual, "test dash 23")
|
So(savedDash.Title, ShouldEqual, "test dash 23")
|
||||||
@@ -104,7 +104,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to delete dashboard", func() {
|
Convey("Should be able to delete dashboard", func() {
|
||||||
dash := insertTestDashboard(t, "delete me", 1, 0, false, "delete this")
|
dash := insertTestDashboard(t, sqlStore, "delete me", 1, 0, false, "delete this")
|
||||||
|
|
||||||
err := DeleteDashboard(&models.DeleteDashboardCommand{
|
err := DeleteDashboard(&models.DeleteDashboardCommand{
|
||||||
Id: dash.Id,
|
Id: dash.Id,
|
||||||
@@ -129,8 +129,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
"tags": []interface{}{},
|
"tags": []interface{}{},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
_, err := sqlStore.SaveDashboard(cmd)
|
||||||
err := SaveDashboard(&cmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
generateNewUid = util.GenerateShortUID
|
generateNewUid = util.GenerateShortUID
|
||||||
@@ -145,13 +144,12 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
UserId: 100,
|
UserId: 100,
|
||||||
}
|
}
|
||||||
|
dashboard, err := sqlStore.SaveDashboard(cmd)
|
||||||
err := SaveDashboard(&cmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(cmd.Result.CreatedBy, ShouldEqual, 100)
|
So(dashboard.CreatedBy, ShouldEqual, 100)
|
||||||
So(cmd.Result.Created.IsZero(), ShouldBeFalse)
|
So(dashboard.Created.IsZero(), ShouldBeFalse)
|
||||||
So(cmd.Result.UpdatedBy, ShouldEqual, 100)
|
So(dashboard.UpdatedBy, ShouldEqual, 100)
|
||||||
So(cmd.Result.Updated.IsZero(), ShouldBeFalse)
|
So(dashboard.Updated.IsZero(), ShouldBeFalse)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to update dashboard by id and remove folderId", func() {
|
Convey("Should be able to update dashboard by id and remove folderId", func() {
|
||||||
@@ -166,10 +164,9 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
FolderId: 2,
|
FolderId: 2,
|
||||||
UserId: 100,
|
UserId: 100,
|
||||||
}
|
}
|
||||||
|
dash, err := sqlStore.SaveDashboard(cmd)
|
||||||
err := SaveDashboard(&cmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(cmd.Result.FolderId, ShouldEqual, 2)
|
So(dash.FolderId, ShouldEqual, 2)
|
||||||
|
|
||||||
cmd = models.SaveDashboardCommand{
|
cmd = models.SaveDashboardCommand{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
@@ -182,8 +179,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
UserId: 100,
|
UserId: 100,
|
||||||
}
|
}
|
||||||
|
_, err = sqlStore.SaveDashboard(cmd)
|
||||||
err = SaveDashboard(&cmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
query := models.GetDashboardQuery{
|
query := models.GetDashboardQuery{
|
||||||
@@ -201,7 +197,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to delete empty folder", func() {
|
Convey("Should be able to delete empty folder", func() {
|
||||||
emptyFolder := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod", "webapp")
|
emptyFolder := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod", "webapp")
|
||||||
|
|
||||||
deleteCmd := &models.DeleteDashboardCommand{Id: emptyFolder.Id}
|
deleteCmd := &models.DeleteDashboardCommand{Id: emptyFolder.Id}
|
||||||
err := DeleteDashboard(deleteCmd)
|
err := DeleteDashboard(deleteCmd)
|
||||||
@@ -236,7 +232,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveDashboard(&cmd)
|
_, err := sqlStore.SaveDashboard(cmd)
|
||||||
So(err, ShouldEqual, models.ErrDashboardNotFound)
|
So(err, ShouldEqual, models.ErrDashboardNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -250,8 +246,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
"tags": []interface{}{},
|
"tags": []interface{}{},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
_, err := sqlStore.SaveDashboard(cmd)
|
||||||
err := SaveDashboard(&cmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -366,7 +361,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given two dashboards, one is starred dashboard by user 10, other starred by user 1", func() {
|
Convey("Given two dashboards, one is starred dashboard by user 10, other starred by user 1", func() {
|
||||||
starredDash := insertTestDashboard(t, "starred dash", 1, 0, false)
|
starredDash := insertTestDashboard(t, sqlStore, "starred dash", 1, 0, false)
|
||||||
err := StarDashboard(&models.StarDashboardCommand{
|
err := StarDashboard(&models.StarDashboardCommand{
|
||||||
DashboardId: starredDash.Id,
|
DashboardId: starredDash.Id,
|
||||||
UserId: 10,
|
UserId: 10,
|
||||||
@@ -396,9 +391,9 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
Convey("Given a plugin with imported dashboards", func() {
|
Convey("Given a plugin with imported dashboards", func() {
|
||||||
pluginId := "test-app"
|
pluginId := "test-app"
|
||||||
|
|
||||||
appFolder := insertTestDashboardForPlugin("app-test", 1, 0, true, pluginId)
|
appFolder := insertTestDashboardForPlugin(t, sqlStore, "app-test", 1, 0, true, pluginId)
|
||||||
insertTestDashboardForPlugin("app-dash1", 1, appFolder.Id, false, pluginId)
|
insertTestDashboardForPlugin(t, sqlStore, "app-dash1", 1, appFolder.Id, false, pluginId)
|
||||||
insertTestDashboardForPlugin("app-dash2", 1, appFolder.Id, false, pluginId)
|
insertTestDashboardForPlugin(t, sqlStore, "app-dash2", 1, appFolder.Id, false, pluginId)
|
||||||
|
|
||||||
Convey("Should return imported dashboard", func() {
|
Convey("Should return imported dashboard", func() {
|
||||||
query := models.GetDashboardsByPluginIdQuery{
|
query := models.GetDashboardsByPluginIdQuery{
|
||||||
@@ -417,9 +412,9 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
func TestDashboard_SortingOptions(t *testing.T) {
|
func TestDashboard_SortingOptions(t *testing.T) {
|
||||||
// insertTestDashboard uses GoConvey's assertions. Workaround.
|
// insertTestDashboard uses GoConvey's assertions. Workaround.
|
||||||
Convey("test with multiple sorting options", t, func() {
|
Convey("test with multiple sorting options", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
dashB := insertTestDashboard(t, "Beta", 1, 0, false)
|
dashB := insertTestDashboard(t, sqlStore, "Beta", 1, 0, false)
|
||||||
dashA := insertTestDashboard(t, "Alfa", 1, 0, false)
|
dashA := insertTestDashboard(t, sqlStore, "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)
|
||||||
@@ -441,7 +436,8 @@ func TestDashboard_SortingOptions(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertTestDashboard(t *testing.T, title string, orgId int64, folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
|
func insertTestDashboard(t *testing.T, sqlStore *SQLStore, title string, orgId int64,
|
||||||
|
folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
@@ -454,17 +450,20 @@ func insertTestDashboard(t *testing.T, title string, orgId int64, folderId int64
|
|||||||
"tags": tags,
|
"tags": tags,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
dash, err := sqlStore.SaveDashboard(cmd)
|
||||||
err := SaveDashboard(&cmd)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, dash)
|
||||||
|
|
||||||
cmd.Result.Data.Set("id", cmd.Result.Id)
|
dash.Data.Set("id", dash.Id)
|
||||||
cmd.Result.Data.Set("uid", cmd.Result.Uid)
|
dash.Data.Set("uid", dash.Uid)
|
||||||
|
|
||||||
return cmd.Result
|
return dash
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertTestDashboardForPlugin(title string, orgId int64, folderId int64, isFolder bool, pluginId string) *models.Dashboard {
|
func insertTestDashboardForPlugin(t *testing.T, sqlStore *SQLStore, title string, orgId int64,
|
||||||
|
folderId int64, isFolder bool, pluginId string) *models.Dashboard {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
cmd := models.SaveDashboardCommand{
|
cmd := models.SaveDashboardCommand{
|
||||||
OrgId: orgId,
|
OrgId: orgId,
|
||||||
FolderId: folderId,
|
FolderId: folderId,
|
||||||
@@ -476,13 +475,13 @@ func insertTestDashboardForPlugin(title string, orgId int64, folderId int64, isF
|
|||||||
PluginId: pluginId,
|
PluginId: pluginId,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveDashboard(&cmd)
|
dash, err := sqlStore.SaveDashboard(cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
return cmd.Result
|
return dash
|
||||||
}
|
}
|
||||||
|
|
||||||
func createUser(t *testing.T, name string, role string, isAdmin bool) models.User {
|
func createUser(t *testing.T, sqlStore *SQLStore, name string, role string, isAdmin bool) models.User {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
setting.AutoAssignOrg = true
|
setting.AutoAssignOrg = true
|
||||||
@@ -490,27 +489,13 @@ func createUser(t *testing.T, name string, role string, isAdmin bool) models.Use
|
|||||||
setting.AutoAssignOrgRole = role
|
setting.AutoAssignOrgRole = role
|
||||||
|
|
||||||
currentUserCmd := models.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
|
currentUserCmd := models.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
|
||||||
err := CreateUser(context.Background(), ¤tUserCmd)
|
currentUser, err := sqlStore.CreateUser(context.Background(), currentUserCmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
q1 := models.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
|
q1 := models.GetUserOrgListQuery{UserId: currentUser.Id}
|
||||||
err = GetUserOrgList(&q1)
|
err = GetUserOrgList(&q1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, models.RoleType(role), q1.Result[0].Role)
|
require.Equal(t, models.RoleType(role), q1.Result[0].Role)
|
||||||
|
|
||||||
return currentUserCmd.Result
|
return *currentUser
|
||||||
}
|
|
||||||
|
|
||||||
func moveDashboard(orgId int64, dashboard *simplejson.Json, newFolderId int64) *models.Dashboard {
|
|
||||||
cmd := models.SaveDashboardCommand{
|
|
||||||
OrgId: orgId,
|
|
||||||
FolderId: newFolderId,
|
|
||||||
Dashboard: dashboard,
|
|
||||||
Overwrite: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := SaveDashboard(&cmd)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
return cmd.Result
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func updateTestDashboard(dashboard *models.Dashboard, data map[string]interface{}) {
|
func updateTestDashboard(t *testing.T, sqlStore *SQLStore, dashboard *models.Dashboard, data map[string]interface{}) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
data["id"] = dashboard.Id
|
data["id"] = dashboard.Id
|
||||||
|
|
||||||
saveCmd := models.SaveDashboardCommand{
|
saveCmd := models.SaveDashboardCommand{
|
||||||
@@ -21,17 +23,16 @@ func updateTestDashboard(dashboard *models.Dashboard, data map[string]interface{
|
|||||||
Overwrite: true,
|
Overwrite: true,
|
||||||
Dashboard: simplejson.NewFromAny(data),
|
Dashboard: simplejson.NewFromAny(data),
|
||||||
}
|
}
|
||||||
|
_, err := sqlStore.SaveDashboard(saveCmd)
|
||||||
err := SaveDashboard(&saveCmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDashboardVersion(t *testing.T) {
|
func TestGetDashboardVersion(t *testing.T) {
|
||||||
Convey("Testing dashboard version retrieval", t, func() {
|
Convey("Testing dashboard version retrieval", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Get a Dashboard ID and version ID", func() {
|
Convey("Get a Dashboard ID and version ID", func() {
|
||||||
savedDash := insertTestDashboard(t, "test dash 26", 1, 0, false, "diff")
|
savedDash := insertTestDashboard(t, sqlStore, "test dash 26", 1, 0, false, "diff")
|
||||||
|
|
||||||
query := models.GetDashboardVersionQuery{
|
query := models.GetDashboardVersionQuery{
|
||||||
DashboardId: savedDash.Id,
|
DashboardId: savedDash.Id,
|
||||||
@@ -71,8 +72,8 @@ func TestGetDashboardVersion(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetDashboardVersions(t *testing.T) {
|
func TestGetDashboardVersions(t *testing.T) {
|
||||||
Convey("Testing dashboard versions retrieval", t, func() {
|
Convey("Testing dashboard versions retrieval", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
savedDash := insertTestDashboard(t, "test dash 43", 1, 0, false, "diff-all")
|
savedDash := insertTestDashboard(t, sqlStore, "test dash 43", 1, 0, false, "diff-all")
|
||||||
|
|
||||||
Convey("Get all versions for a given Dashboard ID", func() {
|
Convey("Get all versions for a given Dashboard ID", func() {
|
||||||
query := models.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1}
|
query := models.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1}
|
||||||
@@ -92,7 +93,7 @@ func TestGetDashboardVersions(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Get all versions for an updated dashboard", func() {
|
Convey("Get all versions for an updated dashboard", func() {
|
||||||
updateTestDashboard(savedDash, map[string]interface{}{
|
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
|
||||||
"tags": "different-tag",
|
"tags": "different-tag",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -107,14 +108,14 @@ func TestGetDashboardVersions(t *testing.T) {
|
|||||||
|
|
||||||
func TestDeleteExpiredVersions(t *testing.T) {
|
func TestDeleteExpiredVersions(t *testing.T) {
|
||||||
Convey("Testing dashboard versions clean up", t, func() {
|
Convey("Testing dashboard versions clean up", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
versionsToKeep := 5
|
versionsToKeep := 5
|
||||||
versionsToWrite := 10
|
versionsToWrite := 10
|
||||||
setting.DashboardVersionsToKeep = versionsToKeep
|
setting.DashboardVersionsToKeep = versionsToKeep
|
||||||
|
|
||||||
savedDash := insertTestDashboard(t, "test dash 53", 1, 0, false, "diff-all")
|
savedDash := insertTestDashboard(t, sqlStore, "test dash 53", 1, 0, false, "diff-all")
|
||||||
for i := 0; i < versionsToWrite-1; i++ {
|
for i := 0; i < versionsToWrite-1; i++ {
|
||||||
updateTestDashboard(savedDash, map[string]interface{}{
|
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
|
||||||
"tags": "different-tag",
|
"tags": "different-tag",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,7 @@ func TestDeleteExpiredVersions(t *testing.T) {
|
|||||||
|
|
||||||
versionsToWriteBigNumber := perBatch*maxBatches + versionsToWrite
|
versionsToWriteBigNumber := perBatch*maxBatches + versionsToWrite
|
||||||
for i := 0; i < versionsToWriteBigNumber-versionsToWrite; i++ {
|
for i := 0; i < versionsToWriteBigNumber-versionsToWrite; i++ {
|
||||||
updateTestDashboard(savedDash, map[string]interface{}{
|
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
|
||||||
"tags": "different-tag",
|
"tags": "different-tag",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
func TestAccountDataAccess(t *testing.T) {
|
func TestAccountDataAccess(t *testing.T) {
|
||||||
Convey("Testing Account DB Access", t, func() {
|
Convey("Testing Account DB Access", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Given we have organizations, we can query them by IDs", func() {
|
Convey("Given we have organizations, we can query them by IDs", func() {
|
||||||
var err error
|
var err error
|
||||||
@@ -78,13 +78,13 @@ func TestAccountDataAccess(t *testing.T) {
|
|||||||
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
|
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
|
||||||
ac2cmd := models.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
|
ac2cmd := models.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
|
||||||
|
|
||||||
err := CreateUser(context.Background(), &ac1cmd)
|
ac1, err := sqlStore.CreateUser(context.Background(), ac1cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = CreateUser(context.Background(), &ac2cmd)
|
ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
q1 := models.GetUserOrgListQuery{UserId: ac1cmd.Result.Id}
|
q1 := models.GetUserOrgListQuery{UserId: ac1.Id}
|
||||||
q2 := models.GetUserOrgListQuery{UserId: ac2cmd.Result.Id}
|
q2 := models.GetUserOrgListQuery{UserId: ac2.Id}
|
||||||
err = GetUserOrgList(&q1)
|
err = GetUserOrgList(&q1)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = GetUserOrgList(&q2)
|
err = GetUserOrgList(&q2)
|
||||||
@@ -101,13 +101,10 @@ func TestAccountDataAccess(t *testing.T) {
|
|||||||
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
|
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
|
||||||
ac2cmd := models.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
|
ac2cmd := models.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
|
||||||
|
|
||||||
err := CreateUser(context.Background(), &ac1cmd)
|
ac1, err := sqlStore.CreateUser(context.Background(), ac1cmd)
|
||||||
err = CreateUser(context.Background(), &ac2cmd)
|
ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
ac1 := ac1cmd.Result
|
|
||||||
ac2 := ac2cmd.Result
|
|
||||||
|
|
||||||
Convey("Should be able to read user info projection", func() {
|
Convey("Should be able to read user info projection", func() {
|
||||||
query := models.GetUserProfileQuery{UserId: ac1.Id}
|
query := models.GetUserProfileQuery{UserId: ac1.Id}
|
||||||
err = GetUserProfile(&query)
|
err = GetUserProfile(&query)
|
||||||
@@ -266,9 +263,8 @@ func TestAccountDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Given an org user with dashboard permissions", func() {
|
Convey("Given an org user with dashboard permissions", func() {
|
||||||
ac3cmd := models.CreateUserCommand{Login: "ac3", Email: "ac3@test.com", Name: "ac3 name", IsAdmin: false}
|
ac3cmd := models.CreateUserCommand{Login: "ac3", Email: "ac3@test.com", Name: "ac3 name", IsAdmin: false}
|
||||||
err := CreateUser(context.Background(), &ac3cmd)
|
ac3, err := sqlStore.CreateUser(context.Background(), ac3cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
ac3 := ac3cmd.Result
|
|
||||||
|
|
||||||
orgUserCmd := models.AddOrgUserCommand{
|
orgUserCmd := models.AddOrgUserCommand{
|
||||||
OrgId: ac1.OrgId,
|
OrgId: ac1.OrgId,
|
||||||
@@ -284,13 +280,17 @@ func TestAccountDataAccess(t *testing.T) {
|
|||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(query.Result), ShouldEqual, 3)
|
So(len(query.Result), ShouldEqual, 3)
|
||||||
|
|
||||||
dash1 := insertTestDashboard(t, "1 test dash", ac1.OrgId, 0, false, "prod", "webapp")
|
dash1 := insertTestDashboard(t, sqlStore, "1 test dash", ac1.OrgId, 0, false, "prod", "webapp")
|
||||||
dash2 := insertTestDashboard(t, "2 test dash", ac3.OrgId, 0, false, "prod", "webapp")
|
dash2 := insertTestDashboard(t, sqlStore, "2 test dash", ac3.OrgId, 0, false, "prod", "webapp")
|
||||||
|
|
||||||
err = testHelperUpdateDashboardAcl(dash1.Id, models.DashboardAcl{DashboardID: dash1.Id, OrgID: ac1.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT})
|
err = testHelperUpdateDashboardAcl(t, sqlStore, dash1.Id, models.DashboardAcl{
|
||||||
|
DashboardID: dash1.Id, OrgID: ac1.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT,
|
||||||
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = testHelperUpdateDashboardAcl(dash2.Id, models.DashboardAcl{DashboardID: dash2.Id, OrgID: ac3.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT})
|
err = testHelperUpdateDashboardAcl(t, sqlStore, dash2.Id, models.DashboardAcl{
|
||||||
|
DashboardID: dash2.Id, OrgID: ac3.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT,
|
||||||
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("When org user is deleted", func() {
|
Convey("When org user is deleted", func() {
|
||||||
@@ -322,13 +322,16 @@ func TestAccountDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHelperUpdateDashboardAcl(dashboardId int64, items ...models.DashboardAcl) error {
|
func testHelperUpdateDashboardAcl(t *testing.T, sqlStore *SQLStore, dashboardID int64,
|
||||||
cmd := models.UpdateDashboardAclCommand{DashboardID: dashboardId}
|
items ...models.DashboardAcl) error {
|
||||||
for _, i := range items {
|
t.Helper()
|
||||||
item := i
|
|
||||||
|
var itemPtrs []*models.DashboardAcl
|
||||||
|
for _, it := range items {
|
||||||
|
item := it
|
||||||
item.Created = time.Now()
|
item.Created = time.Now()
|
||||||
item.Updated = time.Now()
|
item.Updated = time.Now()
|
||||||
cmd.Items = append(cmd.Items, &item)
|
itemPtrs = append(itemPtrs, &item)
|
||||||
}
|
}
|
||||||
return UpdateDashboardAcl(&cmd)
|
return sqlStore.UpdateDashboardACL(dashboardID, itemPtrs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,25 +10,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("sql", GetPluginSettings)
|
|
||||||
bus.AddHandler("sql", GetPluginSettingById)
|
bus.AddHandler("sql", GetPluginSettingById)
|
||||||
bus.AddHandler("sql", UpdatePluginSetting)
|
bus.AddHandler("sql", UpdatePluginSetting)
|
||||||
bus.AddHandler("sql", UpdatePluginSettingVersion)
|
bus.AddHandler("sql", UpdatePluginSettingVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPluginSettings(query *models.GetPluginSettingsQuery) error {
|
func (ss *SQLStore) GetPluginSettings(orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||||
sql := `SELECT org_id, plugin_id, enabled, pinned, plugin_version
|
sql := `SELECT org_id, plugin_id, enabled, pinned, plugin_version
|
||||||
FROM plugin_setting `
|
FROM plugin_setting `
|
||||||
params := make([]interface{}, 0)
|
params := make([]interface{}, 0)
|
||||||
|
|
||||||
if query.OrgId != 0 {
|
if orgID != 0 {
|
||||||
sql += "WHERE org_id=?"
|
sql += "WHERE org_id=?"
|
||||||
params = append(params, query.OrgId)
|
params = append(params, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := x.SQL(sql, params...)
|
sess := x.SQL(sql, params...)
|
||||||
query.Result = make([]*models.PluginSettingInfoDTO, 0)
|
var rslt []*models.PluginSettingInfoDTO
|
||||||
return sess.Find(&query.Result)
|
if err := sess.Find(&rslt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rslt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPluginSettingById(query *models.GetPluginSettingByIdQuery) error {
|
func GetPluginSettingById(query *models.GetPluginSettingByIdQuery) error {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ package searchstore_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -34,13 +33,11 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db := setupTestEnvironment(t)
|
db := setupTestEnvironment(t)
|
||||||
err := createDashboards(0, 1, user.OrgId)
|
createDashboards(t, db, 0, 1, user.OrgId)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// create one dashboard in another organization that shouldn't
|
// create one dashboard in another organization that shouldn't
|
||||||
// be listed in the results.
|
// be listed in the results.
|
||||||
err = createDashboards(1, 2, 2)
|
createDashboards(t, db, 1, 2, 2)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
builder := &searchstore.Builder{
|
builder := &searchstore.Builder{
|
||||||
Filters: []interface{}{
|
Filters: []interface{}{
|
||||||
@@ -51,7 +48,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := []sqlstore.DashboardSearchProjection{}
|
res := []sqlstore.DashboardSearchProjection{}
|
||||||
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
sql, params := builder.ToSQL(limit, page)
|
sql, params := builder.ToSQL(limit, page)
|
||||||
return sess.SQL(sql, params...).Find(&res)
|
return sess.SQL(sql, params...).Find(&res)
|
||||||
})
|
})
|
||||||
@@ -77,8 +74,7 @@ func TestBuilder_Pagination(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db := setupTestEnvironment(t)
|
db := setupTestEnvironment(t)
|
||||||
err := createDashboards(0, 25, user.OrgId)
|
createDashboards(t, db, 0, 25, user.OrgId)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
builder := &searchstore.Builder{
|
builder := &searchstore.Builder{
|
||||||
Filters: []interface{}{
|
Filters: []interface{}{
|
||||||
@@ -91,7 +87,7 @@ func TestBuilder_Pagination(t *testing.T) {
|
|||||||
resPg1 := []sqlstore.DashboardSearchProjection{}
|
resPg1 := []sqlstore.DashboardSearchProjection{}
|
||||||
resPg2 := []sqlstore.DashboardSearchProjection{}
|
resPg2 := []sqlstore.DashboardSearchProjection{}
|
||||||
resPg3 := []sqlstore.DashboardSearchProjection{}
|
resPg3 := []sqlstore.DashboardSearchProjection{}
|
||||||
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
sql, params := builder.ToSQL(15, 1)
|
sql, params := builder.ToSQL(15, 1)
|
||||||
err := sess.SQL(sql, params...).Find(&resPg1)
|
err := sess.SQL(sql, params...).Find(&resPg1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -124,8 +120,7 @@ func TestBuilder_Permissions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db := setupTestEnvironment(t)
|
db := setupTestEnvironment(t)
|
||||||
err := createDashboards(0, 1, user.OrgId)
|
createDashboards(t, db, 0, 1, user.OrgId)
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
level := models.PERMISSION_EDIT
|
level := models.PERMISSION_EDIT
|
||||||
|
|
||||||
@@ -145,7 +140,7 @@ func TestBuilder_Permissions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := []sqlstore.DashboardSearchProjection{}
|
res := []sqlstore.DashboardSearchProjection{}
|
||||||
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
sql, params := builder.ToSQL(limit, page)
|
sql, params := builder.ToSQL(limit, page)
|
||||||
return sess.SQL(sql, params...).Find(&res)
|
return sess.SQL(sql, params...).Find(&res)
|
||||||
})
|
})
|
||||||
@@ -161,10 +156,10 @@ func setupTestEnvironment(t *testing.T) *sqlstore.SQLStore {
|
|||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDashboards(startID, endID int, orgID int64) error {
|
func createDashboards(t *testing.T, db *sqlstore.SQLStore, startID, endID int, orgID int64) {
|
||||||
if endID < startID {
|
t.Helper()
|
||||||
return fmt.Errorf("startID must be smaller than endID")
|
|
||||||
}
|
require.GreaterOrEqual(t, endID, startID)
|
||||||
|
|
||||||
for i := startID; i < endID; i++ {
|
for i := startID; i < endID; i++ {
|
||||||
dashboard, err := simplejson.NewJson([]byte(`{
|
dashboard, err := simplejson.NewJson([]byte(`{
|
||||||
@@ -176,20 +171,15 @@ func createDashboards(startID, endID int, orgID int64) error {
|
|||||||
"schemaVersion": 16,
|
"schemaVersion": 16,
|
||||||
"version": 0
|
"version": 0
|
||||||
}`))
|
}`))
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return err
|
_, err = db.SaveDashboard(models.SaveDashboardCommand{
|
||||||
}
|
|
||||||
err = sqlstore.SaveDashboard(&models.SaveDashboardCommand{
|
|
||||||
Dashboard: dashboard,
|
Dashboard: dashboard,
|
||||||
UserId: 1,
|
UserId: 1,
|
||||||
OrgId: orgID,
|
OrgId: orgID,
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lexiCounter counts in a lexicographically sortable order.
|
// lexiCounter counts in a lexicographically sortable order.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSQLBuilder(t *testing.T) {
|
func TestSQLBuilder(t *testing.T) {
|
||||||
@@ -138,8 +139,8 @@ func TestSQLBuilder(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldFind = true
|
const shouldFind = true
|
||||||
var shouldNotFind = false
|
const shouldNotFind = false
|
||||||
|
|
||||||
type DashboardProps struct {
|
type DashboardProps struct {
|
||||||
OrgId int64
|
OrgId int64
|
||||||
@@ -164,38 +165,35 @@ type dashboardResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool) {
|
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool) {
|
||||||
// Will also cleanup the db
|
t.Helper()
|
||||||
sqlStore := InitTestDB(t)
|
|
||||||
|
|
||||||
dashboard, err := createDummyDashboard(dashboardProps)
|
t.Run("", func(t *testing.T) {
|
||||||
if !assert.Equal(t, nil, err) {
|
// Will also cleanup the db
|
||||||
return
|
sqlStore := InitTestDB(t)
|
||||||
}
|
|
||||||
|
|
||||||
var aclUserId int64
|
dashboard := createDummyDashboard(t, sqlStore, dashboardProps)
|
||||||
if dashboardPermission != nil {
|
|
||||||
aclUserId, err = createDummyAcl(dashboardPermission, search, dashboard.Id)
|
var aclUserID int64
|
||||||
if !assert.Equal(t, nil, err) {
|
if dashboardPermission != nil {
|
||||||
return
|
aclUserID = createDummyACL(t, sqlStore, dashboardPermission, search, dashboard.Id)
|
||||||
|
t.Logf("Created ACL with user ID %d\n", aclUserID)
|
||||||
}
|
}
|
||||||
}
|
dashboards := getDashboards(t, sqlStore, search, aclUserID)
|
||||||
dashboards, err := getDashboards(sqlStore, search, aclUserId)
|
|
||||||
if !assert.Equal(t, nil, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldFind {
|
if shouldFind {
|
||||||
if assert.Equal(t, 1, len(dashboards), "Should return one dashboard") {
|
require.Len(t, dashboards, 1, "Should return one dashboard")
|
||||||
assert.Equal(t, dashboards[0].Id, dashboard.Id, "Should return created dashboard")
|
assert.Equal(t, dashboard.Id, dashboards[0].Id, "Should return created dashboard")
|
||||||
|
} else {
|
||||||
|
assert.Empty(t, dashboards, "Should not return any dashboard")
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
assert.Equal(t, 0, len(dashboards), "Should node return any dashboard")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDummyUser() (*models.User, error) {
|
func createDummyUser(t *testing.T, sqlStore *SQLStore) *models.User {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
uid := strconv.Itoa(rand.Intn(9999999))
|
uid := strconv.Itoa(rand.Intn(9999999))
|
||||||
createUserCmd := &models.CreateUserCommand{
|
createUserCmd := models.CreateUserCommand{
|
||||||
Email: uid + "@example.com",
|
Email: uid + "@example.com",
|
||||||
Login: uid,
|
Login: uid,
|
||||||
Name: uid,
|
Name: uid,
|
||||||
@@ -207,33 +205,28 @@ func createDummyUser() (*models.User, error) {
|
|||||||
SkipOrgSetup: false,
|
SkipOrgSetup: false,
|
||||||
DefaultOrgRole: string(models.ROLE_VIEWER),
|
DefaultOrgRole: string(models.ROLE_VIEWER),
|
||||||
}
|
}
|
||||||
err := CreateUser(context.Background(), createUserCmd)
|
user, err := sqlStore.CreateUser(context.Background(), createUserCmd)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &createUserCmd.Result, nil
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDummyTeam() (*models.Team, error) {
|
func createDummyTeam(t *testing.T, sqlStore *SQLStore) models.Team {
|
||||||
cmd := &models.CreateTeamCommand{
|
t.Helper()
|
||||||
// Does not matter in this tests actually
|
|
||||||
OrgId: 1,
|
|
||||||
Name: "test",
|
|
||||||
Email: "test@example.com",
|
|
||||||
}
|
|
||||||
err := CreateTeam(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cmd.Result, nil
|
team, err := sqlStore.CreateTeam("test", "test@example.com", 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return team
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDummyDashboard(dashboardProps DashboardProps) (*models.Dashboard, error) {
|
func createDummyDashboard(t *testing.T, sqlStore *SQLStore, dashboardProps DashboardProps) *models.Dashboard {
|
||||||
json, _ := simplejson.NewJson([]byte(`{"schemaVersion":17,"title":"gdev dashboards","uid":"","version":1}`))
|
t.Helper()
|
||||||
|
|
||||||
saveDashboardCmd := &models.SaveDashboardCommand{
|
json, err := simplejson.NewJson([]byte(`{"schemaVersion":17,"title":"gdev dashboards","uid":"","version":1}`))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
saveDashboardCmd := models.SaveDashboardCommand{
|
||||||
Dashboard: json,
|
Dashboard: json,
|
||||||
UserId: 0,
|
UserId: 0,
|
||||||
Overwrite: false,
|
Overwrite: false,
|
||||||
@@ -250,53 +243,40 @@ func createDummyDashboard(dashboardProps DashboardProps) (*models.Dashboard, err
|
|||||||
saveDashboardCmd.OrgId = 1
|
saveDashboardCmd.OrgId = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
err := SaveDashboard(saveDashboardCmd)
|
dash, err := sqlStore.SaveDashboard(saveDashboardCmd)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return saveDashboardCmd.Result, nil
|
t.Logf("Created dashboard with ID %d and org ID %d\n", dash.Id, dash.OrgId)
|
||||||
|
return dash
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDummyAcl(dashboardPermission *DashboardPermission, search Search, dashboardId int64) (int64, error) {
|
func createDummyACL(t *testing.T, sqlStore *SQLStore, dashboardPermission *DashboardPermission, search Search, dashboardID int64) int64 {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
acl := &models.DashboardAcl{
|
acl := &models.DashboardAcl{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
Updated: time.Now(),
|
Updated: time.Now(),
|
||||||
Permission: dashboardPermission.Permission,
|
Permission: dashboardPermission.Permission,
|
||||||
DashboardID: dashboardId,
|
DashboardID: dashboardID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var user *models.User
|
var user *models.User
|
||||||
var err error
|
|
||||||
if dashboardPermission.User {
|
if dashboardPermission.User {
|
||||||
user, err = createDummyUser()
|
t.Logf("Creating user")
|
||||||
if err != nil {
|
user = createDummyUser(t, sqlStore)
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
acl.UserID = user.Id
|
acl.UserID = user.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
if dashboardPermission.Team {
|
if dashboardPermission.Team {
|
||||||
team, err := createDummyTeam()
|
t.Logf("Creating team")
|
||||||
if err != nil {
|
team := createDummyTeam(t, sqlStore)
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if search.UserFromACL {
|
if search.UserFromACL {
|
||||||
user, err = createDummyUser()
|
user = createDummyUser(t, sqlStore)
|
||||||
if err != nil {
|
err := sqlStore.AddTeamMember(user.Id, 1, team.Id, false, 0)
|
||||||
return 0, err
|
require.NoError(t, err)
|
||||||
}
|
t.Logf("Created team member with ID %d", user.Id)
|
||||||
addTeamMemberCmd := &models.AddTeamMemberCommand{
|
|
||||||
UserId: user.Id,
|
|
||||||
OrgId: 1,
|
|
||||||
TeamId: team.Id,
|
|
||||||
}
|
|
||||||
err = AddTeamMember(addTeamMemberCmd)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acl.TeamID = team.Id
|
acl.TeamID = team.Id
|
||||||
@@ -306,18 +286,17 @@ func createDummyAcl(dashboardPermission *DashboardPermission, search Search, das
|
|||||||
acl.Role = &dashboardPermission.Role
|
acl.Role = &dashboardPermission.Role
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAclCmd := &models.UpdateDashboardAclCommand{
|
err := sqlStore.UpdateDashboardACL(dashboardID, []*models.DashboardAcl{acl})
|
||||||
DashboardID: dashboardId,
|
require.NoError(t, err)
|
||||||
Items: []*models.DashboardAcl{acl},
|
|
||||||
}
|
|
||||||
err = UpdateDashboardAcl(updateAclCmd)
|
|
||||||
if user != nil {
|
if user != nil {
|
||||||
return user.Id, err
|
return user.Id
|
||||||
}
|
}
|
||||||
return 0, err
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDashboards(sqlStore *SQLStore, search Search, aclUserId int64) ([]*dashboardResponse, error) {
|
func getDashboards(t *testing.T, sqlStore *SQLStore, search Search, aclUserID int64) []*dashboardResponse {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
builder := &SQLBuilder{}
|
builder := &SQLBuilder{}
|
||||||
signedInUser := &models.SignedInUser{
|
signedInUser := &models.SignedInUser{
|
||||||
UserId: 9999999999,
|
UserId: 9999999999,
|
||||||
@@ -335,12 +314,14 @@ func getDashboards(sqlStore *SQLStore, search Search, aclUserId int64) ([]*dashb
|
|||||||
signedInUser.OrgRole = models.ROLE_VIEWER
|
signedInUser.OrgRole = models.ROLE_VIEWER
|
||||||
}
|
}
|
||||||
if search.UserFromACL {
|
if search.UserFromACL {
|
||||||
signedInUser.UserId = aclUserId
|
signedInUser.UserId = aclUserID
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []*dashboardResponse
|
var res []*dashboardResponse
|
||||||
builder.Write("SELECT * FROM dashboard WHERE true")
|
builder.Write("SELECT * FROM dashboard WHERE true")
|
||||||
builder.WriteDashboardPermissionFilter(signedInUser, search.RequiredPermission)
|
builder.WriteDashboardPermissionFilter(signedInUser, search.RequiredPermission)
|
||||||
|
t.Logf("Searching for dashboards, SQL: %q\n", builder.GetSQLString())
|
||||||
err := sqlStore.engine.SQL(builder.GetSQLString(), builder.params...).Find(&res)
|
err := sqlStore.engine.SQL(builder.GetSQLString(), builder.params...).Find(&res)
|
||||||
return res, err
|
require.NoError(t, err)
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
_ "github.com/grafana/grafana/pkg/tsdb/mssql"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestStatsDataAccess(t *testing.T) {
|
func TestStatsDataAccess(t *testing.T) {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
populateDB(t)
|
populateDB(t, sqlStore)
|
||||||
|
|
||||||
t.Run("Get system stats should not results in error", func(t *testing.T) {
|
t.Run("Get system stats should not results in error", func(t *testing.T) {
|
||||||
query := models.GetSystemStatsQuery{}
|
query := models.GetSystemStatsQuery{}
|
||||||
@@ -57,18 +57,20 @@ func TestStatsDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func populateDB(t *testing.T) {
|
func populateDB(t *testing.T, sqlStore *SQLStore) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
users := make([]models.User, 3)
|
users := make([]models.User, 3)
|
||||||
for i := range users {
|
for i := range users {
|
||||||
cmd := &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: fmt.Sprintf("usertest%v@test.com", i),
|
Email: fmt.Sprintf("usertest%v@test.com", i),
|
||||||
Name: fmt.Sprintf("user name %v", i),
|
Name: fmt.Sprintf("user name %v", i),
|
||||||
Login: fmt.Sprintf("user_test_%v_login", i),
|
Login: fmt.Sprintf("user_test_%v_login", i),
|
||||||
OrgName: fmt.Sprintf("Org #%v", i),
|
OrgName: fmt.Sprintf("Org #%v", i),
|
||||||
}
|
}
|
||||||
err := CreateUser(context.Background(), cmd)
|
user, err := sqlStore.CreateUser(context.Background(), cmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
users[i] = cmd.Result
|
users[i] = *user
|
||||||
}
|
}
|
||||||
|
|
||||||
// get 1st user's organisation
|
// get 1st user's organisation
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -11,14 +12,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("sql", CreateTeam)
|
|
||||||
bus.AddHandler("sql", UpdateTeam)
|
bus.AddHandler("sql", UpdateTeam)
|
||||||
bus.AddHandler("sql", DeleteTeam)
|
bus.AddHandler("sql", DeleteTeam)
|
||||||
bus.AddHandler("sql", SearchTeams)
|
bus.AddHandler("sql", SearchTeams)
|
||||||
bus.AddHandler("sql", GetTeamById)
|
bus.AddHandler("sql", GetTeamById)
|
||||||
bus.AddHandler("sql", GetTeamsByUser)
|
bus.AddHandler("sql", GetTeamsByUser)
|
||||||
|
|
||||||
bus.AddHandler("sql", AddTeamMember)
|
|
||||||
bus.AddHandler("sql", UpdateTeamMember)
|
bus.AddHandler("sql", UpdateTeamMember)
|
||||||
bus.AddHandler("sql", RemoveTeamMember)
|
bus.AddHandler("sql", RemoveTeamMember)
|
||||||
bus.AddHandler("sql", GetTeamMembers)
|
bus.AddHandler("sql", GetTeamMembers)
|
||||||
@@ -43,7 +42,7 @@ func getFilteredUsers(signedInUser *models.SignedInUser, hiddenUsers map[string]
|
|||||||
|
|
||||||
func getTeamMemberCount(filteredUsers []string) string {
|
func getTeamMemberCount(filteredUsers []string) string {
|
||||||
if len(filteredUsers) > 0 {
|
if len(filteredUsers) > 0 {
|
||||||
return `(SELECT COUNT(*) FROM team_member
|
return `(SELECT COUNT(*) FROM team_member
|
||||||
INNER JOIN ` + dialect.Quote("user") + ` ON team_member.user_id = ` + dialect.Quote("user") + `.id
|
INNER JOIN ` + dialect.Quote("user") + ` ON team_member.user_id = ` + dialect.Quote("user") + `.id
|
||||||
WHERE team_member.team_id = team.id AND ` + dialect.Quote("user") + `.login NOT IN (?` +
|
WHERE team_member.team_id = team.id AND ` + dialect.Quote("user") + `.login NOT IN (?` +
|
||||||
strings.Repeat(",?", len(filteredUsers)-1) + ")" +
|
strings.Repeat(",?", len(filteredUsers)-1) + ")" +
|
||||||
@@ -75,28 +74,25 @@ func getTeamSelectSQLBase(filteredUsers []string) string {
|
|||||||
` FROM team as team `
|
` FROM team as team `
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTeam(cmd *models.CreateTeamCommand) error {
|
func (ss *SQLStore) CreateTeam(name, email string, orgID int64) (models.Team, error) {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
team := models.Team{
|
||||||
if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, 0, sess); err != nil {
|
Name: name,
|
||||||
|
Email: email,
|
||||||
|
OrgId: orgID,
|
||||||
|
Created: time.Now(),
|
||||||
|
Updated: time.Now(),
|
||||||
|
}
|
||||||
|
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
|
if isNameTaken, err := isTeamNameTaken(orgID, name, 0, sess); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if isNameTaken {
|
} else if isNameTaken {
|
||||||
return models.ErrTeamNameTaken
|
return models.ErrTeamNameTaken
|
||||||
}
|
}
|
||||||
|
|
||||||
team := models.Team{
|
|
||||||
Name: cmd.Name,
|
|
||||||
Email: cmd.Email,
|
|
||||||
OrgId: cmd.OrgId,
|
|
||||||
Created: time.Now(),
|
|
||||||
Updated: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := sess.Insert(&team)
|
_, err := sess.Insert(&team)
|
||||||
|
|
||||||
cmd.Result = team
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
return team, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateTeam(cmd *models.UpdateTeamCommand) error {
|
func UpdateTeam(cmd *models.UpdateTeamCommand) error {
|
||||||
@@ -152,8 +148,8 @@ func DeleteTeam(cmd *models.DeleteTeamCommand) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) {
|
func teamExists(orgID int64, teamID int64, sess *DBSession) (bool, error) {
|
||||||
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgId, teamId); err != nil {
|
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgID, teamID); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if len(res) != 1 {
|
} else if len(res) != 1 {
|
||||||
return false, models.ErrTeamNotFound
|
return false, models.ErrTeamNotFound
|
||||||
@@ -165,7 +161,6 @@ func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) {
|
|||||||
func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession) (bool, error) {
|
func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession) (bool, error) {
|
||||||
var team models.Team
|
var team models.Team
|
||||||
exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team)
|
exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -283,26 +278,27 @@ func GetTeamsByUser(query *models.GetTeamsByUserQuery) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamMember adds a user to a team
|
// AddTeamMember adds a user to a team
|
||||||
func AddTeamMember(cmd *models.AddTeamMemberCommand) error {
|
func (ss *SQLStore) AddTeamMember(userID, orgID, teamID int64, isExternal bool, permission models.PermissionType) error {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
if res, err := sess.Query("SELECT 1 from team_member WHERE org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId); err != nil {
|
if res, err := sess.Query("SELECT 1 from team_member WHERE org_id=? and team_id=? and user_id=?",
|
||||||
|
orgID, teamID, userID); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if len(res) == 1 {
|
} else if len(res) == 1 {
|
||||||
return models.ErrTeamMemberAlreadyAdded
|
return models.ErrTeamMemberAlreadyAdded
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil {
|
if _, err := teamExists(orgID, teamID, sess); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
entity := models.TeamMember{
|
entity := models.TeamMember{
|
||||||
OrgId: cmd.OrgId,
|
OrgId: orgID,
|
||||||
TeamId: cmd.TeamId,
|
TeamId: teamID,
|
||||||
UserId: cmd.UserId,
|
UserId: userID,
|
||||||
External: cmd.External,
|
External: isExternal,
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
Updated: time.Now(),
|
Updated: time.Now(),
|
||||||
Permission: cmd.Permission,
|
Permission: permission,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := sess.Insert(&entity)
|
_, err := sess.Insert(&entity)
|
||||||
|
|||||||
@@ -14,32 +14,29 @@ import (
|
|||||||
|
|
||||||
func TestTeamCommandsAndQueries(t *testing.T) {
|
func TestTeamCommandsAndQueries(t *testing.T) {
|
||||||
Convey("Testing Team commands & queries", t, func() {
|
Convey("Testing Team commands & queries", t, func() {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Given saved users and two teams", func() {
|
Convey("Given saved users and two teams", func() {
|
||||||
var userIds []int64
|
var userIds []int64
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
userCmd := &models.CreateUserCommand{
|
userCmd := models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
Login: fmt.Sprint("loginuser", i),
|
Login: fmt.Sprint("loginuser", i),
|
||||||
}
|
}
|
||||||
err := CreateUser(context.Background(), userCmd)
|
user, err := sqlStore.CreateUser(context.Background(), userCmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
userIds = append(userIds, userCmd.Result.Id)
|
userIds = append(userIds, user.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
var testOrgId int64 = 1
|
const testOrgID int64 = 1
|
||||||
group1 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group1 name", Email: "test1@test.com"}
|
team1, err := sqlStore.CreateTeam("group1 name", "test1@test.com", testOrgID)
|
||||||
group2 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group2 name", Email: "test2@test.com"}
|
|
||||||
|
|
||||||
err := CreateTeam(&group1)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = CreateTeam(&group2)
|
team2, err := sqlStore.CreateTeam("group2 name", "test2@test.com", testOrgID)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Should be able to create teams and add users", func() {
|
Convey("Should be able to create teams and add users", func() {
|
||||||
query := &models.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10}
|
query := &models.SearchTeamsQuery{OrgId: testOrgID, Name: "group1 name", Page: 1, Limit: 10}
|
||||||
err = SearchTeams(query)
|
err = SearchTeams(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Page, ShouldEqual, 1)
|
So(query.Page, ShouldEqual, 1)
|
||||||
@@ -47,33 +44,33 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
team1 := query.Result.Teams[0]
|
team1 := query.Result.Teams[0]
|
||||||
So(team1.Name, ShouldEqual, "group1 name")
|
So(team1.Name, ShouldEqual, "group1 name")
|
||||||
So(team1.Email, ShouldEqual, "test1@test.com")
|
So(team1.Email, ShouldEqual, "test1@test.com")
|
||||||
So(team1.OrgId, ShouldEqual, testOrgId)
|
So(team1.OrgId, ShouldEqual, testOrgID)
|
||||||
So(team1.MemberCount, ShouldEqual, 0)
|
So(team1.MemberCount, ShouldEqual, 0)
|
||||||
|
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[0]})
|
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[1], External: true})
|
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, true, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
q1 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id}
|
q1 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id}
|
||||||
err = GetTeamMembers(q1)
|
err = GetTeamMembers(q1)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(q1.Result, ShouldHaveLength, 2)
|
So(q1.Result, ShouldHaveLength, 2)
|
||||||
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
|
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||||
So(q1.Result[0].Login, ShouldEqual, "loginuser0")
|
So(q1.Result[0].Login, ShouldEqual, "loginuser0")
|
||||||
So(q1.Result[0].OrgId, ShouldEqual, testOrgId)
|
So(q1.Result[0].OrgId, ShouldEqual, testOrgID)
|
||||||
So(q1.Result[1].TeamId, ShouldEqual, team1.Id)
|
So(q1.Result[1].TeamId, ShouldEqual, team1.Id)
|
||||||
So(q1.Result[1].Login, ShouldEqual, "loginuser1")
|
So(q1.Result[1].Login, ShouldEqual, "loginuser1")
|
||||||
So(q1.Result[1].OrgId, ShouldEqual, testOrgId)
|
So(q1.Result[1].OrgId, ShouldEqual, testOrgID)
|
||||||
So(q1.Result[1].External, ShouldEqual, true)
|
So(q1.Result[1].External, ShouldEqual, true)
|
||||||
|
|
||||||
q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
|
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true}
|
||||||
err = GetTeamMembers(q2)
|
err = GetTeamMembers(q2)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(q2.Result, ShouldHaveLength, 1)
|
So(q2.Result, ShouldHaveLength, 1)
|
||||||
So(q2.Result[0].TeamId, ShouldEqual, team1.Id)
|
So(q2.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||||
So(q2.Result[0].Login, ShouldEqual, "loginuser1")
|
So(q2.Result[0].Login, ShouldEqual, "loginuser1")
|
||||||
So(q2.Result[0].OrgId, ShouldEqual, testOrgId)
|
So(q2.Result[0].OrgId, ShouldEqual, testOrgID)
|
||||||
So(q2.Result[0].External, ShouldEqual, true)
|
So(q2.Result[0].External, ShouldEqual, true)
|
||||||
|
|
||||||
err = SearchTeams(query)
|
err = SearchTeams(query)
|
||||||
@@ -81,13 +78,13 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
team1 = query.Result.Teams[0]
|
team1 = query.Result.Teams[0]
|
||||||
So(team1.MemberCount, ShouldEqual, 2)
|
So(team1.MemberCount, ShouldEqual, 2)
|
||||||
|
|
||||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: team1.Id}
|
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: team1.Id}
|
||||||
err = GetTeamById(getTeamQuery)
|
err = GetTeamById(getTeamQuery)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
team1 = getTeamQuery.Result
|
team1 = getTeamQuery.Result
|
||||||
So(team1.Name, ShouldEqual, "group1 name")
|
So(team1.Name, ShouldEqual, "group1 name")
|
||||||
So(team1.Email, ShouldEqual, "test1@test.com")
|
So(team1.Email, ShouldEqual, "test1@test.com")
|
||||||
So(team1.OrgId, ShouldEqual, testOrgId)
|
So(team1.OrgId, ShouldEqual, testOrgID)
|
||||||
So(team1.MemberCount, ShouldEqual, 2)
|
So(team1.MemberCount, ShouldEqual, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -96,49 +93,48 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
err := SetAuthInfo(&models.SetAuthInfoCommand{UserId: userId, AuthModule: "oauth_github", AuthId: "1234567"})
|
err := SetAuthInfo(&models.SetAuthInfoCommand{UserId: userId, AuthModule: "oauth_github", AuthId: "1234567"})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
teamQuery := &models.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10}
|
teamQuery := &models.SearchTeamsQuery{OrgId: testOrgID, Name: "group1 name", Page: 1, Limit: 10}
|
||||||
err = SearchTeams(teamQuery)
|
err = SearchTeams(teamQuery)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(teamQuery.Page, ShouldEqual, 1)
|
So(teamQuery.Page, ShouldEqual, 1)
|
||||||
|
|
||||||
team1 := teamQuery.Result.Teams[0]
|
team1 := teamQuery.Result.Teams[0]
|
||||||
|
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userId, External: true})
|
err = sqlStore.AddTeamMember(userId, testOrgID, team1.Id, true, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
|
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true}
|
||||||
err = GetTeamMembers(memberQuery)
|
err = GetTeamMembers(memberQuery)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(memberQuery.Result, ShouldHaveLength, 1)
|
So(memberQuery.Result, ShouldHaveLength, 1)
|
||||||
So(memberQuery.Result[0].TeamId, ShouldEqual, team1.Id)
|
So(memberQuery.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||||
So(memberQuery.Result[0].Login, ShouldEqual, "loginuser1")
|
So(memberQuery.Result[0].Login, ShouldEqual, "loginuser1")
|
||||||
So(memberQuery.Result[0].OrgId, ShouldEqual, testOrgId)
|
So(memberQuery.Result[0].OrgId, ShouldEqual, testOrgID)
|
||||||
So(memberQuery.Result[0].External, ShouldEqual, true)
|
So(memberQuery.Result[0].External, ShouldEqual, true)
|
||||||
So(memberQuery.Result[0].AuthModule, ShouldEqual, "oauth_github")
|
So(memberQuery.Result[0].AuthModule, ShouldEqual, "oauth_github")
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to update users in a team", func() {
|
Convey("Should be able to update users in a team", func() {
|
||||||
userId := userIds[0]
|
userId := userIds[0]
|
||||||
team := group1.Result
|
team := team1
|
||||||
addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userId}
|
err = sqlStore.AddTeamMember(userId, testOrgID, team.Id, false, 0)
|
||||||
err = AddTeamMember(&addMemberCmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||||
err = GetTeamMembers(qBeforeUpdate)
|
err = GetTeamMembers(qBeforeUpdate)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
|
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
|
||||||
|
|
||||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgID,
|
||||||
TeamId: team.Id,
|
TeamId: team.Id,
|
||||||
Permission: models.PERMISSION_ADMIN,
|
Permission: models.PERMISSION_ADMIN,
|
||||||
})
|
})
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||||
err = GetTeamMembers(qAfterUpdate)
|
err = GetTeamMembers(qAfterUpdate)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(qAfterUpdate.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
|
So(qAfterUpdate.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
|
||||||
@@ -146,12 +142,11 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Should default to member permission level when updating a user with invalid permission level", func() {
|
Convey("Should default to member permission level when updating a user with invalid permission level", func() {
|
||||||
userID := userIds[0]
|
userID := userIds[0]
|
||||||
team := group1.Result
|
team := team1
|
||||||
addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userID}
|
err = sqlStore.AddTeamMember(userID, testOrgID, team.Id, false, 0)
|
||||||
err = AddTeamMember(&addMemberCmd)
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||||
err = GetTeamMembers(qBeforeUpdate)
|
err = GetTeamMembers(qBeforeUpdate)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
|
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
|
||||||
@@ -159,14 +154,14 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
invalidPermissionLevel := models.PERMISSION_EDIT
|
invalidPermissionLevel := models.PERMISSION_EDIT
|
||||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
||||||
UserId: userID,
|
UserId: userID,
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgID,
|
||||||
TeamId: team.Id,
|
TeamId: team.Id,
|
||||||
Permission: invalidPermissionLevel,
|
Permission: invalidPermissionLevel,
|
||||||
})
|
})
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||||
err = GetTeamMembers(qAfterUpdate)
|
err = GetTeamMembers(qAfterUpdate)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(qAfterUpdate.Result[0].Permission, ShouldEqual, 0)
|
So(qAfterUpdate.Result[0].Permission, ShouldEqual, 0)
|
||||||
@@ -175,8 +170,8 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
Convey("Shouldn't be able to update a user not in the team.", func() {
|
Convey("Shouldn't be able to update a user not in the team.", func() {
|
||||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
||||||
UserId: 1,
|
UserId: 1,
|
||||||
OrgId: testOrgId,
|
OrgId: testOrgID,
|
||||||
TeamId: group1.Result.Id,
|
TeamId: team1.Id,
|
||||||
Permission: models.PERMISSION_ADMIN,
|
Permission: models.PERMISSION_ADMIN,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -184,24 +179,24 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to search for teams", func() {
|
Convey("Should be able to search for teams", func() {
|
||||||
query := &models.SearchTeamsQuery{OrgId: testOrgId, Query: "group", Page: 1}
|
query := &models.SearchTeamsQuery{OrgId: testOrgID, Query: "group", Page: 1}
|
||||||
err = SearchTeams(query)
|
err = SearchTeams(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(query.Result.Teams), ShouldEqual, 2)
|
So(len(query.Result.Teams), ShouldEqual, 2)
|
||||||
So(query.Result.TotalCount, ShouldEqual, 2)
|
So(query.Result.TotalCount, ShouldEqual, 2)
|
||||||
|
|
||||||
query2 := &models.SearchTeamsQuery{OrgId: testOrgId, Query: ""}
|
query2 := &models.SearchTeamsQuery{OrgId: testOrgID, Query: ""}
|
||||||
err = SearchTeams(query2)
|
err = SearchTeams(query2)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(query2.Result.Teams), ShouldEqual, 2)
|
So(len(query2.Result.Teams), ShouldEqual, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to return all teams a user is member of", func() {
|
Convey("Should be able to return all teams a user is member of", func() {
|
||||||
groupId := group2.Result.Id
|
groupId := team2.Id
|
||||||
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
|
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
query := &models.GetTeamsByUserQuery{OrgId: testOrgId, UserId: userIds[0]}
|
query := &models.GetTeamsByUserQuery{OrgId: testOrgID, UserId: userIds[0]}
|
||||||
err = GetTeamsByUser(query)
|
err = GetTeamsByUser(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(query.Result), ShouldEqual, 1)
|
So(len(query.Result), ShouldEqual, 1)
|
||||||
@@ -210,63 +205,65 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to remove users from a group", func() {
|
Convey("Should be able to remove users from a group", func() {
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]})
|
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]})
|
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0]})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: group1.Result.Id}
|
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id}
|
||||||
err = GetTeamMembers(q2)
|
err = GetTeamMembers(q2)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(q2.Result), ShouldEqual, 0)
|
So(len(q2.Result), ShouldEqual, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When ProtectLastAdmin is set to true", func() {
|
Convey("When ProtectLastAdmin is set to true", func() {
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: models.PERMISSION_ADMIN})
|
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("A user should not be able to remove the last admin", func() {
|
Convey("A user should not be able to remove the last admin", func() {
|
||||||
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
|
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], ProtectLastAdmin: true})
|
||||||
So(err, ShouldEqual, models.ErrLastTeamAdmin)
|
So(err, ShouldEqual, models.ErrLastTeamAdmin)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("A user should be able to remove an admin if there are other admins", func() {
|
Convey("A user should be able to remove an admin if there are other admins", func() {
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
|
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
|
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], ProtectLastAdmin: true})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("A user should not be able to remove the admin permission for the last admin", func() {
|
Convey("A user should not be able to remove the admin permission for the last admin", func() {
|
||||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
|
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
|
||||||
So(err, ShouldBeError, models.ErrLastTeamAdmin)
|
So(err, ShouldBeError, models.ErrLastTeamAdmin)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("A user should be able to remove the admin permission if there are other admins", func() {
|
Convey("A user should be able to remove the admin permission if there are other admins", func() {
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
|
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
|
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to remove a group with users and permissions", func() {
|
Convey("Should be able to remove a group with users and permissions", func() {
|
||||||
groupId := group2.Result.Id
|
groupId := team2.Id
|
||||||
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]})
|
err := sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[2]})
|
err = sqlStore.AddTeamMember(userIds[2], testOrgID, groupId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = testHelperUpdateDashboardAcl(1, models.DashboardAcl{DashboardID: 1, OrgID: testOrgId, Permission: models.PERMISSION_EDIT, TeamID: groupId})
|
err = testHelperUpdateDashboardAcl(t, sqlStore, 1, models.DashboardAcl{
|
||||||
|
DashboardID: 1, OrgID: testOrgID, Permission: models.PERMISSION_EDIT, TeamID: groupId,
|
||||||
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgId, Id: groupId})
|
err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgID, Id: groupId})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
query := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: groupId}
|
query := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: groupId}
|
||||||
err = GetTeamById(query)
|
err = GetTeamById(query)
|
||||||
So(err, ShouldEqual, models.ErrTeamNotFound)
|
So(err, ShouldEqual, models.ErrTeamNotFound)
|
||||||
|
|
||||||
permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: testOrgId}
|
permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: testOrgID}
|
||||||
err = GetDashboardAclInfoList(permQuery)
|
err = GetDashboardAclInfoList(permQuery)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
@@ -274,18 +271,18 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should be able to return if user is admin of teams or not", func() {
|
Convey("Should be able to return if user is admin of teams or not", func() {
|
||||||
groupId := group2.Result.Id
|
groupId := team2.Id
|
||||||
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
|
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
|
err = sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, models.PERMISSION_ADMIN)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
query := &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgId, UserId: userIds[0]}}
|
query := &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgID, UserId: userIds[0]}}
|
||||||
err = IsAdminOfTeams(query)
|
err = IsAdminOfTeams(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeFalse)
|
So(query.Result, ShouldBeFalse)
|
||||||
|
|
||||||
query = &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgId, UserId: userIds[1]}}
|
query = &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgID, UserId: userIds[1]}}
|
||||||
err = IsAdminOfTeams(query)
|
err = IsAdminOfTeams(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeTrue)
|
So(query.Result, ShouldBeTrue)
|
||||||
@@ -295,29 +292,29 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
|||||||
signedInUser := &models.SignedInUser{Login: "loginuser0"}
|
signedInUser := &models.SignedInUser{Login: "loginuser0"}
|
||||||
hiddenUsers := map[string]struct{}{"loginuser0": {}, "loginuser1": {}}
|
hiddenUsers := map[string]struct{}{"loginuser0": {}, "loginuser1": {}}
|
||||||
|
|
||||||
teamId := group1.Result.Id
|
teamId := team1.Id
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[0]})
|
err = sqlStore.AddTeamMember(userIds[0], testOrgID, teamId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[1]})
|
err = sqlStore.AddTeamMember(userIds[1], testOrgID, teamId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[2]})
|
err = sqlStore.AddTeamMember(userIds[2], testOrgID, teamId, false, 0)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
searchQuery := &models.SearchTeamsQuery{OrgId: testOrgId, Page: 1, Limit: 10, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
searchQuery := &models.SearchTeamsQuery{OrgId: testOrgID, Page: 1, Limit: 10, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||||
err = SearchTeams(searchQuery)
|
err = SearchTeams(searchQuery)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(searchQuery.Result.Teams, ShouldHaveLength, 2)
|
So(searchQuery.Result.Teams, ShouldHaveLength, 2)
|
||||||
team1 := searchQuery.Result.Teams[0]
|
team1 := searchQuery.Result.Teams[0]
|
||||||
So(team1.MemberCount, ShouldEqual, 2)
|
So(team1.MemberCount, ShouldEqual, 2)
|
||||||
|
|
||||||
searchQueryFilteredByUser := &models.SearchTeamsQuery{OrgId: testOrgId, Page: 1, Limit: 10, UserIdFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
searchQueryFilteredByUser := &models.SearchTeamsQuery{OrgId: testOrgID, Page: 1, Limit: 10, UserIdFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||||
err = SearchTeams(searchQueryFilteredByUser)
|
err = SearchTeams(searchQueryFilteredByUser)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(searchQueryFilteredByUser.Result.Teams, ShouldHaveLength, 1)
|
So(searchQueryFilteredByUser.Result.Teams, ShouldHaveLength, 1)
|
||||||
team1 = searchQuery.Result.Teams[0]
|
team1 = searchQuery.Result.Teams[0]
|
||||||
So(team1.MemberCount, ShouldEqual, 2)
|
So(team1.MemberCount, ShouldEqual, 2)
|
||||||
|
|
||||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||||
err = GetTeamById(getTeamQuery)
|
err = GetTeamById(getTeamQuery)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(getTeamQuery.Result.MemberCount, ShouldEqual, 2)
|
So(getTeamQuery.Result.MemberCount, ShouldEqual, 2)
|
||||||
|
|||||||
@@ -31,12 +31,10 @@ func (ss *SQLStore) addUserQueryAndCommandHandlers() {
|
|||||||
bus.AddHandler("sql", DisableUser)
|
bus.AddHandler("sql", DisableUser)
|
||||||
bus.AddHandler("sql", BatchDisableUsers)
|
bus.AddHandler("sql", BatchDisableUsers)
|
||||||
bus.AddHandler("sql", DeleteUser)
|
bus.AddHandler("sql", DeleteUser)
|
||||||
bus.AddHandler("sql", UpdateUserPermissions)
|
|
||||||
bus.AddHandler("sql", SetUserHelpFlag)
|
bus.AddHandler("sql", SetUserHelpFlag)
|
||||||
bus.AddHandlerCtx("sql", CreateUser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrgIdForNewUser(sess *DBSession, cmd *models.CreateUserCommand) (int64, error) {
|
func getOrgIdForNewUser(sess *DBSession, cmd models.CreateUserCommand) (int64, error) {
|
||||||
if cmd.SkipOrgSetup {
|
if cmd.SkipOrgSetup {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
@@ -191,8 +189,9 @@ func (ss *SQLStore) createUser(ctx context.Context, args userCreationArgs, skipO
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
func (ss *SQLStore) CreateUser(ctx context.Context, cmd models.CreateUserCommand) (*models.User, error) {
|
||||||
return inTransactionCtx(ctx, func(sess *DBSession) error {
|
var user *models.User
|
||||||
|
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||||
orgId, err := getOrgIdForNewUser(sess, cmd)
|
orgId, err := getOrgIdForNewUser(sess, cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -202,13 +201,16 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
|||||||
cmd.Email = cmd.Login
|
cmd.Email = cmd.Login
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, _ := sess.Where("email=? OR login=?", cmd.Email, cmd.Login).Get(&models.User{})
|
exists, err := sess.Where("email=? OR login=?", cmd.Email, cmd.Login).Get(&models.User{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if exists {
|
if exists {
|
||||||
return models.ErrUserAlreadyExists
|
return models.ErrUserAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
// create user
|
// create user
|
||||||
user := models.User{
|
user = &models.User{
|
||||||
Email: cmd.Email,
|
Email: cmd.Email,
|
||||||
Name: cmd.Name,
|
Name: cmd.Name,
|
||||||
Login: cmd.Login,
|
Login: cmd.Login,
|
||||||
@@ -243,7 +245,7 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
|||||||
|
|
||||||
sess.UseBool("is_admin")
|
sess.UseBool("is_admin")
|
||||||
|
|
||||||
if _, err := sess.Insert(&user); err != nil {
|
if _, err := sess.Insert(user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,8 +257,6 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
|||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
})
|
})
|
||||||
|
|
||||||
cmd.Result = user
|
|
||||||
|
|
||||||
// create org user link
|
// create org user link
|
||||||
if !cmd.SkipOrgSetup {
|
if !cmd.SkipOrgSetup {
|
||||||
orgUser := models.OrgUser{
|
orgUser := models.OrgUser{
|
||||||
@@ -282,6 +282,8 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserById(query *models.GetUserByIdQuery) error {
|
func GetUserById(query *models.GetUserByIdQuery) error {
|
||||||
@@ -729,14 +731,14 @@ func deleteUserInTransaction(sess *DBSession, cmd *models.DeleteUserCommand) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateUserPermissions(cmd *models.UpdateUserPermissionsCommand) error {
|
func (ss *SQLStore) UpdateUserPermissions(userID int64, isAdmin bool) error {
|
||||||
return inTransaction(func(sess *DBSession) error {
|
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||||
user := models.User{}
|
var user models.User
|
||||||
if _, err := sess.ID(cmd.UserId).Get(&user); err != nil {
|
if _, err := sess.ID(userID).Get(&user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.IsAdmin = cmd.IsGrafanaAdmin
|
user.IsAdmin = isAdmin
|
||||||
sess.UseBool("is_admin")
|
sess.UseBool("is_admin")
|
||||||
|
|
||||||
_, err := sess.ID(user.Id).Update(&user)
|
_, err := sess.ID(user.Id).Update(&user)
|
||||||
|
|||||||
@@ -15,18 +15,16 @@ import (
|
|||||||
|
|
||||||
//nolint:goconst
|
//nolint:goconst
|
||||||
func TestUserAuth(t *testing.T) {
|
func TestUserAuth(t *testing.T) {
|
||||||
InitTestDB(t)
|
sqlStore := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Given 5 users", t, func() {
|
Convey("Given 5 users", t, func() {
|
||||||
var err error
|
|
||||||
var cmd *models.CreateUserCommand
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
cmd = &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
Login: fmt.Sprint("loginuser", i),
|
Login: fmt.Sprint("loginuser", i),
|
||||||
}
|
}
|
||||||
err = CreateUser(context.Background(), cmd)
|
_, err := sqlStore.CreateUser(context.Background(), cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +44,7 @@ func TestUserAuth(t *testing.T) {
|
|||||||
login := "loginuser0"
|
login := "loginuser0"
|
||||||
|
|
||||||
query := &models.GetUserByAuthInfoQuery{Login: login}
|
query := &models.GetUserByAuthInfoQuery{Login: login}
|
||||||
err = GetUserByAuthInfo(query)
|
err := GetUserByAuthInfo(query)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result.Login, ShouldEqual, login)
|
So(query.Result.Login, ShouldEqual, login)
|
||||||
@@ -82,7 +80,7 @@ func TestUserAuth(t *testing.T) {
|
|||||||
Convey("Can set & locate by AuthModule and AuthId", func() {
|
Convey("Can set & locate by AuthModule and AuthId", func() {
|
||||||
// get nonexistent user_auth entry
|
// get nonexistent user_auth entry
|
||||||
query := &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"}
|
query := &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"}
|
||||||
err = GetUserByAuthInfo(query)
|
err := GetUserByAuthInfo(query)
|
||||||
|
|
||||||
So(err, ShouldEqual, models.ErrUserNotFound)
|
So(err, ShouldEqual, models.ErrUserNotFound)
|
||||||
So(query.Result, ShouldBeNil)
|
So(query.Result, ShouldBeNil)
|
||||||
@@ -144,7 +142,7 @@ func TestUserAuth(t *testing.T) {
|
|||||||
|
|
||||||
// Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table
|
// Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table
|
||||||
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test", AuthId: "test"}
|
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test", AuthId: "test"}
|
||||||
err = GetUserByAuthInfo(query)
|
err := GetUserByAuthInfo(query)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result.Login, ShouldEqual, login)
|
So(query.Result.Login, ShouldEqual, login)
|
||||||
@@ -179,7 +177,7 @@ func TestUserAuth(t *testing.T) {
|
|||||||
// Make the first log-in during the past
|
// Make the first log-in during the past
|
||||||
getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) }
|
getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) }
|
||||||
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test1", AuthId: "test1"}
|
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test1", AuthId: "test1"}
|
||||||
err = GetUserByAuthInfo(query)
|
err := GetUserByAuthInfo(query)
|
||||||
getTime = time.Now
|
getTime = time.Now
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|||||||
@@ -20,17 +20,17 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
ss := InitTestDB(t)
|
ss := InitTestDB(t)
|
||||||
|
|
||||||
Convey("Creates a user", func() {
|
Convey("Creates a user", func() {
|
||||||
cmd := &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: "usertest@test.com",
|
Email: "usertest@test.com",
|
||||||
Name: "user name",
|
Name: "user name",
|
||||||
Login: "user_test_login",
|
Login: "user_test_login",
|
||||||
}
|
}
|
||||||
|
|
||||||
err := CreateUser(context.Background(), cmd)
|
user, err := ss.CreateUser(context.Background(), cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Loading a user", func() {
|
Convey("Loading a user", func() {
|
||||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
query := models.GetUserByIdQuery{Id: user.Id}
|
||||||
err := GetUserById(&query)
|
err := GetUserById(&query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
@@ -43,18 +43,18 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Creates disabled user", func() {
|
Convey("Creates disabled user", func() {
|
||||||
cmd := &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: "usertest@test.com",
|
Email: "usertest@test.com",
|
||||||
Name: "user name",
|
Name: "user name",
|
||||||
Login: "user_test_login",
|
Login: "user_test_login",
|
||||||
IsDisabled: true,
|
IsDisabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := CreateUser(context.Background(), cmd)
|
user, err := ss.CreateUser(context.Background(), cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Loading a user", func() {
|
Convey("Loading a user", func() {
|
||||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
query := models.GetUserByIdQuery{Id: user.Id}
|
||||||
err := GetUserById(&query)
|
err := GetUserById(&query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
@@ -78,18 +78,18 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Creates user assigned to other organization", func() {
|
Convey("Creates user assigned to other organization", func() {
|
||||||
cmd := &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: "usertest@test.com",
|
Email: "usertest@test.com",
|
||||||
Name: "user name",
|
Name: "user name",
|
||||||
Login: "user_test_login",
|
Login: "user_test_login",
|
||||||
OrgId: orgCmd.Result.Id,
|
OrgId: orgCmd.Result.Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := CreateUser(context.Background(), cmd)
|
user, err := ss.CreateUser(context.Background(), cmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Loading a user", func() {
|
Convey("Loading a user", func() {
|
||||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
query := models.GetUserByIdQuery{Id: user.Id}
|
||||||
err := GetUserById(&query)
|
err := GetUserById(&query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
@@ -104,20 +104,20 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Don't create user assigned to unknown organization", func() {
|
Convey("Don't create user assigned to unknown organization", func() {
|
||||||
const nonExistingOrgID = 10000
|
const nonExistingOrgID = 10000
|
||||||
cmd := &models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Email: "usertest@test.com",
|
Email: "usertest@test.com",
|
||||||
Name: "user name",
|
Name: "user name",
|
||||||
Login: "user_test_login",
|
Login: "user_test_login",
|
||||||
OrgId: nonExistingOrgID,
|
OrgId: nonExistingOrgID,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := CreateUser(context.Background(), cmd)
|
_, err := ss.CreateUser(context.Background(), cmd)
|
||||||
So(err, ShouldEqual, models.ErrOrgNotFound)
|
So(err, ShouldEqual, models.ErrOrgNotFound)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given 5 users", func() {
|
Convey("Given 5 users", func() {
|
||||||
users := createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||||
return &models.CreateUserCommand{
|
return &models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
@@ -238,7 +238,7 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Can return list users based on their is_disabled flag", func() {
|
Convey("Can return list users based on their is_disabled flag", func() {
|
||||||
ss = InitTestDB(t)
|
ss = InitTestDB(t)
|
||||||
createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||||
return &models.CreateUserCommand{
|
return &models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
@@ -269,7 +269,7 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
So(third, ShouldBeTrue)
|
So(third, ShouldBeTrue)
|
||||||
|
|
||||||
ss = InitTestDB(t)
|
ss = InitTestDB(t)
|
||||||
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||||
return &models.CreateUserCommand{
|
return &models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
@@ -286,7 +286,7 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = testHelperUpdateDashboardAcl(1, models.DashboardAcl{
|
err = testHelperUpdateDashboardAcl(t, ss, 1, models.DashboardAcl{
|
||||||
DashboardID: 1, OrgID: users[0].OrgId, UserID: users[1].Id,
|
DashboardID: 1, OrgID: users[0].OrgId, UserID: users[1].Id,
|
||||||
Permission: models.PERMISSION_EDIT,
|
Permission: models.PERMISSION_EDIT,
|
||||||
})
|
})
|
||||||
@@ -365,7 +365,7 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Should enable all users", func() {
|
Convey("Should enable all users", func() {
|
||||||
ss = InitTestDB(t)
|
ss = InitTestDB(t)
|
||||||
createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||||
return &models.CreateUserCommand{
|
return &models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
@@ -392,7 +392,7 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Should disable only specific users", func() {
|
Convey("Should disable only specific users", func() {
|
||||||
ss = InitTestDB(t)
|
ss = InitTestDB(t)
|
||||||
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||||
return &models.CreateUserCommand{
|
return &models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
@@ -438,7 +438,7 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
// Since previous tests were destructive
|
// Since previous tests were destructive
|
||||||
ss = InitTestDB(t)
|
ss = InitTestDB(t)
|
||||||
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||||
return &models.CreateUserCommand{
|
return &models.CreateUserCommand{
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
Email: fmt.Sprint("user", i, "@test.com"),
|
||||||
Name: fmt.Sprint("user", i),
|
Name: fmt.Sprint("user", i),
|
||||||
@@ -546,25 +546,22 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Given one grafana admin user", func() {
|
Convey("Given one grafana admin user", func() {
|
||||||
var err error
|
createUserCmd := models.CreateUserCommand{
|
||||||
createUserCmd := &models.CreateUserCommand{
|
|
||||||
Email: fmt.Sprint("admin", "@test.com"),
|
Email: fmt.Sprint("admin", "@test.com"),
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Login: "admin",
|
Login: "admin",
|
||||||
IsAdmin: true,
|
IsAdmin: true,
|
||||||
}
|
}
|
||||||
err = CreateUser(context.Background(), createUserCmd)
|
user, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("Cannot make themselves a non-admin", func() {
|
Convey("Cannot make themselves a non-admin", func() {
|
||||||
updateUserPermsCmd := models.UpdateUserPermissionsCommand{IsGrafanaAdmin: false, UserId: 1}
|
updatePermsError := ss.UpdateUserPermissions(1, false)
|
||||||
updatePermsError := UpdateUserPermissions(&updateUserPermsCmd)
|
|
||||||
|
|
||||||
So(updatePermsError, ShouldEqual, models.ErrLastGrafanaAdmin)
|
So(updatePermsError, ShouldEqual, models.ErrLastGrafanaAdmin)
|
||||||
|
|
||||||
query := models.GetUserByIdQuery{Id: createUserCmd.Result.Id}
|
query := models.GetUserByIdQuery{Id: user.Id}
|
||||||
getUserError := GetUserById(&query)
|
getUserError := GetUserById(&query)
|
||||||
|
|
||||||
So(getUserError, ShouldBeNil)
|
So(getUserError, ShouldBeNil)
|
||||||
|
|
||||||
So(query.Result.IsAdmin, ShouldEqual, true)
|
So(query.Result.IsAdmin, ShouldEqual, true)
|
||||||
@@ -574,33 +571,33 @@ func TestUserDataAccess(t *testing.T) {
|
|||||||
Convey("Given one user", func() {
|
Convey("Given one user", func() {
|
||||||
const email = "user@test.com"
|
const email = "user@test.com"
|
||||||
const username = "user"
|
const username = "user"
|
||||||
createUserCmd := &models.CreateUserCommand{
|
createUserCmd := models.CreateUserCommand{
|
||||||
Email: email,
|
Email: email,
|
||||||
Name: "user",
|
Name: "user",
|
||||||
Login: username,
|
Login: username,
|
||||||
}
|
}
|
||||||
err := CreateUser(context.Background(), createUserCmd)
|
_, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
Convey("When trying to create a new user with the same email, an error is returned", func() {
|
Convey("When trying to create a new user with the same email, an error is returned", func() {
|
||||||
createUserCmd := &models.CreateUserCommand{
|
createUserCmd := models.CreateUserCommand{
|
||||||
Email: email,
|
Email: email,
|
||||||
Name: "user2",
|
Name: "user2",
|
||||||
Login: "user2",
|
Login: "user2",
|
||||||
SkipOrgSetup: true,
|
SkipOrgSetup: true,
|
||||||
}
|
}
|
||||||
err := CreateUser(context.Background(), createUserCmd)
|
_, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||||
So(err, ShouldEqual, models.ErrUserAlreadyExists)
|
So(err, ShouldEqual, models.ErrUserAlreadyExists)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When trying to create a new user with the same login, an error is returned", func() {
|
Convey("When trying to create a new user with the same login, an error is returned", func() {
|
||||||
createUserCmd := &models.CreateUserCommand{
|
createUserCmd := models.CreateUserCommand{
|
||||||
Email: "user2@test.com",
|
Email: "user2@test.com",
|
||||||
Name: "user2",
|
Name: "user2",
|
||||||
Login: username,
|
Login: username,
|
||||||
SkipOrgSetup: true,
|
SkipOrgSetup: true,
|
||||||
}
|
}
|
||||||
err := CreateUser(context.Background(), createUserCmd)
|
_, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||||
So(err, ShouldEqual, models.ErrUserAlreadyExists)
|
So(err, ShouldEqual, models.ErrUserAlreadyExists)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -618,15 +615,15 @@ func GetOrgUsersForTest(query *models.GetOrgUsersQuery) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFiveTestUsers(fn func(i int) *models.CreateUserCommand) []models.User {
|
func createFiveTestUsers(t *testing.T, sqlStore *SQLStore, fn func(i int) *models.CreateUserCommand) []models.User {
|
||||||
var err error
|
t.Helper()
|
||||||
var cmd *models.CreateUserCommand
|
|
||||||
users := []models.User{}
|
users := []models.User{}
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
cmd = fn(i)
|
cmd := fn(i)
|
||||||
|
|
||||||
err = CreateUser(context.Background(), cmd)
|
user, err := sqlStore.CreateUser(context.Background(), *cmd)
|
||||||
users = append(users, cmd.Result)
|
users = append(users, *user)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
@@ -27,8 +26,9 @@ import (
|
|||||||
|
|
||||||
// ApplicationInsightsDatasource calls the application insights query API.
|
// ApplicationInsightsDatasource calls the application insights query API.
|
||||||
type ApplicationInsightsDatasource struct {
|
type ApplicationInsightsDatasource struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dsInfo *models.DataSource
|
dsInfo *models.DataSource
|
||||||
|
pluginManager plugins.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicationInsightsQuery is the model that holds the information
|
// ApplicationInsightsQuery is the model that holds the information
|
||||||
@@ -210,8 +210,8 @@ func (e *ApplicationInsightsDatasource) executeQuery(ctx context.Context, query
|
|||||||
|
|
||||||
func (e *ApplicationInsightsDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
func (e *ApplicationInsightsDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
return nil, errors.New("unable to find datasource plugin Azure Application Insights")
|
return nil, errors.New("unable to find datasource plugin Azure Application Insights")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
@@ -26,8 +25,9 @@ import (
|
|||||||
|
|
||||||
// AzureLogAnalyticsDatasource calls the Azure Log Analytics API's
|
// AzureLogAnalyticsDatasource calls the Azure Log Analytics API's
|
||||||
type AzureLogAnalyticsDatasource struct {
|
type AzureLogAnalyticsDatasource struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dsInfo *models.DataSource
|
dsInfo *models.DataSource
|
||||||
|
pluginManager plugins.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// AzureLogAnalyticsQuery is the query request that is built from the saved values for
|
// AzureLogAnalyticsQuery is the query request that is built from the saved values for
|
||||||
@@ -217,8 +217,8 @@ func (e *AzureLogAnalyticsDatasource) createRequest(ctx context.Context, dsInfo
|
|||||||
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
||||||
|
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
return nil, errors.New("unable to find datasource plugin Azure Monitor")
|
return nil, errors.New("unable to find datasource plugin Azure Monitor")
|
||||||
}
|
}
|
||||||
cloudName := dsInfo.JsonData.Get("cloudName").MustString("azuremonitor")
|
cloudName := dsInfo.JsonData.Get("cloudName").MustString("azuremonitor")
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
@@ -26,8 +25,9 @@ import (
|
|||||||
|
|
||||||
// AzureMonitorDatasource calls the Azure Monitor API - one of the four API's supported
|
// AzureMonitorDatasource calls the Azure Monitor API - one of the four API's supported
|
||||||
type AzureMonitorDatasource struct {
|
type AzureMonitorDatasource struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dsInfo *models.DataSource
|
dsInfo *models.DataSource
|
||||||
|
pluginManager plugins.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -226,8 +226,8 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureM
|
|||||||
|
|
||||||
func (e *AzureMonitorDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
func (e *AzureMonitorDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
return nil, errors.New("unable to find datasource plugin Azure Monitor")
|
return nil, errors.New("unable to find datasource plugin Azure Monitor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
PluginManager plugins.Manager `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Init() error {
|
func (s *Service) Init() error {
|
||||||
@@ -34,8 +35,9 @@ func (s *Service) Init() error {
|
|||||||
|
|
||||||
// AzureMonitorExecutor executes queries for the Azure Monitor datasource - all four services
|
// AzureMonitorExecutor executes queries for the Azure Monitor datasource - all four services
|
||||||
type AzureMonitorExecutor struct {
|
type AzureMonitorExecutor struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dsInfo *models.DataSource
|
dsInfo *models.DataSource
|
||||||
|
pluginManager plugins.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAzureMonitorExecutor initializes a http client
|
// NewAzureMonitorExecutor initializes a http client
|
||||||
@@ -46,8 +48,9 @@ func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &AzureMonitorExecutor{
|
return &AzureMonitorExecutor{
|
||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
dsInfo: dsInfo,
|
dsInfo: dsInfo,
|
||||||
|
pluginManager: s.PluginManager,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,23 +85,27 @@ func (e *AzureMonitorExecutor) DataQuery(ctx context.Context, dsInfo *models.Dat
|
|||||||
}
|
}
|
||||||
|
|
||||||
azDatasource := &AzureMonitorDatasource{
|
azDatasource := &AzureMonitorDatasource{
|
||||||
httpClient: e.httpClient,
|
httpClient: e.httpClient,
|
||||||
dsInfo: e.dsInfo,
|
dsInfo: e.dsInfo,
|
||||||
|
pluginManager: e.pluginManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
aiDatasource := &ApplicationInsightsDatasource{
|
aiDatasource := &ApplicationInsightsDatasource{
|
||||||
httpClient: e.httpClient,
|
httpClient: e.httpClient,
|
||||||
dsInfo: e.dsInfo,
|
dsInfo: e.dsInfo,
|
||||||
|
pluginManager: e.pluginManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
alaDatasource := &AzureLogAnalyticsDatasource{
|
alaDatasource := &AzureLogAnalyticsDatasource{
|
||||||
httpClient: e.httpClient,
|
httpClient: e.httpClient,
|
||||||
dsInfo: e.dsInfo,
|
dsInfo: e.dsInfo,
|
||||||
|
pluginManager: e.pluginManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
iaDatasource := &InsightsAnalyticsDatasource{
|
iaDatasource := &InsightsAnalyticsDatasource{
|
||||||
httpClient: e.httpClient,
|
httpClient: e.httpClient,
|
||||||
dsInfo: e.dsInfo,
|
dsInfo: e.dsInfo,
|
||||||
|
pluginManager: e.pluginManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
azResult, err := azDatasource.executeTimeSeriesQuery(ctx, azureMonitorQueries, *tsdbQuery.TimeRange)
|
azResult, err := azDatasource.executeTimeSeriesQuery(ctx, azureMonitorQueries, *tsdbQuery.TimeRange)
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
@@ -23,8 +22,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type InsightsAnalyticsDatasource struct {
|
type InsightsAnalyticsDatasource struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dsInfo *models.DataSource
|
dsInfo *models.DataSource
|
||||||
|
pluginManager plugins.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
type InsightsAnalyticsQuery struct {
|
type InsightsAnalyticsQuery struct {
|
||||||
@@ -187,8 +187,8 @@ func (e *InsightsAnalyticsDatasource) executeQuery(ctx context.Context, query *I
|
|||||||
|
|
||||||
func (e *InsightsAnalyticsDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
func (e *InsightsAnalyticsDatasource) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
return nil, errors.New("unable to find datasource plugin Azure Application Insights")
|
return nil, errors.New("unable to find datasource plugin Azure Application Insights")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
@@ -73,6 +72,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
PluginManager plugins.Manager `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Init() error {
|
func (s *Service) Init() error {
|
||||||
@@ -81,8 +81,9 @@ func (s *Service) Init() error {
|
|||||||
|
|
||||||
// Executor executes queries for the CloudMonitoring datasource.
|
// Executor executes queries for the CloudMonitoring datasource.
|
||||||
type Executor struct {
|
type Executor struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dsInfo *models.DataSource
|
dsInfo *models.DataSource
|
||||||
|
pluginManager plugins.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExecutor returns an Executor.
|
// NewExecutor returns an Executor.
|
||||||
@@ -93,8 +94,9 @@ func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Executor{
|
return &Executor{
|
||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
dsInfo: dsInfo,
|
dsInfo: dsInfo,
|
||||||
|
pluginManager: s.PluginManager,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,8 +536,8 @@ func (e *Executor) createRequest(ctx context.Context, dsInfo *models.DataSource,
|
|||||||
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
||||||
|
|
||||||
// find plugin
|
// find plugin
|
||||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||||
if !ok {
|
if plugin == nil {
|
||||||
return nil, errors.New("unable to find datasource plugin CloudMonitoring")
|
return nil, errors.New("unable to find datasource plugin CloudMonitoring")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||||
@@ -46,7 +45,7 @@ type Service struct {
|
|||||||
PostgresService *postgres.PostgresService `inject:""`
|
PostgresService *postgres.PostgresService `inject:""`
|
||||||
CloudMonitoringService *cloudmonitoring.Service `inject:""`
|
CloudMonitoringService *cloudmonitoring.Service `inject:""`
|
||||||
AzureMonitorService *azuremonitor.Service `inject:""`
|
AzureMonitorService *azuremonitor.Service `inject:""`
|
||||||
PluginManager *manager.PluginManager `inject:""`
|
PluginManager plugins.Manager `inject:""`
|
||||||
|
|
||||||
registry map[string]func(*models.DataSource) (plugins.DataPlugin, error)
|
registry map[string]func(*models.DataSource) (plugins.DataPlugin, error)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user