mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
10
pkg/api/acl.go
Normal file
10
pkg/api/acl.go
Normal file
@@ -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)
|
||||
}
|
||||
@@ -266,7 +266,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
apiRoute.Get("/datasources/id/:name", routing.Wrap(GetDataSourceIdByName), reqSignedIn)
|
||||
|
||||
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/health", routing.Wrap(hs.CheckHealth))
|
||||
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
|
||||
@@ -288,13 +288,13 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
|
||||
// Folders
|
||||
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
|
||||
folderRoute.Get("/", routing.Wrap(GetFolders))
|
||||
folderRoute.Get("/id/:id", routing.Wrap(GetFolderByID))
|
||||
folderRoute.Get("/", routing.Wrap(hs.GetFolders))
|
||||
folderRoute.Get("/id/:id", routing.Wrap(hs.GetFolderByID))
|
||||
folderRoute.Post("/", bind(models.CreateFolderCommand{}), routing.Wrap(hs.CreateFolder))
|
||||
|
||||
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
|
||||
folderUidRoute.Get("/", routing.Wrap(GetFolderByUID))
|
||||
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), routing.Wrap(UpdateFolder))
|
||||
folderUidRoute.Get("/", routing.Wrap(hs.GetFolderByUID))
|
||||
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), routing.Wrap(hs.UpdateFolder))
|
||||
folderUidRoute.Delete("/", routing.Wrap(hs.DeleteFolder))
|
||||
|
||||
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"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/dashboards"
|
||||
|
||||
@@ -122,7 +121,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
|
||||
meta.FolderUrl = query.Result.GetUrl()
|
||||
}
|
||||
|
||||
svc := dashboards.NewProvisioningService()
|
||||
svc := dashboards.NewProvisioningService(hs.SQLStore)
|
||||
provisioningData, err := svc.GetProvisionedDashboardDataByDashboardID(dash.Id)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
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,
|
||||
}
|
||||
|
||||
dashSvc := dashboards.NewService()
|
||||
dashSvc := dashboards.NewService(hs.SQLStore)
|
||||
dashboard, err := dashSvc.SaveDashboard(dashItem, allowUiUpdate)
|
||||
if err != nil {
|
||||
return dashboardSaveErrorToApiResponse(err)
|
||||
return hs.dashboardSaveErrorToApiResponse(err)
|
||||
}
|
||||
|
||||
if hs.Cfg.EditorsCanAdmin && newDashboard {
|
||||
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 {
|
||||
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
|
||||
if ok := errors.As(err, &dashboardErr); ok {
|
||||
if body := dashboardErr.Body(); body != nil {
|
||||
@@ -360,7 +359,7 @@ func dashboardSaveErrorToApiResponse(err error) response.Response {
|
||||
if ok := errors.As(err, &pluginErr); ok {
|
||||
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
||||
// 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)
|
||||
}
|
||||
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/response"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
)
|
||||
@@ -68,11 +67,9 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dt
|
||||
return dashboardGuardianResponse(err)
|
||||
}
|
||||
|
||||
cmd := models.UpdateDashboardAclCommand{}
|
||||
cmd.DashboardID = dashID
|
||||
|
||||
var items []*models.DashboardAcl
|
||||
for _, item := range apiCmd.Items {
|
||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
||||
items = append(items, &models.DashboardAcl{
|
||||
OrgID: c.OrgId,
|
||||
DashboardID: dashID,
|
||||
UserID: item.UserID,
|
||||
@@ -88,9 +85,9 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dt
|
||||
if err != nil {
|
||||
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 errors.Is(err, guardian.ErrGuardianPermissionExists) || errors.Is(err, guardian.ErrGuardianOverride) {
|
||||
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)
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err := updateDashboardACL(hs, dashID, items); err != nil {
|
||||
if errors.Is(err, models.ErrDashboardAclInfoMissing) ||
|
||||
errors.Is(err, models.ErrDashboardPermissionDashboardEmpty) {
|
||||
return response.Error(409, err.Error(), err)
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
callUpdateDashboardPermissions(t, sc)
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -91,7 +91,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
callUpdateDashboardPermissions(t, sc)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -151,7 +151,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
callUpdateDashboardPermissions(t, sc)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -189,7 +189,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
callUpdateDashboardPermissions(t, sc)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -217,7 +217,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/dashboards/id/:id/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateDashboardPermissions(sc)
|
||||
callUpdateDashboardPermissions(t, sc)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
@@ -260,7 +260,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
callUpdateDashboardPermissions(t, sc)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -335,13 +335,20 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
setUp()
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
||||
assert.Len(t, cmd.Items, 4)
|
||||
return nil
|
||||
// TODO: Replace this fake with a fake SQLStore instead (once we can use an interface in its stead)
|
||||
origUpdateDashboardACL := updateDashboardACL
|
||||
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()
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
assert.Len(t, gotItems, 4)
|
||||
},
|
||||
}, hs)
|
||||
})
|
||||
@@ -353,10 +360,16 @@ func callGetDashboardPermissions(sc *scenarioContext, hs *HTTPServer) {
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||
}
|
||||
|
||||
func callUpdateDashboardPermissions(sc *scenarioContext) {
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
||||
return nil
|
||||
func callUpdateDashboardPermissions(t *testing.T, sc *scenarioContext) {
|
||||
t.Helper()
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@@ -28,7 +29,10 @@ func TestGetHomeDashboard(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
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 {
|
||||
query.Result = &models.Preferences{
|
||||
HomeDashboardId: 0,
|
||||
@@ -102,11 +106,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
query.Result = nil
|
||||
return nil
|
||||
})
|
||||
|
||||
viewerRole := models.ROLE_VIEWER
|
||||
editorRole := models.ROLE_EDITOR
|
||||
|
||||
@@ -165,7 +164,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
|
||||
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) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
|
||||
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) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
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) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||
})
|
||||
@@ -281,11 +280,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
})
|
||||
setting.ViewersCanEdit = false
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
query.Result = nil
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetDashboardsBySlugQuery) error {
|
||||
dashboards := []*models.Dashboard{fakeDash}
|
||||
query.Result = dashboards
|
||||
@@ -354,7 +348,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, hs)
|
||||
callDeleteDashboardBySlug(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardByUID(sc, hs)
|
||||
callDeleteDashboardByUID(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, hs)
|
||||
callDeleteDashboardBySlug(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUp()
|
||||
|
||||
CallDeleteDashboardByUID(sc, hs)
|
||||
callDeleteDashboardByUID(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, hs)
|
||||
callDeleteDashboardBySlug(sc, hs)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardByUID(sc, hs)
|
||||
callDeleteDashboardByUID(sc, hs)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, hs)
|
||||
callDeleteDashboardBySlug(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardByUID(sc, hs)
|
||||
callDeleteDashboardByUID(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, hs)
|
||||
callDeleteDashboardBySlug(sc, hs)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardByUID(sc, hs)
|
||||
callDeleteDashboardByUID(sc, hs)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardBySlug(sc, hs)
|
||||
callDeleteDashboardBySlug(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
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) {
|
||||
state := setUpInner()
|
||||
|
||||
CallDeleteDashboardByUID(sc, hs)
|
||||
callDeleteDashboardByUID(sc, hs)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
|
||||
})
|
||||
@@ -730,11 +724,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
dashTwo.FolderId = 3
|
||||
dashTwo.HasAcl = false
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
query.Result = nil
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetDashboardsBySlugQuery) error {
|
||||
dashboards := []*models.Dashboard{dashOne, dashTwo}
|
||||
query.Result = dashboards
|
||||
@@ -743,14 +732,15 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
|
||||
role := models.ROLE_EDITOR
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||
CallDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash",
|
||||
"/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||
callDeleteDashboardBySlug(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
|
||||
assert.Equal(t, 412, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
assert.Equal(t, "multiple-slugs-exists", result.Get("status").MustString())
|
||||
assert.Equal(t, models.ErrDashboardsWithSameSlugExists.Error(), result.Get("message").MustString())
|
||||
})
|
||||
assert.Equal(t, 412, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
assert.Equal(t, "multiple-slugs-exists", result.Get("status").MustString())
|
||||
assert.Equal(t, models.ErrDashboardsWithSameSlugExists.Error(), result.Get("message").MustString())
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Post dashboard response tests", func(t *testing.T) {
|
||||
@@ -856,11 +846,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
query.Result = nil
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetDashboardVersionQuery) error {
|
||||
query.Result = &models.DashboardVersion{
|
||||
Data: simplejson.NewFromAny(map[string]interface{}{
|
||||
@@ -1017,10 +1002,13 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
query.Result = &models.DashboardProvisioning{ExternalId: "/tmp/grafana/dashboards/test/dashboard1.json"}
|
||||
return nil
|
||||
origGetProvisionedData := dashboards.GetProvisionedData
|
||||
t.Cleanup(func() {
|
||||
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 {
|
||||
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) {
|
||||
setUp()
|
||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/dash",
|
||||
"/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)
|
||||
result := sc.ToJSON()
|
||||
assert.Equal(t, models.ErrDashboardCannotDeleteProvisionedDashboard.Error(), result.Get("error").MustString())
|
||||
})
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
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) {
|
||||
setUp()
|
||||
|
||||
CallDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
callDeleteDashboardByUID(sc, &HTTPServer{Cfg: setting.NewCfg()})
|
||||
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
@@ -1141,7 +1130,7 @@ func callGetDashboardVersions(sc *scenarioContext) {
|
||||
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 {
|
||||
return nil
|
||||
})
|
||||
@@ -1150,7 +1139,7 @@ func CallDeleteDashboardBySlug(sc *scenarioContext, hs *HTTPServer) {
|
||||
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 {
|
||||
return nil
|
||||
})
|
||||
@@ -1187,6 +1176,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
||||
QuotaService: "a.QuotaService{
|
||||
Cfg: cfg,
|
||||
},
|
||||
PluginManager: &fakePluginManager{},
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, url)
|
||||
@@ -1204,7 +1194,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
||||
dashboards.NewProvisioningService = origProvisioningService
|
||||
})
|
||||
dashboards.MockDashboardService(mock)
|
||||
dashboards.NewProvisioningService = func() dashboards.DashboardProvisioningService {
|
||||
dashboards.NewProvisioningService = func(dboards.Store) dashboards.DashboardProvisioningService {
|
||||
return mockDashboardProvisioningService{}
|
||||
}
|
||||
|
||||
@@ -1268,7 +1258,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
||||
dashboards.NewService = origNewDashboardService
|
||||
dashboards.NewProvisioningService = origProvisioningService
|
||||
})
|
||||
dashboards.NewProvisioningService = func() dashboards.DashboardProvisioningService {
|
||||
dashboards.NewProvisioningService = func(dboards.Store) dashboards.DashboardProvisioningService {
|
||||
return mockDashboardProvisioningService{}
|
||||
}
|
||||
dashboards.MockDashboardService(mock)
|
||||
@@ -1290,6 +1280,7 @@ type mockDashboardProvisioningService struct {
|
||||
dashboards.DashboardProvisioningService
|
||||
}
|
||||
|
||||
func (m mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(dashboardId int64) (*models.DashboardProvisioning, error) {
|
||||
return &models.DashboardProvisioning{}, nil
|
||||
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(dashboardID int64) (
|
||||
*models.DashboardProvisioning, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
)
|
||||
|
||||
// ProxyDataSourceRequest proxies datasource requests
|
||||
@@ -35,8 +34,8 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) {
|
||||
}
|
||||
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[ds.Type]
|
||||
if !ok {
|
||||
plugin := hs.PluginManager.GetDataSource(ds.Type)
|
||||
if plugin == nil {
|
||||
c.JsonApiErr(http.StatusInternalServerError, "Unable to find datasource plugin", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@@ -47,7 +46,7 @@ func (hs *HTTPServer) GetDataSources(c *models.ReqContext) response.Response {
|
||||
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.TypeName = plugin.Name
|
||||
} else {
|
||||
@@ -363,8 +362,8 @@ func (hs *HTTPServer) CallDatasourceResource(c *models.ReqContext) {
|
||||
}
|
||||
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[ds.Type]
|
||||
if !ok {
|
||||
plugin := hs.PluginManager.GetDataSource(ds.Type)
|
||||
if plugin == nil {
|
||||
c.JsonApiErr(500, "Unable to find datasource plugin", err)
|
||||
return
|
||||
}
|
||||
@@ -428,8 +427,8 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Respo
|
||||
return response.Error(500, "Unable to load datasource metadata", err)
|
||||
}
|
||||
|
||||
plugin, ok := hs.PluginManager.GetDatasource(ds.Type)
|
||||
if !ok {
|
||||
plugin := hs.PluginManager.GetDataSource(ds.Type)
|
||||
if plugin == nil {
|
||||
return response.Error(500, "Unable to find datasource plugin", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,11 @@ func TestDataSourcesProxy_userLoggedIn(t *testing.T) {
|
||||
})
|
||||
|
||||
// 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.fakeReq("GET", "/api/datasources").exec()
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package dtos
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
)
|
||||
|
||||
type PluginSetting struct {
|
||||
@@ -64,6 +63,6 @@ type ImportDashboardCommand struct {
|
||||
Path string `json:"path"`
|
||||
Overwrite bool `json:"overwrite"`
|
||||
Dashboard *simplejson.Json `json:"dashboard"`
|
||||
Inputs []manager.ImportDashboardInput `json:"inputs"`
|
||||
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
||||
FolderId int64 `json:"folderId"`
|
||||
}
|
||||
|
||||
19
pkg/api/fakes.go
Normal file
19
pkg/api/fakes.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func GetFolders(c *models.ReqContext) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
||||
func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
folders, err := s.GetFolders(c.QueryInt64("limit"))
|
||||
|
||||
if err != nil {
|
||||
@@ -35,10 +35,9 @@ func GetFolders(c *models.ReqContext) response.Response {
|
||||
return response.JSON(200, result)
|
||||
}
|
||||
|
||||
func GetFolderByUID(c *models.ReqContext) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
||||
func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
folder, err := s.GetFolderByUID(c.Params(":uid"))
|
||||
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
@@ -47,8 +46,8 @@ func GetFolderByUID(c *models.ReqContext) response.Response {
|
||||
return response.JSON(200, toFolderDto(g, folder))
|
||||
}
|
||||
|
||||
func GetFolderByID(c *models.ReqContext) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
||||
func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
folder, err := s.GetFolderByID(c.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
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 {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
||||
err := s.CreateFolder(&cmd)
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
folder, err := s.CreateFolder(cmd.Title, cmd.Uid)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
|
||||
if hs.Cfg.EditorsCanAdmin {
|
||||
if err := dashboards.MakeUserAdmin(hs.Bus, c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil {
|
||||
hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err)
|
||||
if err := s.MakeUserAdmin(c.OrgId, c.SignedInUser.UserId, folder.Id, true); err != nil {
|
||||
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)
|
||||
return response.JSON(200, toFolderDto(g, cmd.Result))
|
||||
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
|
||||
return response.JSON(200, toFolderDto(g, folder))
|
||||
}
|
||||
|
||||
func UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
||||
func (hs *HTTPServer) UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response.Response {
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
err := s.UpdateFolder(c.Params(":uid"), &cmd)
|
||||
if err != nil {
|
||||
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
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
|
||||
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
|
||||
if hs.Cfg.IsPanelLibraryEnabled() {
|
||||
err := hs.LibraryPanelService.DeleteLibraryPanelsInFolder(c, c.Params(":uid"))
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
@@ -14,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
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"))
|
||||
|
||||
if err != nil {
|
||||
@@ -62,9 +61,8 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
||||
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"))
|
||||
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
}
|
||||
@@ -79,11 +77,9 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
||||
return toFolderError(models.ErrFolderAccessDenied)
|
||||
}
|
||||
|
||||
cmd := models.UpdateDashboardAclCommand{}
|
||||
cmd.DashboardID = folder.Id
|
||||
|
||||
var items []*models.DashboardAcl
|
||||
for _, item := range apiCmd.Items {
|
||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
||||
items = append(items, &models.DashboardAcl{
|
||||
OrgID: c.OrgId,
|
||||
DashboardID: folder.Id,
|
||||
UserID: item.UserID,
|
||||
@@ -99,9 +95,9 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
|
||||
if err != nil {
|
||||
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 errors.Is(err, guardian.ErrGuardianPermissionExists) ||
|
||||
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)
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err := updateDashboardACL(hs, folder.Id, items); err != nil {
|
||||
if errors.Is(err, models.ErrDashboardAclInfoMissing) {
|
||||
err = models.ErrFolderAclInfoMissing
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
callUpdateFolderPermissions(t, sc)
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -92,7 +92,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
callUpdateFolderPermissions(t, sc)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -153,7 +153,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
callUpdateFolderPermissions(t, sc)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
|
||||
var resp struct {
|
||||
@@ -205,7 +205,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
callUpdateFolderPermissions(t, sc)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -233,7 +233,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
callUpdateFolderPermissions(t, sc)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
@@ -279,7 +279,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
callUpdateFolderPermissions(t, sc)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
@@ -355,13 +355,19 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
routePattern: "/api/folders/:uid/permissions",
|
||||
cmd: cmd,
|
||||
fn: func(sc *scenarioContext) {
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
||||
assert.Len(t, cmd.Items, 4)
|
||||
return nil
|
||||
origUpdateDashboardACL := updateDashboardACL
|
||||
t.Cleanup(func() {
|
||||
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()
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
assert.Len(t, gotItems, 4)
|
||||
},
|
||||
}, hs)
|
||||
})
|
||||
@@ -372,10 +378,16 @@ func callGetFolderPermissions(sc *scenarioContext, hs *HTTPServer) {
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||
}
|
||||
|
||||
func callUpdateFolderPermissions(sc *scenarioContext) {
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAclCommand) error {
|
||||
return nil
|
||||
func callUpdateFolderPermissions(t *testing.T, sc *scenarioContext) {
|
||||
t.Helper()
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"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) {
|
||||
defer bus.ClearBusHandlers()
|
||||
|
||||
hs := HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
}
|
||||
|
||||
sc := setupScenarioContext(t, url)
|
||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||
sc.context = c
|
||||
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
|
||||
|
||||
return UpdateFolder(c, cmd)
|
||||
return hs.UpdateFolder(c, cmd)
|
||||
})
|
||||
|
||||
origNewFolderService := dashboards.NewFolderService
|
||||
@@ -195,6 +200,8 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
|
||||
}
|
||||
|
||||
type fakeFolderService struct {
|
||||
dashboards.FolderService
|
||||
|
||||
GetFoldersResult []*models.Folder
|
||||
GetFoldersError error
|
||||
GetFolderByUIDResult *models.Folder
|
||||
@@ -222,9 +229,8 @@ func (s *fakeFolderService) GetFolderByUID(uid string) (*models.Folder, error) {
|
||||
return s.GetFolderByUIDResult, s.GetFolderByUIDError
|
||||
}
|
||||
|
||||
func (s *fakeFolderService) CreateFolder(cmd *models.CreateFolderCommand) error {
|
||||
cmd.Result = s.CreateFolderResult
|
||||
return s.CreateFolderError
|
||||
func (s *fakeFolderService) CreateFolder(title, uid string) (*models.Folder, error) {
|
||||
return s.CreateFolderResult, s.CreateFolderError
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ func ReadSourceMapFromFS(dir string, path string) ([]byte, error) {
|
||||
}
|
||||
|
||||
type SourceMapStore struct {
|
||||
sync.Mutex
|
||||
cache map[string]*sourceMap
|
||||
cfg *setting.Cfg
|
||||
readSourceMap ReadSourceMapFn
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewSourceMapStore(cfg *setting.Cfg, readSourceMap ReadSourceMapFn) *SourceMapStore {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strconv"
|
||||
|
||||
"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/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
|
||||
// the datasource table)
|
||||
for _, ds := range manager.DataSources {
|
||||
for _, ds := range hs.PluginManager.DataSources() {
|
||||
if ds.BuiltIn {
|
||||
dataSources[ds.Name] = map[string]interface{}{
|
||||
"type": ds.Type,
|
||||
"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,
|
||||
"buildstamp": buildstamp,
|
||||
"edition": hs.License.Edition(),
|
||||
"latestVersion": hs.PluginManager.GrafanaLatestVersion,
|
||||
"hasUpdate": hs.PluginManager.GrafanaHasUpdate,
|
||||
"latestVersion": hs.PluginManager.GrafanaLatestVersion(),
|
||||
"hasUpdate": hs.PluginManager.GrafanaHasUpdate(),
|
||||
"env": setting.Env,
|
||||
"isEnterprise": hs.License.HasValidLicense(),
|
||||
},
|
||||
|
||||
@@ -39,18 +39,21 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg) (*macaron.Macaron, *HT
|
||||
})
|
||||
}
|
||||
|
||||
bus.ClearBusHandlers()
|
||||
bus.AddHandler("sql", sqlstore.GetPluginSettings)
|
||||
t.Cleanup(bus.ClearBusHandlers)
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
pm := &manager.PluginManager{Cfg: cfg, SQLStore: sqlStore}
|
||||
|
||||
r := &rendering.RenderingService{Cfg: cfg}
|
||||
r := &rendering.RenderingService{
|
||||
Cfg: cfg,
|
||||
PluginManager: pm,
|
||||
}
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
Bus: bus.GetBus(),
|
||||
License: &licensing.OSSLicensingService{Cfg: cfg},
|
||||
RenderService: r,
|
||||
PluginManager: &manager.PluginManager{Cfg: cfg},
|
||||
SQLStore: sqlStore,
|
||||
PluginManager: pm,
|
||||
}
|
||||
|
||||
m := macaron.New()
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
@@ -79,7 +80,7 @@ type HTTPServer struct {
|
||||
License models.Licensing `inject:""`
|
||||
BackendPluginManager backendplugin.Manager `inject:""`
|
||||
PluginRequestValidator models.PluginRequestValidator `inject:""`
|
||||
PluginManager *manager.PluginManager `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
SearchService *search.SearchService `inject:""`
|
||||
ShortURLService *shorturls.ShortURLService `inject:""`
|
||||
Live *live.GrafanaLive `inject:""`
|
||||
|
||||
@@ -400,8 +400,8 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
GoogleTagManagerId: setting.GoogleTagManagerId,
|
||||
BuildVersion: setting.BuildVersion,
|
||||
BuildCommit: setting.BuildCommit,
|
||||
NewGrafanaVersion: hs.PluginManager.GrafanaLatestVersion,
|
||||
NewGrafanaVersionExists: hs.PluginManager.GrafanaHasUpdate,
|
||||
NewGrafanaVersion: hs.PluginManager.GrafanaLatestVersion(),
|
||||
NewGrafanaVersionExists: hs.PluginManager.GrafanaHasUpdate(),
|
||||
AppName: setting.ApplicationName,
|
||||
AppNameBodyClass: getAppNameBodyClass(hs.License.HasValidLicense()),
|
||||
FavIcon: "public/img/fav32.png",
|
||||
|
||||
@@ -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) {
|
||||
pc := backend.PluginContext{}
|
||||
plugin, exists := manager.Plugins[pluginID]
|
||||
if !exists {
|
||||
plugin := hs.PluginManager.GetPlugin(pluginID)
|
||||
if plugin == nil {
|
||||
return pc, ErrPluginNotFound
|
||||
}
|
||||
|
||||
@@ -74,13 +74,12 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
}
|
||||
|
||||
pluginSettingsMap, err := hs.PluginManager.GetPluginSettings(c.OrgId)
|
||||
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to get list of plugins", err)
|
||||
}
|
||||
|
||||
result := make(dtos.PluginList, 0)
|
||||
for _, pluginDef := range manager.Plugins {
|
||||
for _, pluginDef := range hs.PluginManager.Plugins() {
|
||||
// filter out app sub plugins
|
||||
if embeddedFilter == "0" && pluginDef.IncludedInAppId != "" {
|
||||
continue
|
||||
@@ -130,7 +129,7 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
continue
|
||||
}
|
||||
@@ -143,11 +142,11 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
||||
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")
|
||||
|
||||
def, exists := manager.Plugins[pluginID]
|
||||
if !exists {
|
||||
def := hs.PluginManager.GetPlugin(pluginID)
|
||||
if def == 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,
|
||||
apiCmd.Dashboard, apiCmd.Overwrite, apiCmd.Inputs, c.SignedInUser, hs.DataService)
|
||||
if err != nil {
|
||||
return dashboardSaveErrorToApiResponse(err)
|
||||
return hs.dashboardSaveErrorToApiResponse(err)
|
||||
}
|
||||
|
||||
return response.JSON(200, dashInfo)
|
||||
@@ -267,8 +266,8 @@ func (hs *HTTPServer) ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDa
|
||||
// /api/plugins/:pluginId/metrics
|
||||
func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) response.Response {
|
||||
pluginID := c.Params("pluginId")
|
||||
plugin, exists := manager.Plugins[pluginID]
|
||||
if !exists {
|
||||
plugin := hs.PluginManager.GetPlugin(pluginID)
|
||||
if plugin == nil {
|
||||
return response.Error(404, "Plugin not found", nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,19 +7,19 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
// POST /api/teams
|
||||
func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamCommand) response.Response {
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
if c.OrgRole == models.ROLE_VIEWER {
|
||||
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) {
|
||||
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
|
||||
// an additional check whether it is an actual user is required
|
||||
if c.SignedInUser.IsRealUser() {
|
||||
addMemberCmd := models.AddTeamMemberCommand{
|
||||
UserId: c.SignedInUser.UserId,
|
||||
OrgId: cmd.OrgId,
|
||||
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)
|
||||
if err := addTeamMember(hs.SQLStore, c.SignedInUser.UserId, c.OrgId, team.Id, false,
|
||||
models.PERMISSION_ADMIN); err != nil {
|
||||
c.Logger.Error("Could not add creator to team", "error", err)
|
||||
}
|
||||
} 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{
|
||||
"teamId": cmd.Result.Id,
|
||||
"teamId": team.Id,
|
||||
"message": "Team created",
|
||||
})
|
||||
}
|
||||
@@ -175,3 +169,18 @@ func (hs *HTTPServer) UpdateTeamPreferences(c *models.ReqContext, dtoCmd dtos.Up
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
|
||||
@@ -98,20 +99,29 @@ func TestTeamAPIEndpoint(t *testing.T) {
|
||||
|
||||
teamName := "team foo"
|
||||
|
||||
createTeamCalled := 0
|
||||
bus.AddHandler("test", func(cmd *models.CreateTeamCommand) error {
|
||||
createTeamCalled += 1
|
||||
cmd.Result = models.Team{Name: teamName, Id: 42}
|
||||
return nil
|
||||
// TODO: Use a fake SQLStore when it's represented by an interface
|
||||
origCreateTeam := createTeam
|
||||
origAddTeamMember := addTeamMember
|
||||
t.Cleanup(func() {
|
||||
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
|
||||
bus.AddHandler("test", func(cmd *models.AddTeamMemberCommand) error {
|
||||
addTeamMemberCalled += 1
|
||||
addTeamMember = func(sqlStore *sqlstore.SQLStore, userID, orgID, teamID int64, isExternal bool,
|
||||
permission models.PermissionType) error {
|
||||
addTeamMemberCalled++
|
||||
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) {
|
||||
stub := &testLogger{}
|
||||
@@ -128,7 +138,7 @@ func TestTeamAPIEndpoint(t *testing.T) {
|
||||
assert.Equal(t, createTeamCalled, 1)
|
||||
assert.Equal(t, addTeamMemberCalled, 0)
|
||||
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) {
|
||||
|
||||
16
pkg/dashboards/ifaces.go
Normal file
16
pkg/dashboards/ifaces.go
Normal file
@@ -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/login/social"
|
||||
"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/sqlstore"
|
||||
|
||||
@@ -38,6 +39,7 @@ type UsageStatsService struct {
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
AlertingUsageStats alerting.UsageStatsQuerier `inject:""`
|
||||
License models.Licensing `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
|
||||
log log.Logger
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport,
|
||||
metrics["stats.playlist.count"] = statsQuery.Result.Playlists
|
||||
metrics["stats.plugins.apps.count"] = len(manager.Apps)
|
||||
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.active_users.count"] = statsQuery.Result.ActiveUsers
|
||||
metrics["stats.datasources.count"] = statsQuery.Result.Datasources
|
||||
@@ -329,8 +329,8 @@ func (uss *UsageStatsService) updateTotalStats() {
|
||||
}
|
||||
|
||||
func (uss *UsageStatsService) shouldBeReported(dsType string) bool {
|
||||
ds, ok := manager.DataSources[dsType]
|
||||
if !ok {
|
||||
ds := uss.PluginManager.GetDataSource(dsType)
|
||||
if ds == nil {
|
||||
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, 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.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.ActiveUsers, metrics.Get("stats.active_users.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
|
||||
}
|
||||
|
||||
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) {
|
||||
t.Helper()
|
||||
|
||||
originalDataSources := manager.DataSources
|
||||
t.Cleanup(func() { manager.DataSources = originalDataSources })
|
||||
manager.DataSources = map[string]*plugins.DataSourcePlugin{
|
||||
models.DS_ES: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
uss.PluginManager = &fakePluginManager{
|
||||
dataSources: map[string]*plugins.DataSourcePlugin{
|
||||
models.DS_ES: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
models.DS_PROMETHEUS: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
models.DS_PROMETHEUS: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
models.DS_GRAPHITE: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
models.DS_GRAPHITE: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
models.DS_MYSQL: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
models.DS_MYSQL: {
|
||||
FrontendPluginBase: plugins.FrontendPluginBase{
|
||||
PluginBase: plugins.PluginBase{
|
||||
Signature: "internal",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -595,5 +609,6 @@ func createService(t *testing.T, cfg setting.Cfg) *UsageStatsService {
|
||||
License: &licensing.OSSLicensingService{},
|
||||
AlertingUsageStats: &alertingUsageMock{},
|
||||
externalMetrics: make(map[string]MetricFunc),
|
||||
PluginManager: &fakePluginManager{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,18 +209,3 @@ type AlertStateInfoDTO struct {
|
||||
State AlertStateType `json:"state"`
|
||||
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)
|
||||
}
|
||||
|
||||
//
|
||||
// COMMANDS
|
||||
//
|
||||
|
||||
type UpdateDashboardAclCommand struct {
|
||||
DashboardID int64
|
||||
Items []*DashboardAcl
|
||||
}
|
||||
|
||||
//
|
||||
// QUERIES
|
||||
//
|
||||
|
||||
@@ -367,13 +367,6 @@ type DeleteDashboardCommand struct {
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
type ValidateDashboardBeforeSaveCommand struct {
|
||||
OrgId int64
|
||||
Dashboard *Dashboard
|
||||
Overwrite bool
|
||||
Result *ValidateDashboardBeforeSaveResult
|
||||
}
|
||||
|
||||
type DeleteOrphanedProvisionedDashboardsCommand struct {
|
||||
ReaderNames []string
|
||||
}
|
||||
@@ -430,11 +423,6 @@ type GetProvisionedDashboardDataByIdQuery struct {
|
||||
Result *DashboardProvisioning
|
||||
}
|
||||
|
||||
type GetProvisionedDashboardDataQuery struct {
|
||||
Name string
|
||||
Result []*DashboardProvisioning
|
||||
}
|
||||
|
||||
type GetDashboardsBySlugQuery struct {
|
||||
OrgId int64
|
||||
Slug string
|
||||
|
||||
@@ -32,23 +32,6 @@ type Folder struct {
|
||||
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
|
||||
func (cmd *UpdateFolderCommand) UpdateDashboardModel(dashFolder *Dashboard, orgId int64, userId int64) {
|
||||
dashFolder.OrgId = orgId
|
||||
|
||||
@@ -53,10 +53,6 @@ func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() securejsondata.SecureJ
|
||||
|
||||
// ---------------------
|
||||
// QUERIES
|
||||
type GetPluginSettingsQuery struct {
|
||||
OrgId int64
|
||||
Result []*PluginSettingInfoDTO
|
||||
}
|
||||
|
||||
type PluginSettingInfoDTO struct {
|
||||
OrgId int64
|
||||
|
||||
@@ -3,10 +3,55 @@ package plugins
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"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.
|
||||
type DataRequestHandler interface {
|
||||
// HandleRequest handles a data request.
|
||||
HandleRequest(context.Context, *models.DataSource, DataQuery) (DataResponse, error)
|
||||
}
|
||||
|
||||
@@ -13,13 +13,6 @@ import (
|
||||
|
||||
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 {
|
||||
VariableName string
|
||||
}
|
||||
@@ -29,7 +22,7 @@ func (e DashboardInputMissingError) Error() string {
|
||||
}
|
||||
|
||||
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) {
|
||||
var dashboard *models.Dashboard
|
||||
if pluginID != "" {
|
||||
@@ -67,7 +60,7 @@ func (pm *PluginManager) ImportDashboard(pluginID, path string, orgID, folderID
|
||||
User: user,
|
||||
}
|
||||
|
||||
savedDash, err := dashboards.NewService().ImportDashboard(dto)
|
||||
savedDash, err := dashboards.NewService(pm.SQLStore).ImportDashboard(dto)
|
||||
if err != nil {
|
||||
return plugins.PluginDashboardInfoDTO{}, err
|
||||
}
|
||||
@@ -89,12 +82,12 @@ func (pm *PluginManager) ImportDashboard(pluginID, path string, orgID, folderID
|
||||
|
||||
type DashTemplateEvaluator struct {
|
||||
template *simplejson.Json
|
||||
inputs []ImportDashboardInput
|
||||
inputs []plugins.ImportDashboardInput
|
||||
variables map[string]string
|
||||
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 {
|
||||
if varType == input.Type && (input.Name == varName || input.Name == "*") {
|
||||
return &input
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -21,7 +22,7 @@ func TestDashboardImport(t *testing.T) {
|
||||
dashboards.MockDashboardService(mock)
|
||||
|
||||
info, err := pm.ImportDashboard("test-app", "dashboards/connections.json", 1, 0, nil, false,
|
||||
[]ImportDashboardInput{
|
||||
[]plugins.ImportDashboardInput{
|
||||
{Name: "*", Type: "datasource", Value: "graphite"},
|
||||
}, &models.SignedInUser{UserId: 1, OrgRole: models.ROLE_ADMIN}, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -58,7 +59,7 @@ func TestDashboardImport(t *testing.T) {
|
||||
|
||||
evaluator := &DashTemplateEvaluator{
|
||||
template: template,
|
||||
inputs: []ImportDashboardInput{
|
||||
inputs: []plugins.ImportDashboardInput{
|
||||
{Name: "*", Type: "datasource", Value: "my-server"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func (pm *PluginManager) GetPluginDashboards(orgId int64, pluginId string) ([]*plugins.PluginDashboardInfoDTO, error) {
|
||||
plugin, exists := Plugins[pluginId]
|
||||
plugin, exists := pm.plugins[pluginId]
|
||||
if !exists {
|
||||
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) {
|
||||
plugin, exists := Plugins[pluginId]
|
||||
plugin, exists := pm.plugins[pluginId]
|
||||
if !exists {
|
||||
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,17 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
var (
|
||||
DataSources map[string]*plugins.DataSourcePlugin
|
||||
Panels map[string]*plugins.PanelPlugin
|
||||
StaticRoutes []*plugins.PluginStaticRoute
|
||||
Apps map[string]*plugins.AppPlugin
|
||||
Plugins map[string]*plugins.PluginBase
|
||||
PluginTypes map[string]interface{}
|
||||
Renderer *plugins.RendererPlugin
|
||||
|
||||
plog log.Logger
|
||||
)
|
||||
@@ -54,30 +52,36 @@ type PluginScanner struct {
|
||||
type PluginManager struct {
|
||||
BackendPluginManager backendplugin.Manager `inject:""`
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
log log.Logger
|
||||
scanningErrors []error
|
||||
|
||||
// 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.
|
||||
AllowUnsignedPluginsCondition unsignedPluginConditionFunc
|
||||
GrafanaLatestVersion string
|
||||
GrafanaHasUpdate bool
|
||||
grafanaLatestVersion string
|
||||
grafanaHasUpdate bool
|
||||
pluginScanningErrors map[string]plugins.PluginError
|
||||
|
||||
renderer *plugins.RendererPlugin
|
||||
dataSources map[string]*plugins.DataSourcePlugin
|
||||
plugins map[string]*plugins.PluginBase
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterService(&PluginManager{})
|
||||
registry.RegisterService(&PluginManager{
|
||||
dataSources: map[string]*plugins.DataSourcePlugin{},
|
||||
})
|
||||
}
|
||||
|
||||
func (pm *PluginManager) Init() error {
|
||||
pm.log = log.New("plugins")
|
||||
plog = log.New("plugins")
|
||||
|
||||
DataSources = map[string]*plugins.DataSourcePlugin{}
|
||||
StaticRoutes = []*plugins.PluginStaticRoute{}
|
||||
Panels = map[string]*plugins.PanelPlugin{}
|
||||
Apps = map[string]*plugins.AppPlugin{}
|
||||
Plugins = map[string]*plugins.PluginBase{}
|
||||
pm.plugins = map[string]*plugins.PluginBase{}
|
||||
PluginTypes = map[string]interface{}{
|
||||
"panel": plugins.PanelPlugin{},
|
||||
"datasource": plugins.DataSourcePlugin{},
|
||||
@@ -134,22 +138,22 @@ func (pm *PluginManager) Init() error {
|
||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||
}
|
||||
|
||||
for _, ds := range DataSources {
|
||||
for _, ds := range pm.dataSources {
|
||||
staticRoutes := ds.InitFrontendPlugin(pm.Cfg)
|
||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||
}
|
||||
|
||||
for _, app := range Apps {
|
||||
staticRoutes := app.InitApp(Panels, DataSources, pm.Cfg)
|
||||
staticRoutes := app.InitApp(Panels, pm.dataSources, pm.Cfg)
|
||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||
}
|
||||
|
||||
if Renderer != nil {
|
||||
staticRoutes := Renderer.InitFrontendPlugin(pm.Cfg)
|
||||
if pm.renderer != nil {
|
||||
staticRoutes := pm.renderer.InitFrontendPlugin(pm.Cfg)
|
||||
StaticRoutes = append(StaticRoutes, staticRoutes...)
|
||||
}
|
||||
|
||||
for _, p := range Plugins {
|
||||
for _, p := range pm.plugins {
|
||||
if p.IsCorePlugin {
|
||||
p.Signature = plugins.PluginSignatureInternal
|
||||
} else {
|
||||
@@ -178,6 +182,48 @@ func (pm *PluginManager) Run(ctx context.Context) error {
|
||||
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.
|
||||
func (pm *PluginManager) scanPluginPaths() error {
|
||||
for pluginID, settings := range pm.Cfg.PluginSettings {
|
||||
@@ -315,13 +361,13 @@ func (pm *PluginManager) loadPlugin(jsonParser *json.Decoder, pluginBase *plugin
|
||||
var pb *plugins.PluginBase
|
||||
switch p := plug.(type) {
|
||||
case *plugins.DataSourcePlugin:
|
||||
DataSources[p.Id] = p
|
||||
pm.dataSources[p.Id] = p
|
||||
pb = &p.PluginBase
|
||||
case *plugins.PanelPlugin:
|
||||
Panels[p.Id] = p
|
||||
pb = &p.PluginBase
|
||||
case *plugins.RendererPlugin:
|
||||
Renderer = p
|
||||
pm.renderer = p
|
||||
pb = &p.PluginBase
|
||||
case *plugins.AppPlugin:
|
||||
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))
|
||||
}
|
||||
|
||||
if p, exists := Plugins[pb.Id]; exists {
|
||||
if p, exists := pm.plugins[pb.Id]; exists {
|
||||
pm.log.Warn("Plugin is duplicate", "id", pb.Id)
|
||||
scanner.errors = append(scanner.errors, plugins.DuplicatePluginError{Plugin: pb, ExistingPlugin: p})
|
||||
return nil
|
||||
@@ -360,20 +406,11 @@ func (pm *PluginManager) loadPlugin(jsonParser *json.Decoder, pluginBase *plugin
|
||||
pb.SignatureType = pluginBase.SignatureType
|
||||
pb.SignatureOrg = pluginBase.SignatureOrg
|
||||
|
||||
Plugins[pb.Id] = pb
|
||||
pm.plugins[pb.Id] = pb
|
||||
pm.log.Debug("Successfully added plugin", "id", pb.Id)
|
||||
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 {
|
||||
// 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
|
||||
@@ -545,7 +582,7 @@ func (pm *PluginManager) ScanningErrors() []plugins.PluginError {
|
||||
}
|
||||
|
||||
func (pm *PluginManager) GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
|
||||
plug, exists := Plugins[pluginId]
|
||||
plug, exists := pm.plugins[pluginId]
|
||||
if !exists {
|
||||
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.
|
||||
func (pm *PluginManager) GetDataPlugin(pluginID string) plugins.DataPlugin {
|
||||
if p, exists := DataSources[pluginID]; exists && p.CanHandleDataQueries() {
|
||||
func (pm *PluginManager) GetDataPlugin(id string) plugins.DataPlugin {
|
||||
if p, exists := pm.dataSources[id]; exists && p.CanHandleDataQueries() {
|
||||
return p
|
||||
}
|
||||
|
||||
// XXX: Might other plugins implement DataPlugin?
|
||||
|
||||
p := pm.BackendPluginManager.GetDataPlugin(pluginID)
|
||||
p := pm.BackendPluginManager.GetDataPlugin(id)
|
||||
if p != nil {
|
||||
return p.(plugins.DataPlugin)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
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.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.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)
|
||||
@@ -114,15 +114,15 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
require.Empty(t, pm.scanningErrors)
|
||||
|
||||
const pluginID = "test"
|
||||
assert.NotNil(t, Plugins[pluginID])
|
||||
assert.Equal(t, "datasource", Plugins[pluginID].Type)
|
||||
assert.Equal(t, "Test", Plugins[pluginID].Name)
|
||||
assert.Equal(t, pluginID, Plugins[pluginID].Id)
|
||||
assert.Equal(t, "1.0.0", Plugins[pluginID].Info.Version)
|
||||
assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginID].Signature)
|
||||
assert.Equal(t, plugins.GrafanaType, Plugins[pluginID].SignatureType)
|
||||
assert.Equal(t, "Grafana Labs", Plugins[pluginID].SignatureOrg)
|
||||
assert.False(t, Plugins[pluginID].IsCorePlugin)
|
||||
assert.NotNil(t, pm.plugins[pluginID])
|
||||
assert.Equal(t, "datasource", pm.plugins[pluginID].Type)
|
||||
assert.Equal(t, "Test", pm.plugins[pluginID].Name)
|
||||
assert.Equal(t, pluginID, pm.plugins[pluginID].Id)
|
||||
assert.Equal(t, "1.0.0", pm.plugins[pluginID].Info.Version)
|
||||
assert.Equal(t, plugins.PluginSignatureValid, pm.plugins[pluginID].Signature)
|
||||
assert.Equal(t, plugins.GrafanaType, pm.plugins[pluginID].SignatureType)
|
||||
assert.Equal(t, "Grafana Labs", pm.plugins[pluginID].SignatureOrg)
|
||||
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) {
|
||||
@@ -139,7 +139,7 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
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) {
|
||||
@@ -157,15 +157,15 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
require.Empty(t, pm.scanningErrors)
|
||||
|
||||
const pluginID = "test"
|
||||
assert.NotNil(t, Plugins[pluginID])
|
||||
assert.Equal(t, "datasource", Plugins[pluginID].Type)
|
||||
assert.Equal(t, "Test", Plugins[pluginID].Name)
|
||||
assert.Equal(t, pluginID, Plugins[pluginID].Id)
|
||||
assert.Equal(t, "1.0.0", Plugins[pluginID].Info.Version)
|
||||
assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginID].Signature)
|
||||
assert.Equal(t, plugins.PrivateType, Plugins[pluginID].SignatureType)
|
||||
assert.Equal(t, "Will Browne", Plugins[pluginID].SignatureOrg)
|
||||
assert.False(t, Plugins[pluginID].IsCorePlugin)
|
||||
assert.NotNil(t, pm.plugins[pluginID])
|
||||
assert.Equal(t, "datasource", pm.plugins[pluginID].Type)
|
||||
assert.Equal(t, "Test", pm.plugins[pluginID].Name)
|
||||
assert.Equal(t, pluginID, pm.plugins[pluginID].Id)
|
||||
assert.Equal(t, "1.0.0", pm.plugins[pluginID].Info.Version)
|
||||
assert.Equal(t, plugins.PluginSignatureValid, pm.plugins[pluginID].Signature)
|
||||
assert.Equal(t, plugins.PrivateType, pm.plugins[pluginID].SignatureType)
|
||||
assert.Equal(t, "Will Browne", pm.plugins[pluginID].SignatureOrg)
|
||||
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) {
|
||||
@@ -181,7 +181,7 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
err := pm.Init()
|
||||
require.NoError(t, err)
|
||||
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) {
|
||||
@@ -197,7 +197,7 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
err := pm.Init()
|
||||
require.NoError(t, err)
|
||||
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,
|
||||
},
|
||||
BackendPluginManager: &fakeBackendPluginManager{},
|
||||
dataSources: map[string]*plugins.DataSourcePlugin{},
|
||||
}
|
||||
for _, cb := range cbs {
|
||||
cb(pm)
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
)
|
||||
|
||||
func (pm *PluginManager) GetPluginSettings(orgId int64) (map[string]*models.PluginSettingInfoDTO, error) {
|
||||
query := models.GetPluginSettingsQuery{OrgId: orgId}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
func (pm *PluginManager) GetPluginSettings(orgID int64) (map[string]*models.PluginSettingInfoDTO, error) {
|
||||
pluginSettings, err := pm.SQLStore.GetPluginSettings(orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginMap := make(map[string]*models.PluginSettingInfoDTO)
|
||||
for _, plug := range query.Result {
|
||||
for _, plug := range pluginSettings {
|
||||
pluginMap[plug.PluginId] = plug
|
||||
}
|
||||
|
||||
for _, pluginDef := range Plugins {
|
||||
for _, pluginDef := range pm.plugins {
|
||||
// ignore entries that exists
|
||||
if _, ok := pluginMap[pluginDef.Id]; ok {
|
||||
continue
|
||||
@@ -26,7 +25,7 @@ func (pm *PluginManager) GetPluginSettings(orgId int64) (map[string]*models.Plug
|
||||
// default to enabled true
|
||||
opt := &models.PluginSettingInfoDTO{
|
||||
PluginId: pluginDef.Id,
|
||||
OrgId: orgId,
|
||||
OrgId: orgID,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
@@ -72,7 +71,7 @@ func (pm *PluginManager) GetEnabledPlugins(orgID int64) (*plugins.EnabledPlugins
|
||||
}
|
||||
|
||||
// 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 {
|
||||
enabledPlugins.DataSources[dsID] = ds
|
||||
}
|
||||
|
||||
@@ -16,19 +16,19 @@ var (
|
||||
httpClient = http.Client{Timeout: 10 * time.Second}
|
||||
)
|
||||
|
||||
type GrafanaNetPlugin struct {
|
||||
type grafanaNetPlugin struct {
|
||||
Slug string `json:"slug"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type GithubLatest struct {
|
||||
type gitHubLatest struct {
|
||||
Stable string `json:"stable"`
|
||||
Testing string `json:"testing"`
|
||||
}
|
||||
|
||||
func getAllExternalPluginSlugs() string {
|
||||
func (pm *PluginManager) getAllExternalPluginSlugs() string {
|
||||
var result []string
|
||||
for _, plug := range Plugins {
|
||||
for _, plug := range pm.plugins {
|
||||
if plug.IsCorePlugin {
|
||||
continue
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func (pm *PluginManager) checkForUpdates() {
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
log.Tracef("Failed to get plugins repo from grafana.com, %v", err.Error())
|
||||
@@ -64,14 +64,14 @@ func (pm *PluginManager) checkForUpdates() {
|
||||
return
|
||||
}
|
||||
|
||||
gNetPlugins := []GrafanaNetPlugin{}
|
||||
gNetPlugins := []grafanaNetPlugin{}
|
||||
err = json.Unmarshal(body, &gNetPlugins)
|
||||
if err != nil {
|
||||
log.Tracef("Failed to unmarshal plugin repo, reading response from grafana.com, %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, plug := range Plugins {
|
||||
for _, plug := range pm.plugins {
|
||||
for _, gplug := range gNetPlugins {
|
||||
if gplug.Slug == plug.Id {
|
||||
plug.GrafanaNetVersion = gplug.Version
|
||||
@@ -104,24 +104,24 @@ func (pm *PluginManager) checkForUpdates() {
|
||||
return
|
||||
}
|
||||
|
||||
var githubLatest GithubLatest
|
||||
err = json.Unmarshal(body, &githubLatest)
|
||||
var latest gitHubLatest
|
||||
err = json.Unmarshal(body, &latest)
|
||||
if err != nil {
|
||||
log.Tracef("Failed to unmarshal github.com latest, reading response from github.com: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(setting.BuildVersion, "-") {
|
||||
pm.GrafanaLatestVersion = githubLatest.Testing
|
||||
pm.GrafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
|
||||
pm.grafanaLatestVersion = latest.Testing
|
||||
pm.grafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, latest.Testing)
|
||||
} else {
|
||||
pm.GrafanaLatestVersion = githubLatest.Stable
|
||||
pm.GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
|
||||
pm.grafanaLatestVersion = latest.Stable
|
||||
pm.grafanaHasUpdate = latest.Stable != 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 {
|
||||
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/manager"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
)
|
||||
|
||||
@@ -22,6 +23,7 @@ func init() {
|
||||
type Service struct {
|
||||
DataService *tsdb.Service `inject:""`
|
||||
PluginManager *manager.PluginManager `inject:""`
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
|
||||
logger log.Logger
|
||||
}
|
||||
@@ -40,19 +42,19 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
func (s *Service) updateAppDashboards() {
|
||||
s.logger.Debug("Looking for app dashboard updates")
|
||||
|
||||
query := models.GetPluginSettingsQuery{OrgId: 0}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
pluginSettings, err := s.SQLStore.GetPluginSettings(0)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get all plugin settings", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, pluginSetting := range query.Result {
|
||||
for _, pluginSetting := range pluginSettings {
|
||||
// ignore disabled plugins
|
||||
if !pluginSetting.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
if pluginDef, exists := manager.Plugins[pluginSetting.PluginId]; exists {
|
||||
if pluginDef := s.PluginManager.GetPlugin(pluginSetting.PluginId); pluginDef != nil {
|
||||
if pluginDef.Info.Version != pluginSetting.PluginVersion {
|
||||
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)
|
||||
|
||||
if event.Enabled {
|
||||
s.syncPluginDashboards(manager.Plugins[event.PluginId], event.OrgId)
|
||||
s.syncPluginDashboards(s.PluginManager.GetPlugin(event.PluginId), event.OrgId)
|
||||
} else {
|
||||
query := models.GetDashboardsByPluginIdQuery{PluginId: event.PluginId, OrgId: event.OrgId}
|
||||
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/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
tlog "github.com/opentracing/opentracing-go/log"
|
||||
@@ -27,7 +27,7 @@ type AlertEngine struct {
|
||||
RenderService rendering.Service `inject:""`
|
||||
Bus bus.Bus `inject:""`
|
||||
RequestValidator models.PluginRequestValidator `inject:""`
|
||||
DataService *tsdb.Service `inject:""`
|
||||
DataService plugins.DataRequestHandler `inject:""`
|
||||
|
||||
execQueue chan *Job
|
||||
ticker *Ticker
|
||||
|
||||
@@ -61,7 +61,7 @@ func findPanelQueryByRefID(panel *simplejson.Json, refID string) *simplejson.Jso
|
||||
func copyJSON(in json.Marshaler) (*simplejson.Json, error) {
|
||||
rawJSON, err := in.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("JSON marshaling failed: %w", err)
|
||||
}
|
||||
|
||||
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
|
||||
// in the first validation pass.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ package dashboards
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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
|
||||
rtViewer := models.ROLE_VIEWER
|
||||
|
||||
@@ -43,12 +42,7 @@ func MakeUserAdmin(bus bus.Bus, orgID int64, userID int64, dashboardID int64, se
|
||||
)
|
||||
}
|
||||
|
||||
aclCmd := &models.UpdateDashboardAclCommand{
|
||||
DashboardID: dashboardID,
|
||||
Items: items,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(aclCmd); err != nil {
|
||||
if err := dr.dashboardStore.UpdateDashboardACL(dashboardID, items); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package dashboards
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/bus"
|
||||
@@ -20,6 +23,7 @@ type DashboardService interface {
|
||||
SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error)
|
||||
ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, 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.
|
||||
@@ -27,21 +31,22 @@ type DashboardProvisioningService interface {
|
||||
SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
||||
SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
|
||||
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
||||
GetProvisionedDashboardDataByDashboardID(DashboardId int64) (*models.DashboardProvisioning, error)
|
||||
UnprovisionDashboard(dashboardId int64) error
|
||||
DeleteProvisionedDashboard(dashboardId int64, orgId int64) error
|
||||
GetProvisionedDashboardDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error)
|
||||
UnprovisionDashboard(dashboardID int64) error
|
||||
DeleteProvisionedDashboard(dashboardID int64, orgID int64) error
|
||||
}
|
||||
|
||||
// NewService is a factory for creating a new dashboard service.
|
||||
var NewService = func() DashboardService {
|
||||
var NewService = func(store dashboards.Store) DashboardService {
|
||||
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.
|
||||
var NewProvisioningService = func() DashboardProvisioningService {
|
||||
return NewService().(*dashboardServiceImpl)
|
||||
var NewProvisioningService = func(store dashboards.Store) DashboardProvisioningService {
|
||||
return NewService(store).(*dashboardServiceImpl)
|
||||
}
|
||||
|
||||
type SaveDashboardDTO struct {
|
||||
@@ -54,35 +59,32 @@ type SaveDashboardDTO struct {
|
||||
}
|
||||
|
||||
type dashboardServiceImpl struct {
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
log log.Logger
|
||||
dashboardStore dashboards.Store
|
||||
orgId int64
|
||||
user *models.SignedInUser
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
||||
cmd := &models.GetProvisionedDashboardDataQuery{Name: name}
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dr.dashboardStore.GetProvisionedDashboardData(name)
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(dashboardId int64) (*models.DashboardProvisioning, error) {
|
||||
cmd := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashboardId}
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
// GetProvisionedData gets provisioned dashboard data.
|
||||
//
|
||||
// Stubbable by tests.
|
||||
var GetProvisionedData = func(store dashboards.Store, dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||
return store.GetProvisionedDataByDashboardID(dashboardID)
|
||||
}
|
||||
|
||||
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) {
|
||||
dash := dto.Dashboard
|
||||
|
||||
dash.OrgId = dto.OrgId
|
||||
dash.Title = strings.TrimSpace(dash.Title)
|
||||
dash.Data.Set("title", dash.Title)
|
||||
dash.SetUid(strings.TrimSpace(dash.Uid))
|
||||
@@ -109,29 +111,18 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if validateAlerts {
|
||||
validateAlertsCmd := models.ValidateDashboardAlertsCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: dash,
|
||||
User: dto.User,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&validateAlertsCmd); err != nil {
|
||||
if shouldValidateAlerts {
|
||||
if err := validateAlerts(dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
validateBeforeSaveCmd := models.ValidateDashboardBeforeSaveCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: dash,
|
||||
Overwrite: dto.Overwrite,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&validateBeforeSaveCmd); err != nil {
|
||||
isParentFolderChanged, err := dr.dashboardStore.ValidateDashboardBeforeSave(dash, dto.Overwrite)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if validateBeforeSaveCmd.Result.IsParentFolderChanged {
|
||||
if isParentFolderChanged {
|
||||
folderGuardian := guardian.New(dash.FolderId, dto.OrgId, dto.User)
|
||||
if canSave, err := folderGuardian.CanSave(); err != nil || !canSave {
|
||||
if err != nil {
|
||||
@@ -178,6 +169,11 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
||||
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 {
|
||||
if setting.MinRefreshInterval == "" {
|
||||
return nil
|
||||
@@ -191,11 +187,11 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
|
||||
minRefreshInterval, err := gtime.ParseDuration(setting.MinRefreshInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("parsing min refresh interval %q failed: %w", setting.MinRefreshInterval, err)
|
||||
}
|
||||
d, err := gtime.ParseDuration(refresh)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("parsing refresh duration %q failed: %w", refresh, err)
|
||||
}
|
||||
|
||||
if d < minRefreshInterval {
|
||||
@@ -205,14 +201,17 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand, dto *SaveDashboardDTO) error {
|
||||
alertCmd := models.UpdateDashboardAlertsCommand{
|
||||
OrgId: dto.OrgId,
|
||||
Dashboard: cmd.Result,
|
||||
User: dto.User,
|
||||
// UpdateAlerting updates alerting.
|
||||
//
|
||||
// Stubbable by tests.
|
||||
var UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||
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,
|
||||
@@ -234,24 +233,18 @@ func (dr *dashboardServiceImpl) SaveProvisionedDashboard(dto *SaveDashboardDTO,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
saveCmd := &models.SaveProvisionedDashboardCommand{
|
||||
DashboardCmd: cmd,
|
||||
DashboardProvisioning: provisioning,
|
||||
}
|
||||
|
||||
// dashboard
|
||||
err = bus.Dispatch(saveCmd)
|
||||
dash, err := dr.dashboardStore.SaveProvisionedDashboard(*cmd, provisioning)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// alerts
|
||||
err = dr.updateAlerting(cmd, dto)
|
||||
if err != nil {
|
||||
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDashboardDTO) (*models.Dashboard, error) {
|
||||
@@ -264,22 +257,23 @@ func (dr *dashboardServiceImpl) SaveFolderForProvisionedDashboards(dto *SaveDash
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bus.Dispatch(cmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dr.updateAlerting(cmd, dto)
|
||||
if err != nil {
|
||||
if err := UpdateAlerting(dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpdate bool) (*models.Dashboard, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -288,17 +282,16 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO, allowUiUpda
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bus.Dispatch(cmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*cmd)
|
||||
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
|
||||
}
|
||||
|
||||
err = dr.updateAlerting(cmd, dto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmd.Result, nil
|
||||
return dash, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -359,6 +355,7 @@ type FakeDashboardService struct {
|
||||
SaveDashboardResult *models.Dashboard
|
||||
SaveDashboardError error
|
||||
SavedDashboards []*SaveDashboardDTO
|
||||
ProvisionedDashData *models.DashboardProvisioning
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (s *FakeDashboardService) GetProvisionedDashboardDataByDashboardID(id int64) (*models.DashboardProvisioning, error) {
|
||||
return s.ProvisionedDashData, nil
|
||||
}
|
||||
|
||||
func MockDashboardService(mock *FakeDashboardService) {
|
||||
NewService = func() DashboardService {
|
||||
NewService = func(dashboards.Store) DashboardService {
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
928
pkg/services/dashboards/dashboard_service_integration_test.go
Normal file
928
pkg/services/dashboards/dashboard_service_integration_test.go
Normal file
@@ -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"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
@@ -17,8 +18,10 @@ func TestDashboardService(t *testing.T) {
|
||||
Convey("Dashboard service tests", t, func() {
|
||||
bus.ClearBusHandlers()
|
||||
|
||||
fakeStore := fakeDashboardStore{}
|
||||
service := &dashboardServiceImpl{
|
||||
log: log.New("test.logger"),
|
||||
log: log.New("test.logger"),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
origNewDashboardGuardian := guardian.New
|
||||
@@ -51,19 +54,13 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("When saving a dashboard should validate uid", func() {
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
cmd.Result = nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
Uid string
|
||||
@@ -89,27 +86,23 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||
provisioningValidated := false
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
provisioningValidated = true
|
||||
cmd.Result = &models.DashboardProvisioning{}
|
||||
return nil
|
||||
t.Cleanup(func() {
|
||||
fakeStore.provisionedData = nil
|
||||
})
|
||||
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
_, err := service.SaveDashboard(dto, false)
|
||||
So(provisioningValidated, ShouldBeTrue)
|
||||
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
||||
})
|
||||
|
||||
@@ -121,21 +114,20 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
_, err := service.SaveDashboard(dto, true)
|
||||
So(provisioningValidated, ShouldBeFalse)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Should return validation error if alert data is invalid", func() {
|
||||
@@ -144,9 +136,13 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return fmt.Errorf("alert validation error")
|
||||
origValidateAlerts := validateAlerts
|
||||
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")
|
||||
_, err := service.SaveDashboard(dto, false)
|
||||
@@ -165,23 +161,27 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
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 {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
dto.Dashboard = models.NewDashboard("Dash")
|
||||
dto.Dashboard.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
@@ -200,22 +200,26 @@ func TestDashboardService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
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.SetId(3)
|
||||
@@ -231,41 +235,42 @@ func TestDashboardService(t *testing.T) {
|
||||
dto := &SaveDashboardDTO{}
|
||||
|
||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||
provisioningValidated := false
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
provisioningValidated = true
|
||||
cmd.Result = &models.DashboardProvisioning{}
|
||||
return nil
|
||||
t.Cleanup(func() {
|
||||
fakeStore.provisionedData = nil
|
||||
})
|
||||
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origValidateAlerts := validateAlerts
|
||||
t.Cleanup(func() {
|
||||
validateAlerts = origValidateAlerts
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
validateAlerts = func(dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
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.SetId(3)
|
||||
dto.User = &models.SignedInUser{UserId: 1}
|
||||
_, err := service.ImportDashboard(dto)
|
||||
So(provisioningValidated, ShouldBeTrue)
|
||||
So(err, ShouldEqual, models.ErrDashboardCannotSaveProvisionedDashboard)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given provisioned dashboard", func() {
|
||||
result := setupDeleteHandlers(true)
|
||||
result := setupDeleteHandlers(t, &fakeStore, true)
|
||||
|
||||
Convey("DeleteProvisionedDashboard should delete it", func() {
|
||||
err := service.DeleteProvisionedDashboard(1, 1)
|
||||
@@ -281,7 +286,7 @@ func TestDashboardService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given non provisioned dashboard", func() {
|
||||
result := setupDeleteHandlers(false)
|
||||
result := setupDeleteHandlers(t, &fakeStore, false)
|
||||
|
||||
Convey("DeleteProvisionedDashboard should delete it", func() {
|
||||
err := service.DeleteProvisionedDashboard(1, 1)
|
||||
@@ -306,15 +311,15 @@ type Result struct {
|
||||
deleteWasCalled bool
|
||||
}
|
||||
|
||||
func setupDeleteHandlers(provisioned bool) *Result {
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
if provisioned {
|
||||
cmd.Result = &models.DashboardProvisioning{}
|
||||
} else {
|
||||
cmd.Result = nil
|
||||
}
|
||||
return nil
|
||||
func setupDeleteHandlers(t *testing.T, fakeStore *fakeDashboardStore, provisioned bool) *Result {
|
||||
t.Helper()
|
||||
|
||||
t.Cleanup(func() {
|
||||
fakeStore.provisionedData = nil
|
||||
})
|
||||
if provisioned {
|
||||
fakeStore.provisionedData = &models.DashboardProvisioning{}
|
||||
}
|
||||
|
||||
result := &Result{}
|
||||
bus.AddHandler("test", func(cmd *models.DeleteDashboardCommand) error {
|
||||
@@ -326,3 +331,32 @@ func setupDeleteHandlers(provisioned bool) *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 (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
)
|
||||
|
||||
// FolderService service for operating on folders
|
||||
// FolderService is a service for operating on folders.
|
||||
type FolderService interface {
|
||||
GetFolders(limit int64) ([]*models.Folder, error)
|
||||
GetFolderByID(id int64) (*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
|
||||
DeleteFolder(uid string) (*models.Folder, error)
|
||||
MakeUserAdmin(orgID int64, userID, folderID int64, setViewAndEditPermissions bool) error
|
||||
}
|
||||
|
||||
// NewFolderService factory for creating a new folder service
|
||||
var NewFolderService = func(orgId int64, user *models.SignedInUser) FolderService {
|
||||
// NewFolderService is a factory for creating a new folder service.
|
||||
var NewFolderService = func(orgID int64, user *models.SignedInUser, store dashboards.Store) FolderService {
|
||||
return &dashboardServiceImpl{
|
||||
orgId: orgId,
|
||||
user: user,
|
||||
orgId: orgID,
|
||||
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) {
|
||||
query := models.GetDashboardQuery{OrgId: dr.orgId, Id: id}
|
||||
dashFolder, err := getFolder(query)
|
||||
|
||||
if err != nil {
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
@@ -93,8 +96,17 @@ func (dr *dashboardServiceImpl) GetFolderByUID(uid string) (*models.Folder, erro
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) error {
|
||||
dashFolder := cmd.GetDashboardModel(dr.orgId, dr.user.UserId)
|
||||
func (dr *dashboardServiceImpl) CreateFolder(title, uid string) (*models.Folder, error) {
|
||||
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{
|
||||
Dashboard: dashFolder,
|
||||
@@ -104,23 +116,21 @@ func (dr *dashboardServiceImpl) CreateFolder(cmd *models.CreateFolderCommand) er
|
||||
|
||||
saveDashboardCmd, err := dr.buildSaveDashboardCommand(dto, false, false)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
err = bus.Dispatch(saveDashboardCmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*saveDashboardCmd)
|
||||
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)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
return nil, toFolderError(err)
|
||||
}
|
||||
|
||||
cmd.Result = dashToFolder(dashFolder)
|
||||
|
||||
return nil
|
||||
return dashToFolder(dashFolder), nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
err = bus.Dispatch(saveDashboardCmd)
|
||||
dash, err := dr.dashboardStore.SaveDashboard(*saveDashboardCmd)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return toFolderError(err)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -15,8 +16,9 @@ import (
|
||||
func TestFolderService(t *testing.T) {
|
||||
Convey("Folder service tests", t, func() {
|
||||
service := dashboardServiceImpl{
|
||||
orgId: 1,
|
||||
user: &models.SignedInUser{UserId: 1},
|
||||
orgId: 1,
|
||||
user: &models.SignedInUser{UserId: 1},
|
||||
dashboardStore: &fakeDashboardStore{},
|
||||
}
|
||||
|
||||
Convey("Given user has no permissions", func() {
|
||||
@@ -28,32 +30,26 @@ func TestFolderService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
return models.ErrDashboardUpdateAccessDenied
|
||||
origStore := service.dashboardStore
|
||||
t.Cleanup(func() {
|
||||
service.dashboardStore = origStore
|
||||
})
|
||||
service.dashboardStore = &fakeDashboardStore{
|
||||
validationError: models.ErrDashboardUpdateAccessDenied,
|
||||
}
|
||||
|
||||
Convey("When get folder by id should return access denied error", func() {
|
||||
_, err := service.GetFolderByID(1)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Convey("When get folder by uid should return access denied error", func() {
|
||||
_, err := service.GetFolderByUID("uid")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
Convey("When creating folder should return access denied error", func() {
|
||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
_, err := service.CreateFolder("Folder", "")
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
@@ -62,7 +58,6 @@ func TestFolderService(t *testing.T) {
|
||||
Uid: "uid",
|
||||
Title: "Folder",
|
||||
})
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, models.ErrFolderAccessDenied)
|
||||
})
|
||||
|
||||
@@ -89,18 +84,14 @@ func TestFolderService(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
UpdateAlerting = func(store dashboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||
user *models.SignedInUser) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.SaveDashboardCommand) error {
|
||||
cmd.Result = dash
|
||||
@@ -120,9 +111,7 @@ func TestFolderService(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("When creating folder should not return access denied error", func() {
|
||||
err := service.CreateFolder(&models.CreateFolderCommand{
|
||||
Title: "Folder",
|
||||
})
|
||||
_, err := service.CreateFolder("Folder", "")
|
||||
So(err, ShouldBeNil)
|
||||
So(provisioningValidated, ShouldBeFalse)
|
||||
})
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"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 {
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, cmd.FolderID); err != nil {
|
||||
if err := lps.requirePermissionsOnFolder(c.SignedInUser, cmd.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := session.Insert(&libraryPanel); err != nil {
|
||||
@@ -108,12 +107,22 @@ func (lps *LibraryPanelService) createLibraryPanel(c *models.ReqContext, cmd cre
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := requirePermissionsOnFolder(user, panel.FolderID); err != nil {
|
||||
if err := lps.requirePermissionsOnFolder(user, panel.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -126,7 +135,7 @@ func connectDashboard(session *sqlstore.DBSession, dialect migrator.Dialect, use
|
||||
CreatedBy: user.UserId,
|
||||
}
|
||||
if _, err := session.Insert(&libraryPanelDashboard); err != nil {
|
||||
if dialect.IsUniqueConstraintViolation(err) {
|
||||
if lps.SQLStore.Dialect.IsUniqueConstraintViolation(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -134,15 +143,6 @@ func connectDashboard(session *sqlstore.DBSession, dialect migrator.Dialect, use
|
||||
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.
|
||||
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 {
|
||||
@@ -151,7 +151,7 @@ func (lps *LibraryPanelService) connectLibraryPanelsForDashboard(c *models.ReqCo
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func (lps *LibraryPanelService) deleteLibraryPanel(c *models.ReqContext, uid str
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||
if err := lps.requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||
if err := lps.requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ func (lps *LibraryPanelService) deleteLibraryPanelsInFolder(c *models.ReqContext
|
||||
}
|
||||
folderID := folderUIDs[0].ID
|
||||
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, folderID); err != nil {
|
||||
if err := lps.requirePermissionsOnFolder(c.SignedInUser, folderID); err != nil {
|
||||
return err
|
||||
}
|
||||
var dashIDs []struct {
|
||||
@@ -496,7 +496,8 @@ func (lps *LibraryPanelService) getLibraryPanelsForDashboardID(c *models.ReqCont
|
||||
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
|
||||
if toFolderID == -1 {
|
||||
toFolderID = fromFolderID
|
||||
@@ -504,13 +505,13 @@ func handleFolderIDPatches(panelToPatch *LibraryPanel, fromFolderID int64, toFol
|
||||
|
||||
// FolderID was provided in the PATCH request
|
||||
if toFolderID != -1 && toFolderID != fromFolderID {
|
||||
if err := requirePermissionsOnFolder(user, toFolderID); err != nil {
|
||||
if err := lps.requirePermissionsOnFolder(user, toFolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -551,7 +552,7 @@ func (lps *LibraryPanelService) patchLibraryPanel(c *models.ReqContext, cmd patc
|
||||
if cmd.Model == nil {
|
||||
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
|
||||
}
|
||||
if err := syncTitleWithName(&libraryPanel); err != nil {
|
||||
|
||||
@@ -10,7 +10,7 @@ func isGeneralFolder(folderID int64) bool {
|
||||
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) {
|
||||
return nil
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func requirePermissionsOnFolder(user *models.SignedInUser, folderID int64) error
|
||||
return models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
s := dashboards.NewFolderService(user.OrgId, user)
|
||||
s := dashboards.NewFolderService(user.OrgId, user, lps.SQLStore)
|
||||
folder, err := s.GetFolderByID(folderID)
|
||||
if err != nil {
|
||||
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",
|
||||
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)})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
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)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestPatchLibraryPanel(t *testing.T) {
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: newFolder.Id,
|
||||
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",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
||||
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: newFolder.Id,
|
||||
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",
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
@@ -68,7 +68,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
||||
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),
|
||||
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
|
||||
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
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
|
||||
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
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
|
||||
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
||||
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||
dashboard := createDashboard(t, sc.sqlStore, sc.user, "Some Folder Dash", folder.Id)
|
||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
||||
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
|
||||
dashboard := createDashboard(t, sc.sqlStore, sc.user, "Some Folder Dash", folder.Id)
|
||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
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),
|
||||
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")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -329,7 +329,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
var results []libraryPanel
|
||||
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))
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
@@ -401,8 +401,8 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
for i, folderCase := range folderCases {
|
||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
||||
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
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)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
@@ -434,7 +434,7 @@ func TestLibraryPanelPermissions(t *testing.T) {
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
var results []libraryPanel
|
||||
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))
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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/registry"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@@ -731,6 +731,7 @@ type scenarioContext struct {
|
||||
user models.SignedInUser
|
||||
folder *models.Folder
|
||||
initialResult libraryPanelResult
|
||||
sqlStore *sqlstore.SQLStore
|
||||
}
|
||||
|
||||
type folderACLItem struct {
|
||||
@@ -738,7 +739,8 @@ type folderACLItem struct {
|
||||
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.FolderId = folderID
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
@@ -748,53 +750,47 @@ func createDashboard(t *testing.T, user models.SignedInUser, title string, folde
|
||||
User: &user,
|
||||
Overwrite: false,
|
||||
}
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
origUpdateAlerting := dashboards.UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
dashboards.UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
dashboards.UpdateAlerting = func(store dboards.Store, orgID int64, dashboard *models.Dashboard,
|
||||
user *models.SignedInUser) error {
|
||||
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)
|
||||
|
||||
return dashboard
|
||||
}
|
||||
|
||||
func createFolderWithACL(t *testing.T, title string, user models.SignedInUser, items []folderACLItem) *models.Folder {
|
||||
s := dashboards.NewFolderService(user.OrgId, &user)
|
||||
folderCmd := models.CreateFolderCommand{
|
||||
Uid: title,
|
||||
Title: title,
|
||||
}
|
||||
err := s.CreateFolder(&folderCmd)
|
||||
func createFolderWithACL(t *testing.T, sqlStore *sqlstore.SQLStore, title string, user models.SignedInUser,
|
||||
items []folderACLItem) *models.Folder {
|
||||
t.Helper()
|
||||
|
||||
s := dashboards.NewFolderService(user.OrgId, &user, sqlStore)
|
||||
t.Logf("Creating folder with title and UID %q", title)
|
||||
folder, err := s.CreateFolder(title, title)
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := models.UpdateDashboardAclCommand{
|
||||
DashboardID: folderID,
|
||||
}
|
||||
var aclItems []*models.DashboardAcl
|
||||
for _, item := range items {
|
||||
role := item.roleType
|
||||
permission := item.permission
|
||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
||||
aclItems = append(aclItems, &models.DashboardAcl{
|
||||
DashboardID: folderID,
|
||||
Role: &role,
|
||||
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)
|
||||
}
|
||||
|
||||
func validateAndUnMarshalResponse(t *testing.T, resp response.Response) libraryPanelResult {
|
||||
t.Helper()
|
||||
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
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)) {
|
||||
t.Helper()
|
||||
|
||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel")
|
||||
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
|
||||
// what to expect in the tests
|
||||
// In the real world these are identical
|
||||
cmd := &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: "user.in.db@test.com",
|
||||
Name: "User In DB",
|
||||
Login: UserInDbName,
|
||||
}
|
||||
err := sqlstore.CreateUser(context.Background(), cmd)
|
||||
_, err := sqlStore.CreateUser(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
sc := scenarioContext{
|
||||
user: user,
|
||||
ctx: &ctx,
|
||||
service: &service,
|
||||
user: user,
|
||||
ctx: &ctx,
|
||||
service: &service,
|
||||
sqlStore: sqlStore,
|
||||
reqContext: &models.ReqContext{
|
||||
Context: &ctx,
|
||||
SignedInUser: &user,
|
||||
},
|
||||
}
|
||||
|
||||
sc.folder = createFolderWithACL(t, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||
sc.folder = createFolderWithACL(t, sc.sqlStore, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||
|
||||
fn(t, sc)
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"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/services/live/features"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -43,6 +43,7 @@ type GrafanaLive struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
RouteRegister routing.RouteRegister `inject:""`
|
||||
LogsService *cloudwatch.LogsService `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
node *centrifuge.Node
|
||||
|
||||
// The websocket handler
|
||||
@@ -222,8 +223,8 @@ func (g *GrafanaLive) GetChannelHandlerFactory(scope string, name string) (model
|
||||
}, nil
|
||||
}
|
||||
|
||||
p, ok := manager.Plugins[name]
|
||||
if ok {
|
||||
p := g.PluginManager.GetPlugin(name)
|
||||
if p != nil {
|
||||
h := &PluginHandler{
|
||||
Plugin: p,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -21,6 +22,7 @@ var (
|
||||
type TeamSyncFunc func(user *models.User, externalUser *models.ExternalUserInfo) error
|
||||
|
||||
type LoginService struct {
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
Bus bus.Bus `inject:""`
|
||||
QuotaService *quota.QuotaService `inject:""`
|
||||
TeamSync TeamSyncFunc
|
||||
@@ -107,7 +109,7 @@ func (ls *LoginService) UpsertUser(cmd *models.UpsertUserCommand) error {
|
||||
|
||||
// Sync isGrafanaAdmin permission
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
@@ -22,7 +24,7 @@ type DashboardProvisioner interface {
|
||||
}
|
||||
|
||||
// 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.
|
||||
type Provisioner struct {
|
||||
@@ -32,16 +34,15 @@ type Provisioner struct {
|
||||
}
|
||||
|
||||
// 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")
|
||||
cfgReader := &configReader{path: configDirectory, log: logger}
|
||||
configs, err := cfgReader.readConfig()
|
||||
|
||||
if err != nil {
|
||||
return nil, errutil.Wrap("Failed to read dashboards config", err)
|
||||
}
|
||||
|
||||
fileReaders, err := getFileReaders(configs, logger)
|
||||
fileReaders, err := getFileReaders(configs, logger, store)
|
||||
if err != nil {
|
||||
return nil, errutil.Wrap("Failed to initialize file readers", err)
|
||||
}
|
||||
@@ -115,13 +116,14 @@ func (provider *Provisioner) GetAllowUIUpdatesFromConfig(name string) bool {
|
||||
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
|
||||
|
||||
for _, config := range configs {
|
||||
switch config.Type {
|
||||
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 {
|
||||
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/components/simplejson"
|
||||
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@@ -35,7 +36,7 @@ type FileReader struct {
|
||||
}
|
||||
|
||||
// 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
|
||||
path, ok := cfg.Options["path"].(string)
|
||||
if !ok {
|
||||
@@ -56,7 +57,7 @@ func NewDashboardFileReader(cfg *config, log log.Logger) (*FileReader, error) {
|
||||
Cfg: cfg,
|
||||
Path: path,
|
||||
log: log,
|
||||
dashboardProvisioningService: dashboards.NewProvisioningService(),
|
||||
dashboardProvisioningService: dashboards.NewProvisioningService(store),
|
||||
FoldersFromFilesStructure: foldersFromFilesStructure,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestProvisionedSymlinkedFolder(t *testing.T) {
|
||||
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)
|
||||
|
||||
want, err := filepath.Abs(containingID)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
dboards "github.com/grafana/grafana/pkg/dashboards"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -41,14 +42,14 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
||||
|
||||
Convey("using path parameter", func() {
|
||||
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(reader.Path, ShouldNotEqual, "")
|
||||
})
|
||||
|
||||
Convey("using folder as options", func() {
|
||||
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(reader.Path, ShouldNotEqual, "")
|
||||
})
|
||||
@@ -56,7 +57,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
||||
Convey("using foldersFromFilesStructure as options", func() {
|
||||
cfg.Options["path"] = foldersFromFilesStructure
|
||||
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(reader.Path, ShouldNotEqual, "")
|
||||
})
|
||||
@@ -68,7 +69,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
||||
}
|
||||
|
||||
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(reader.Path, ShouldEqual, fullPath)
|
||||
@@ -77,7 +78,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
||||
|
||||
Convey("using relative path", func() {
|
||||
cfg.Options["folder"] = defaultDashboards
|
||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"), nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
resolvedPath := reader.resolvedPath()
|
||||
@@ -111,7 +112,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
cfg.Options["path"] = defaultDashboards
|
||||
cfg.Folder = "Team A"
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader.walkDisk()
|
||||
@@ -142,7 +143,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
Slug: "grafana",
|
||||
})
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader.walkDisk()
|
||||
@@ -154,7 +155,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
Convey("Overrides id from dashboard.json files", func() {
|
||||
cfg.Options["path"] = containingID
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader.walkDisk()
|
||||
@@ -167,7 +168,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
cfg.Options["path"] = foldersFromFilesStructure
|
||||
cfg.Options["foldersFromFilesStructure"] = true
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader.walkDisk()
|
||||
@@ -211,14 +212,14 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
Folder: "",
|
||||
}
|
||||
|
||||
_, err := NewDashboardFileReader(cfg, logger)
|
||||
_, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Broken dashboards should not cause error", func() {
|
||||
cfg.Options["path"] = brokenDashboards
|
||||
|
||||
_, err := NewDashboardFileReader(cfg, logger)
|
||||
_, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
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}}
|
||||
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)
|
||||
|
||||
err = reader1.walkDisk()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger)
|
||||
reader2, err := NewDashboardFileReader(cfg2, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader2.walkDisk()
|
||||
@@ -336,7 +337,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
Convey("Missing dashboard should be unprovisioned if DisableDeletion = true", func() {
|
||||
cfg.DisableDeletion = true
|
||||
|
||||
reader, err := NewDashboardFileReader(cfg, logger)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader.walkDisk()
|
||||
@@ -347,7 +348,7 @@ func TestDashboardFileReader(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Missing dashboard should be deleted if DisableDeletion = false", func() {
|
||||
reader, err := NewDashboardFileReader(cfg, logger)
|
||||
reader, err := NewDashboardFileReader(cfg, logger, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = reader.walkDisk()
|
||||
@@ -395,7 +396,7 @@ func mockDashboardProvisioningService() *fakeDashboardProvisioningService {
|
||||
mock := fakeDashboardProvisioningService{
|
||||
provisioned: map[string][]*models.DashboardProvisioning{},
|
||||
}
|
||||
dashboards.NewProvisioningService = func() dashboards.DashboardProvisioningService {
|
||||
dashboards.NewProvisioningService = func(dboards.Store) dashboards.DashboardProvisioningService {
|
||||
return &mock
|
||||
}
|
||||
return &mock
|
||||
|
||||
@@ -55,7 +55,7 @@ func (cr *configReaderImpl) readConfig(path string) ([]*pluginsAsConfig, error)
|
||||
|
||||
checkOrgIDAndOrgName(apps)
|
||||
|
||||
err = validatePluginsConfig(apps)
|
||||
err = cr.validatePluginsConfig(apps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -105,7 +105,7 @@ func validateRequiredField(apps []*pluginsAsConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePluginsConfig(apps []*pluginsAsConfig) error {
|
||||
func (cr *configReaderImpl) validatePluginsConfig(apps []*pluginsAsConfig) error {
|
||||
for i := range apps {
|
||||
if apps[i].Apps == nil {
|
||||
continue
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
"sync"
|
||||
|
||||
"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/services/provisioning/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/notifiers"
|
||||
"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/util/errutil"
|
||||
)
|
||||
@@ -27,10 +29,8 @@ type ProvisioningService interface {
|
||||
func init() {
|
||||
registry.Register(®istry.Descriptor{
|
||||
Name: "ProvisioningService",
|
||||
Instance: NewProvisioningServiceImpl(
|
||||
func(path string) (dashboards.DashboardProvisioner, error) {
|
||||
return dashboards.New(path)
|
||||
},
|
||||
Instance: newProvisioningServiceImpl(
|
||||
dashboards.New,
|
||||
notifiers.Provision,
|
||||
datasources.Provision,
|
||||
plugins.Provision,
|
||||
@@ -39,7 +39,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func NewProvisioningServiceImpl(
|
||||
func newProvisioningServiceImpl(
|
||||
newDashboardProvisioner dashboards.DashboardProvisionerFactory,
|
||||
provisionNotifiers func(string) error,
|
||||
provisionDatasources func(string) error,
|
||||
@@ -55,7 +55,9 @@ func NewProvisioningServiceImpl(
|
||||
}
|
||||
|
||||
type provisioningServiceImpl struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
RequestHandler plugifaces.DataRequestHandler `inject:""`
|
||||
SQLStore *sqlstore.SQLStore `inject:""`
|
||||
log log.Logger
|
||||
pollingCtxCancel context.CancelFunc
|
||||
newDashboardProvisioner dashboards.DashboardProvisionerFactory
|
||||
@@ -134,7 +136,7 @@ func (ps *provisioningServiceImpl) ProvisionNotifications() error {
|
||||
|
||||
func (ps *provisioningServiceImpl) ProvisionDashboards() error {
|
||||
dashboardPath := filepath.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
||||
dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath)
|
||||
dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath, ps.SQLStore, ps.RequestHandler)
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to create provisioner", err)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"testing"
|
||||
"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/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -90,8 +92,8 @@ func setup() *serviceTestStruct {
|
||||
pollChangesChannel <- ctx
|
||||
}
|
||||
|
||||
serviceTest.service = NewProvisioningServiceImpl(
|
||||
func(path string) (dashboards.DashboardProvisioner, error) {
|
||||
serviceTest.service = newProvisioningServiceImpl(
|
||||
func(string, dboards.Store, plugifaces.DataRequestHandler) (dashboards.DashboardProvisioner, error) {
|
||||
return serviceTest.mock, nil
|
||||
},
|
||||
nil,
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -50,6 +49,7 @@ type RenderingService struct {
|
||||
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
RemoteCacheService *remotecache.RemoteCache `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
}
|
||||
|
||||
func (rs *RenderingService) Init() error {
|
||||
@@ -87,7 +87,7 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
||||
|
||||
if rs.pluginAvailable() {
|
||||
rs.log = rs.log.New("renderer", "plugin")
|
||||
rs.pluginInfo = manager.Renderer
|
||||
rs.pluginInfo = rs.PluginManager.Renderer()
|
||||
|
||||
if err := rs.startPlugin(ctx); err != nil {
|
||||
return err
|
||||
@@ -107,7 +107,7 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (rs *RenderingService) pluginAvailable() bool {
|
||||
return manager.Renderer != nil
|
||||
return rs.PluginManager.Renderer() != nil
|
||||
}
|
||||
|
||||
func (rs *RenderingService) remoteAvailable() bool {
|
||||
|
||||
@@ -2,6 +2,7 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -166,18 +167,18 @@ func deleteAlertDefinition(dashboardId int64, sess *DBSession) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SaveAlerts(cmd *models.SaveAlertsCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
|
||||
func (ss *SQLStore) SaveAlerts(dashID int64, alerts []*models.Alert) error {
|
||||
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateAlerts(existingAlerts, cmd, sess); err != nil {
|
||||
if err := updateAlerts(existingAlerts, alerts, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := deleteMissingAlerts(existingAlerts, cmd, sess); err != nil {
|
||||
if err := deleteMissingAlerts(existingAlerts, alerts, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -185,8 +186,27 @@ func SaveAlerts(cmd *models.SaveAlertsCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func updateAlerts(existingAlerts []*models.Alert, cmd *models.SaveAlertsCommand, sess *DBSession) error {
|
||||
for _, alert := range cmd.Alerts {
|
||||
func SaveAlerts(cmd *models.SaveAlertsCommand) error {
|
||||
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
|
||||
var alertToUpdate *models.Alert
|
||||
|
||||
@@ -245,11 +265,11 @@ func updateAlerts(existingAlerts []*models.Alert, cmd *models.SaveAlertsCommand,
|
||||
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 {
|
||||
missing := true
|
||||
|
||||
for _, k := range cmd.Alerts {
|
||||
for _, k := range existingAlerts {
|
||||
if missingAlert.PanelId == k.PanelId {
|
||||
missing = false
|
||||
break
|
||||
|
||||
@@ -30,10 +30,11 @@ func TestAlertingDataAccess(t *testing.T) {
|
||||
defer resetTimeNow()
|
||||
|
||||
Convey("Testing Alerting data access", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
testDash := insertTestDashboard(t, "dashboard with alerts", 1, 0, false, "alert")
|
||||
evalData, _ := simplejson.NewJson([]byte(`{"test": "test"}`))
|
||||
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
|
||||
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
|
||||
So(err, ShouldBeNil)
|
||||
items := []*models.Alert{
|
||||
{
|
||||
PanelId: 1,
|
||||
@@ -54,7 +55,7 @@ func TestAlertingDataAccess(t *testing.T) {
|
||||
UserId: 1,
|
||||
}
|
||||
|
||||
err := SaveAlerts(&cmd)
|
||||
err = SaveAlerts(&cmd)
|
||||
|
||||
Convey("Can create one alert", func() {
|
||||
So(err, ShouldBeNil)
|
||||
@@ -271,10 +272,11 @@ func TestPausingAlerts(t *testing.T) {
|
||||
defer resetTimeNow()
|
||||
|
||||
Convey("Given an alert", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
testDash := insertTestDashboard(t, "dashboard with alerts", 1, 0, false, "alert")
|
||||
alert, _ := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New())
|
||||
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
|
||||
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
stateDateBeforePause := alert.NewStateDate
|
||||
stateDateAfterPause := stateDateBeforePause
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -24,7 +26,6 @@ var shadowSearchCounter = prometheus.NewCounterVec(
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", SaveDashboard)
|
||||
bus.AddHandler("sql", GetDashboard)
|
||||
bus.AddHandler("sql", GetDashboards)
|
||||
bus.AddHandler("sql", DeleteDashboard)
|
||||
@@ -35,7 +36,6 @@ func init() {
|
||||
bus.AddHandler("sql", GetDashboardsByPluginId)
|
||||
bus.AddHandler("sql", GetDashboardPermissionsForUser)
|
||||
bus.AddHandler("sql", GetDashboardsBySlug)
|
||||
bus.AddHandler("sql", ValidateDashboardBeforeSave)
|
||||
bus.AddHandler("sql", HasEditPermissionInFolders)
|
||||
bus.AddHandler("sql", HasAdminPermissionInFolders)
|
||||
|
||||
@@ -44,10 +44,11 @@ func init() {
|
||||
|
||||
var generateNewUid func() string = util.GenerateShortUID
|
||||
|
||||
func SaveDashboard(cmd *models.SaveDashboardCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
return saveDashboard(sess, cmd)
|
||||
func (ss *SQLStore) SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) {
|
||||
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
return saveDashboard(sess, &cmd)
|
||||
})
|
||||
return cmd.Result, err
|
||||
}
|
||||
|
||||
func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
|
||||
@@ -162,7 +163,7 @@ func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
|
||||
|
||||
cmd.Result = dash
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
|
||||
@@ -182,6 +183,26 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
|
||||
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 {
|
||||
if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {
|
||||
return models.ErrDashboardIdentifierNotSet
|
||||
@@ -573,20 +594,20 @@ func GetDashboardUIDById(query *models.GetDashboardRefByIdQuery) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
|
||||
dash := cmd.Dashboard
|
||||
|
||||
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, dash *models.Dashboard, overwrite bool) (bool, error) {
|
||||
dashWithIdExists := false
|
||||
isParentFolderChanged := false
|
||||
var existingById models.Dashboard
|
||||
|
||||
if dash.Id > 0 {
|
||||
var err error
|
||||
dashWithIdExists, err = sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existingById)
|
||||
if err != nil {
|
||||
return err
|
||||
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by ID failed: %w", err)
|
||||
}
|
||||
|
||||
if !dashWithIdExists {
|
||||
return models.ErrDashboardNotFound
|
||||
return isParentFolderChanged, models.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
if dash.Uid == "" {
|
||||
@@ -598,30 +619,32 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.Validat
|
||||
var existingByUid models.Dashboard
|
||||
|
||||
if dash.Uid != "" {
|
||||
var err error
|
||||
dashWithUidExists, err = sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&existingByUid)
|
||||
if err != nil {
|
||||
return err
|
||||
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by UID failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if dash.FolderId > 0 {
|
||||
var existingFolder models.Dashboard
|
||||
folderExists, folderErr := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId, dialect.BooleanStr(true)).Get(&existingFolder)
|
||||
if folderErr != nil {
|
||||
return folderErr
|
||||
folderExists, err := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId,
|
||||
dialect.BooleanStr(true)).Get(&existingFolder)
|
||||
if err != nil {
|
||||
return isParentFolderChanged, fmt.Errorf("SQL query for folder failed: %w", err)
|
||||
}
|
||||
|
||||
if !folderExists {
|
||||
return models.ErrDashboardFolderNotFound
|
||||
return isParentFolderChanged, models.ErrDashboardFolderNotFound
|
||||
}
|
||||
}
|
||||
|
||||
if !dashWithIdExists && !dashWithUidExists {
|
||||
return nil
|
||||
return isParentFolderChanged, nil
|
||||
}
|
||||
|
||||
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
|
||||
return models.ErrDashboardWithSameUIDExists
|
||||
return isParentFolderChanged, models.ErrDashboardWithSameUIDExists
|
||||
}
|
||||
|
||||
existing := existingById
|
||||
@@ -632,84 +655,92 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.Validat
|
||||
existing = existingByUid
|
||||
|
||||
if !dash.IsFolder {
|
||||
cmd.Result.IsParentFolderChanged = true
|
||||
isParentFolderChanged = true
|
||||
}
|
||||
}
|
||||
|
||||
if (existing.IsFolder && !dash.IsFolder) ||
|
||||
(!existing.IsFolder && dash.IsFolder) {
|
||||
return models.ErrDashboardTypeMismatch
|
||||
return isParentFolderChanged, models.ErrDashboardTypeMismatch
|
||||
}
|
||||
|
||||
if !dash.IsFolder && dash.FolderId != existing.FolderId {
|
||||
cmd.Result.IsParentFolderChanged = true
|
||||
isParentFolderChanged = true
|
||||
}
|
||||
|
||||
// check for is someone else has written in between
|
||||
if dash.Version != existing.Version {
|
||||
if cmd.Overwrite {
|
||||
if overwrite {
|
||||
dash.SetVersion(existing.Version)
|
||||
} else {
|
||||
return models.ErrDashboardVersionMismatch
|
||||
return isParentFolderChanged, models.ErrDashboardVersionMismatch
|
||||
}
|
||||
}
|
||||
|
||||
// do not allow plugin dashboard updates without overwrite flag
|
||||
if existing.PluginId != "" && !cmd.Overwrite {
|
||||
return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
||||
if existing.PluginId != "" && !overwrite {
|
||||
return isParentFolderChanged, models.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
||||
}
|
||||
|
||||
return nil
|
||||
return isParentFolderChanged, nil
|
||||
}
|
||||
|
||||
func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
dash := cmd.Dashboard
|
||||
func getExistingDashboardByTitleAndFolder(sess *DBSession, dash *models.Dashboard, overwrite,
|
||||
isParentFolderChanged bool) (bool, error) {
|
||||
var existing models.Dashboard
|
||||
|
||||
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)
|
||||
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)
|
||||
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 existing.IsFolder && !dash.IsFolder {
|
||||
return models.ErrDashboardWithSameNameAsFolder
|
||||
return isParentFolderChanged, models.ErrDashboardWithSameNameAsFolder
|
||||
}
|
||||
|
||||
if !existing.IsFolder && dash.IsFolder {
|
||||
return models.ErrDashboardFolderWithSameNameAsDashboard
|
||||
return isParentFolderChanged, models.ErrDashboardFolderWithSameNameAsDashboard
|
||||
}
|
||||
|
||||
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.SetUid(existing.Uid)
|
||||
dash.SetVersion(existing.Version)
|
||||
} else {
|
||||
return models.ErrDashboardWithSameNameInFolderExists
|
||||
return isParentFolderChanged, models.ErrDashboardWithSameNameInFolderExists
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return isParentFolderChanged, nil
|
||||
}
|
||||
|
||||
func ValidateDashboardBeforeSave(cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
|
||||
func (ss *SQLStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (bool, error) {
|
||||
isParentFolderChanged := false
|
||||
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
var err error
|
||||
isParentFolderChanged, err = getExistingDashboardByIdOrUidForUpdate(sess, dashboard, overwrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = getExistingDashboardByTitleAndFolder(sess, cmd); err != nil {
|
||||
isParentFolderChanged, err = getExistingDashboardByTitleAndFolder(sess, dashboard, overwrite,
|
||||
isParentFolderChanged)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isParentFolderChanged, nil
|
||||
}
|
||||
|
||||
func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) error {
|
||||
@@ -719,7 +750,8 @@ func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) e
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
type folderCount struct {
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", UpdateDashboardAcl)
|
||||
bus.AddHandler("sql", GetDashboardAclInfoList)
|
||||
}
|
||||
|
||||
func UpdateDashboardAcl(cmd *models.UpdateDashboardAclCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
func (ss *SQLStore) UpdateDashboardACL(dashboardID int64, items []*models.DashboardAcl) error {
|
||||
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
// 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 {
|
||||
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()) {
|
||||
return models.ErrDashboardAclInfoMissing
|
||||
}
|
||||
@@ -35,7 +37,7 @@ func UpdateDashboardAcl(cmd *models.UpdateDashboardAclCommand) error {
|
||||
|
||||
// Update dashboard HasAcl flag
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,14 +11,15 @@ import (
|
||||
|
||||
func TestDashboardAclDataAccess(t *testing.T) {
|
||||
Convey("Testing DB", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
Convey("Given a dashboard folder and a user", func() {
|
||||
currentUser := createUser(t, "viewer", "Viewer", false)
|
||||
savedFolder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
childDash := insertTestDashboard(t, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||
savedFolder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "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() {
|
||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||
OrgID: 1,
|
||||
DashboardID: savedFolder.Id,
|
||||
Permission: models.PERMISSION_EDIT,
|
||||
@@ -61,10 +62,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given dashboard folder with removed default permissions", func() {
|
||||
err := UpdateDashboardAcl(&models.UpdateDashboardAclCommand{
|
||||
DashboardID: savedFolder.Id,
|
||||
Items: []*models.DashboardAcl{},
|
||||
})
|
||||
err := sqlStore.UpdateDashboardACL(savedFolder.Id, nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
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() {
|
||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||
OrgID: 1,
|
||||
UserID: currentUser.Id,
|
||||
DashboardID: savedFolder.Id,
|
||||
@@ -97,7 +95,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given child dashboard permission", func() {
|
||||
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
|
||||
OrgID: 1,
|
||||
UserID: currentUser.Id,
|
||||
DashboardID: childDash.Id,
|
||||
@@ -121,7 +119,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
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,
|
||||
UserID: currentUser.Id,
|
||||
DashboardID: childDash.Id,
|
||||
@@ -149,7 +147,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
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,
|
||||
UserID: currentUser.Id,
|
||||
DashboardID: savedFolder.Id,
|
||||
@@ -169,7 +167,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
So(q1.Result[0].UserEmail, ShouldEqual, currentUser.Email)
|
||||
|
||||
Convey("Should be able to delete an existing permission", func() {
|
||||
err := testHelperUpdateDashboardAcl(savedFolder.Id)
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
q3 := &models.GetDashboardAclInfoListQuery{DashboardID: savedFolder.Id, OrgID: 1}
|
||||
@@ -180,14 +178,13 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given a team", func() {
|
||||
group1 := models.CreateTeamCommand{Name: "group1 name", OrgId: 1}
|
||||
err := CreateTeam(&group1)
|
||||
team1, err := sqlStore.CreateTeam("group1 name", "", 1)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
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,
|
||||
TeamID: group1.Result.Id,
|
||||
TeamID: team1.Id,
|
||||
DashboardID: savedFolder.Id,
|
||||
Permission: models.PERMISSION_EDIT,
|
||||
})
|
||||
@@ -198,13 +195,13 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
||||
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() {
|
||||
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
|
||||
OrgID: 1,
|
||||
TeamID: group1.Result.Id,
|
||||
TeamID: team1.Id,
|
||||
DashboardID: savedFolder.Id,
|
||||
Permission: models.PERMISSION_ADMIN,
|
||||
})
|
||||
@@ -216,7 +213,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
|
||||
So(len(q3.Result), ShouldEqual, 1)
|
||||
So(q3.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
|
||||
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/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
)
|
||||
|
||||
func TestDashboardFolderDataAccess(t *testing.T) {
|
||||
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() {
|
||||
folder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
dashInRoot := insertTestDashboard(t, "test dash 67", 1, 0, false, "prod", "webapp")
|
||||
childDash := insertTestDashboard(t, "test dash 23", 1, folder.Id, false, "prod", "webapp")
|
||||
insertTestDashboard(t, "test dash 45", 1, folder.Id, false, "prod")
|
||||
folder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
dashInRoot := insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod", "webapp")
|
||||
childDash := insertTestDashboard(t, sqlStore, "test dash 23", 1, folder.Id, false, "prod", "webapp")
|
||||
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("should return all dashboards", func() {
|
||||
@@ -40,7 +41,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
||||
|
||||
Convey("and acl is set for dashboard folder", func() {
|
||||
var otherUser int64 = 999
|
||||
err := testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id, models.DashboardAcl{
|
||||
DashboardID: folder.Id,
|
||||
OrgID: 1,
|
||||
UserID: otherUser,
|
||||
@@ -61,7 +62,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
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,
|
||||
})
|
||||
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() {
|
||||
var otherUser int64 = 999
|
||||
err := testHelperUpdateDashboardAcl(folder.Id)
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id)
|
||||
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,
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
@@ -120,7 +121,9 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
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() {
|
||||
folder1 := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod")
|
||||
folder2 := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod")
|
||||
dashInRoot := insertTestDashboard(t, "test dash 67", 1, 0, false, "prod")
|
||||
childDash1 := insertTestDashboard(t, "child dash 1", 1, folder1.Id, false, "prod")
|
||||
childDash2 := insertTestDashboard(t, "child dash 2", 1, folder2.Id, false, "prod")
|
||||
folder1 := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod")
|
||||
folder2 := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod")
|
||||
dashInRoot := insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod")
|
||||
childDash1 := insertTestDashboard(t, sqlStore, "child dash 1", 1, folder1.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
|
||||
|
||||
Convey("and one folder is expanded, the other collapsed", 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)
|
||||
So(err, ShouldBeNil)
|
||||
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() {
|
||||
var otherUser int64 = 999
|
||||
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
|
||||
const otherUser int64 = 999
|
||||
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
|
||||
DashboardID: folder1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
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() {
|
||||
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() {
|
||||
moveDashboard(1, childDash1.Data, folder2.Id)
|
||||
moveDashboard(t, sqlStore, 1, childDash1.Data, folder2.Id)
|
||||
|
||||
Convey("should return folder without acl and its children", func() {
|
||||
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() {
|
||||
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,
|
||||
})
|
||||
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() {
|
||||
query := &search.FindPersistedDashboardsQuery{
|
||||
@@ -246,13 +255,13 @@ func TestDashboardFolderDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given two dashboard folders", func() {
|
||||
folder1 := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod")
|
||||
folder2 := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod")
|
||||
insertTestDashboard(t, "folder in another org", 2, 0, true, "prod")
|
||||
folder1 := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod")
|
||||
folder2 := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod")
|
||||
insertTestDashboard(t, sqlStore, "folder in another org", 2, 0, true, "prod")
|
||||
|
||||
adminUser := createUser(t, "admin", "Admin", true)
|
||||
editorUser := createUser(t, "editor", "Editor", false)
|
||||
viewerUser := createUser(t, "viewer", "Viewer", false)
|
||||
adminUser := createUser(t, sqlStore, "admin", "Admin", true)
|
||||
editorUser := createUser(t, sqlStore, "editor", "Editor", false)
|
||||
viewerUser := createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||
|
||||
Convey("Admin users", 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() {
|
||||
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,
|
||||
})
|
||||
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() {
|
||||
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,
|
||||
})
|
||||
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() {
|
||||
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,
|
||||
})
|
||||
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() {
|
||||
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,
|
||||
})
|
||||
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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -8,9 +9,6 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetProvisionedDashboardDataQuery)
|
||||
bus.AddHandler("sql", SaveProvisionedDashboard)
|
||||
bus.AddHandler("sql", GetProvisionedDataByDashboardId)
|
||||
bus.AddHandler("sql", UnprovisionDashboard)
|
||||
bus.AddHandler("sql", DeleteOrphanedProvisionedDashboards)
|
||||
}
|
||||
@@ -22,64 +20,62 @@ type DashboardExtras struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
result := &models.DashboardProvisioning{}
|
||||
|
||||
exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
|
||||
func (ss *SQLStore) GetProvisionedDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) {
|
||||
var data models.DashboardProvisioning
|
||||
exists, err := x.Where("dashboard_id = ?", dashboardID).Get(&data)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
cmd.Result = result
|
||||
if exists {
|
||||
return &data, nil
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func SaveProvisionedDashboard(cmd *models.SaveProvisionedDashboardCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
err := saveDashboard(sess, cmd.DashboardCmd)
|
||||
if err != nil {
|
||||
func (ss *SQLStore) SaveProvisionedDashboard(cmd models.SaveDashboardCommand,
|
||||
provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
|
||||
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
if err := saveDashboard(sess, &cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = cmd.DashboardCmd.Result
|
||||
if cmd.DashboardProvisioning.Updated == 0 {
|
||||
cmd.DashboardProvisioning.Updated = cmd.Result.Updated.Unix()
|
||||
if provisioning.Updated == 0 {
|
||||
provisioning.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{}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Id = result.Id
|
||||
cmd.DashboardId = dashboard.Id
|
||||
provisioning.Id = result.Id
|
||||
provisioning.DashboardId = dashboard.Id
|
||||
|
||||
if exist {
|
||||
_, err = sess.ID(result.Id).Update(cmd)
|
||||
_, err = sess.ID(result.Id).Update(provisioning)
|
||||
} else {
|
||||
_, err = sess.Insert(cmd)
|
||||
_, err = sess.Insert(provisioning)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetProvisionedDashboardDataQuery(cmd *models.GetProvisionedDashboardDataQuery) error {
|
||||
func (ss *SQLStore) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
|
||||
var result []*models.DashboardProvisioning
|
||||
|
||||
if err := x.Where("name = ?", cmd.Name).Find(&result); err != nil {
|
||||
return err
|
||||
if err := ss.engine.Where("name = ?", name).Find(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd.Result = result
|
||||
return nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Convey("Testing Dashboard provisioning", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
folderCmd := &models.SaveDashboardCommand{
|
||||
folderCmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
FolderId: 0,
|
||||
IsFolder: true,
|
||||
@@ -25,13 +25,13 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(folderCmd)
|
||||
dash, err := sqlStore.SaveDashboard(folderCmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
saveDashboardCmd := &models.SaveDashboardCommand{
|
||||
saveDashboardCmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
IsFolder: false,
|
||||
FolderId: folderCmd.Result.Id,
|
||||
FolderId: dash.Id,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": "test dashboard",
|
||||
@@ -41,43 +41,38 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
Convey("Saving dashboards with provisioning meta data", func() {
|
||||
now := time.Now()
|
||||
|
||||
cmd := &models.SaveProvisionedDashboardCommand{
|
||||
DashboardCmd: saveDashboardCmd,
|
||||
DashboardProvisioning: &models.DashboardProvisioning{
|
||||
Name: "default",
|
||||
ExternalId: "/var/grafana.json",
|
||||
Updated: now.Unix(),
|
||||
},
|
||||
provisioning := &models.DashboardProvisioning{
|
||||
Name: "default",
|
||||
ExternalId: "/var/grafana.json",
|
||||
Updated: now.Unix(),
|
||||
}
|
||||
|
||||
err := SaveProvisionedDashboard(cmd)
|
||||
dash, err := sqlStore.SaveProvisionedDashboard(saveDashboardCmd, provisioning)
|
||||
So(err, ShouldBeNil)
|
||||
So(cmd.Result, ShouldNotBeNil)
|
||||
So(cmd.Result.Id, ShouldNotEqual, 0)
|
||||
dashId := cmd.Result.Id
|
||||
So(dash, ShouldNotBeNil)
|
||||
So(dash.Id, ShouldNotEqual, 0)
|
||||
dashId := dash.Id
|
||||
|
||||
Convey("Deleting orphaned provisioned dashboards", func() {
|
||||
anotherCmd := &models.SaveProvisionedDashboardCommand{
|
||||
DashboardCmd: &models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
IsFolder: false,
|
||||
FolderId: folderCmd.Result.Id,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": "another_dashboard",
|
||||
}),
|
||||
},
|
||||
DashboardProvisioning: &models.DashboardProvisioning{
|
||||
Name: "another_reader",
|
||||
ExternalId: "/var/grafana.json",
|
||||
Updated: now.Unix(),
|
||||
},
|
||||
saveCmd := models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
IsFolder: false,
|
||||
FolderId: dash.Id,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": "another_dashboard",
|
||||
}),
|
||||
}
|
||||
provisioning := &models.DashboardProvisioning{
|
||||
Name: "another_reader",
|
||||
ExternalId: "/var/grafana.json",
|
||||
Updated: now.Unix(),
|
||||
}
|
||||
|
||||
err := SaveProvisionedDashboard(anotherCmd)
|
||||
anotherDash, err := sqlStore.SaveProvisionedDashboard(saveCmd, provisioning)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := &models.GetDashboardsQuery{DashboardIds: []int64{anotherCmd.Result.Id}}
|
||||
query := &models.GetDashboardsQuery{DashboardIds: []int64{anotherDash.Id}}
|
||||
err = GetDashboards(query)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result, ShouldNotBeNil)
|
||||
@@ -85,7 +80,7 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
deleteCmd := &models.DeleteOrphanedProvisionedDashboardsCommand{ReaderNames: []string{"default"}}
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -94,45 +89,38 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Can query for provisioned dashboards", func() {
|
||||
query := &models.GetProvisionedDashboardDataQuery{Name: "default"}
|
||||
err := GetProvisionedDashboardDataQuery(query)
|
||||
rslt, err := sqlStore.GetProvisionedDashboardData("default")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(query.Result), ShouldEqual, 1)
|
||||
So(query.Result[0].DashboardId, ShouldEqual, dashId)
|
||||
So(query.Result[0].Updated, ShouldEqual, now.Unix())
|
||||
So(len(rslt), ShouldEqual, 1)
|
||||
So(rslt[0].DashboardId, ShouldEqual, dashId)
|
||||
So(rslt[0].Updated, ShouldEqual, now.Unix())
|
||||
})
|
||||
|
||||
Convey("Can query for one provisioned dashboard", func() {
|
||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
|
||||
|
||||
err := GetProvisionedDataByDashboardId(query)
|
||||
data, err := sqlStore.GetProvisionedDataByDashboardID(dash.Id)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(query.Result, ShouldNotBeNil)
|
||||
So(data, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Can query for none provisioned dashboard", func() {
|
||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: 3000}
|
||||
|
||||
err := GetProvisionedDataByDashboardId(query)
|
||||
data, err := sqlStore.GetProvisionedDataByDashboardID(3000)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result, ShouldBeNil)
|
||||
So(data, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Deleting folder should delete provision meta data", func() {
|
||||
deleteCmd := &models.DeleteDashboardCommand{
|
||||
Id: folderCmd.Result.Id,
|
||||
Id: dash.Id,
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
So(DeleteDashboard(deleteCmd), ShouldBeNil)
|
||||
|
||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
|
||||
|
||||
err = GetProvisionedDataByDashboardId(query)
|
||||
data, err := sqlStore.GetProvisionedDataByDashboardID(dash.Id)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result, ShouldBeNil)
|
||||
So(data, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("UnprovisionDashboard should delete provisioning metadata", func() {
|
||||
@@ -142,11 +130,9 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
||||
|
||||
So(UnprovisionDashboard(unprovisionCmd), ShouldBeNil)
|
||||
|
||||
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashId}
|
||||
|
||||
err = GetProvisionedDataByDashboardId(query)
|
||||
data, err := sqlStore.GetProvisionedDataByDashboardID(dashId)
|
||||
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) {
|
||||
Convey("Testing DB", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
Convey("Given saved dashboard", func() {
|
||||
savedFolder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
savedDash := insertTestDashboard(t, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
insertTestDashboard(t, "test dash 45", 1, savedFolder.Id, false, "prod")
|
||||
insertTestDashboard(t, "test dash 67", 1, 0, false, "prod", "webapp")
|
||||
savedFolder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
savedDash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
|
||||
insertTestDashboard(t, sqlStore, "test dash 45", 1, savedFolder.Id, false, "prod")
|
||||
insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod", "webapp")
|
||||
|
||||
Convey("Should return dashboard model", func() {
|
||||
So(savedDash.Title, ShouldEqual, "test dash 23")
|
||||
@@ -104,7 +104,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
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{
|
||||
Id: dash.Id,
|
||||
@@ -129,8 +129,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
"tags": []interface{}{},
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
_, err := sqlStore.SaveDashboard(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
generateNewUid = util.GenerateShortUID
|
||||
@@ -145,13 +144,12 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
}),
|
||||
UserId: 100,
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
dashboard, err := sqlStore.SaveDashboard(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
So(cmd.Result.CreatedBy, ShouldEqual, 100)
|
||||
So(cmd.Result.Created.IsZero(), ShouldBeFalse)
|
||||
So(cmd.Result.UpdatedBy, ShouldEqual, 100)
|
||||
So(cmd.Result.Updated.IsZero(), ShouldBeFalse)
|
||||
So(dashboard.CreatedBy, ShouldEqual, 100)
|
||||
So(dashboard.Created.IsZero(), ShouldBeFalse)
|
||||
So(dashboard.UpdatedBy, ShouldEqual, 100)
|
||||
So(dashboard.Updated.IsZero(), ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Should be able to update dashboard by id and remove folderId", func() {
|
||||
@@ -166,10 +164,9 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
FolderId: 2,
|
||||
UserId: 100,
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
dash, err := sqlStore.SaveDashboard(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
So(cmd.Result.FolderId, ShouldEqual, 2)
|
||||
So(dash.FolderId, ShouldEqual, 2)
|
||||
|
||||
cmd = models.SaveDashboardCommand{
|
||||
OrgId: 1,
|
||||
@@ -182,8 +179,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
Overwrite: true,
|
||||
UserId: 100,
|
||||
}
|
||||
|
||||
err = SaveDashboard(&cmd)
|
||||
_, err = sqlStore.SaveDashboard(cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := models.GetDashboardQuery{
|
||||
@@ -201,7 +197,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
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}
|
||||
err := DeleteDashboard(deleteCmd)
|
||||
@@ -236,7 +232,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
_, err := sqlStore.SaveDashboard(cmd)
|
||||
So(err, ShouldEqual, models.ErrDashboardNotFound)
|
||||
})
|
||||
|
||||
@@ -250,8 +246,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
"tags": []interface{}{},
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
_, err := sqlStore.SaveDashboard(cmd)
|
||||
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() {
|
||||
starredDash := insertTestDashboard(t, "starred dash", 1, 0, false)
|
||||
starredDash := insertTestDashboard(t, sqlStore, "starred dash", 1, 0, false)
|
||||
err := StarDashboard(&models.StarDashboardCommand{
|
||||
DashboardId: starredDash.Id,
|
||||
UserId: 10,
|
||||
@@ -396,9 +391,9 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
Convey("Given a plugin with imported dashboards", func() {
|
||||
pluginId := "test-app"
|
||||
|
||||
appFolder := insertTestDashboardForPlugin("app-test", 1, 0, true, pluginId)
|
||||
insertTestDashboardForPlugin("app-dash1", 1, appFolder.Id, false, pluginId)
|
||||
insertTestDashboardForPlugin("app-dash2", 1, appFolder.Id, false, pluginId)
|
||||
appFolder := insertTestDashboardForPlugin(t, sqlStore, "app-test", 1, 0, true, pluginId)
|
||||
insertTestDashboardForPlugin(t, sqlStore, "app-dash1", 1, appFolder.Id, false, pluginId)
|
||||
insertTestDashboardForPlugin(t, sqlStore, "app-dash2", 1, appFolder.Id, false, pluginId)
|
||||
|
||||
Convey("Should return imported dashboard", func() {
|
||||
query := models.GetDashboardsByPluginIdQuery{
|
||||
@@ -417,9 +412,9 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
func TestDashboard_SortingOptions(t *testing.T) {
|
||||
// insertTestDashboard uses GoConvey's assertions. Workaround.
|
||||
Convey("test with multiple sorting options", t, func() {
|
||||
InitTestDB(t)
|
||||
dashB := insertTestDashboard(t, "Beta", 1, 0, false)
|
||||
dashA := insertTestDashboard(t, "Alfa", 1, 0, false)
|
||||
sqlStore := InitTestDB(t)
|
||||
dashB := insertTestDashboard(t, sqlStore, "Beta", 1, 0, false)
|
||||
dashA := insertTestDashboard(t, sqlStore, "Alfa", 1, 0, false)
|
||||
|
||||
assert.NotZero(t, 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()
|
||||
|
||||
cmd := models.SaveDashboardCommand{
|
||||
@@ -454,17 +450,20 @@ func insertTestDashboard(t *testing.T, title string, orgId int64, folderId int64
|
||||
"tags": tags,
|
||||
}),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
dash, err := sqlStore.SaveDashboard(cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
|
||||
cmd.Result.Data.Set("id", cmd.Result.Id)
|
||||
cmd.Result.Data.Set("uid", cmd.Result.Uid)
|
||||
dash.Data.Set("id", dash.Id)
|
||||
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{
|
||||
OrgId: orgId,
|
||||
FolderId: folderId,
|
||||
@@ -476,13 +475,13 @@ func insertTestDashboardForPlugin(title string, orgId int64, folderId int64, isF
|
||||
PluginId: pluginId,
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
dash, err := sqlStore.SaveDashboard(cmd)
|
||||
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()
|
||||
|
||||
setting.AutoAssignOrg = true
|
||||
@@ -490,27 +489,13 @@ func createUser(t *testing.T, name string, role string, isAdmin bool) models.Use
|
||||
setting.AutoAssignOrgRole = role
|
||||
|
||||
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)
|
||||
|
||||
q1 := models.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
|
||||
q1 := models.GetUserOrgListQuery{UserId: currentUser.Id}
|
||||
err = GetUserOrgList(&q1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, models.RoleType(role), q1.Result[0].Role)
|
||||
|
||||
return currentUserCmd.Result
|
||||
}
|
||||
|
||||
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
|
||||
return *currentUser
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import (
|
||||
"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
|
||||
|
||||
saveCmd := models.SaveDashboardCommand{
|
||||
@@ -21,17 +23,16 @@ func updateTestDashboard(dashboard *models.Dashboard, data map[string]interface{
|
||||
Overwrite: true,
|
||||
Dashboard: simplejson.NewFromAny(data),
|
||||
}
|
||||
|
||||
err := SaveDashboard(&saveCmd)
|
||||
_, err := sqlStore.SaveDashboard(saveCmd)
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
|
||||
func TestGetDashboardVersion(t *testing.T) {
|
||||
Convey("Testing dashboard version retrieval", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
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{
|
||||
DashboardId: savedDash.Id,
|
||||
@@ -71,8 +72,8 @@ func TestGetDashboardVersion(t *testing.T) {
|
||||
|
||||
func TestGetDashboardVersions(t *testing.T) {
|
||||
Convey("Testing dashboard versions retrieval", t, func() {
|
||||
InitTestDB(t)
|
||||
savedDash := insertTestDashboard(t, "test dash 43", 1, 0, false, "diff-all")
|
||||
sqlStore := InitTestDB(t)
|
||||
savedDash := insertTestDashboard(t, sqlStore, "test dash 43", 1, 0, false, "diff-all")
|
||||
|
||||
Convey("Get all versions for a given Dashboard ID", func() {
|
||||
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() {
|
||||
updateTestDashboard(savedDash, map[string]interface{}{
|
||||
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
|
||||
@@ -107,14 +108,14 @@ func TestGetDashboardVersions(t *testing.T) {
|
||||
|
||||
func TestDeleteExpiredVersions(t *testing.T) {
|
||||
Convey("Testing dashboard versions clean up", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
versionsToKeep := 5
|
||||
versionsToWrite := 10
|
||||
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++ {
|
||||
updateTestDashboard(savedDash, map[string]interface{}{
|
||||
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
}
|
||||
@@ -152,7 +153,7 @@ func TestDeleteExpiredVersions(t *testing.T) {
|
||||
|
||||
versionsToWriteBigNumber := perBatch*maxBatches + versionsToWrite
|
||||
for i := 0; i < versionsToWriteBigNumber-versionsToWrite; i++ {
|
||||
updateTestDashboard(savedDash, map[string]interface{}{
|
||||
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
|
||||
"tags": "different-tag",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
func TestAccountDataAccess(t *testing.T) {
|
||||
Convey("Testing Account DB Access", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
Convey("Given we have organizations, we can query them by IDs", func() {
|
||||
var err error
|
||||
@@ -78,13 +78,13 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 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)
|
||||
err = CreateUser(context.Background(), &ac2cmd)
|
||||
ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
q1 := models.GetUserOrgListQuery{UserId: ac1cmd.Result.Id}
|
||||
q2 := models.GetUserOrgListQuery{UserId: ac2cmd.Result.Id}
|
||||
q1 := models.GetUserOrgListQuery{UserId: ac1.Id}
|
||||
q2 := models.GetUserOrgListQuery{UserId: ac2.Id}
|
||||
err = GetUserOrgList(&q1)
|
||||
So(err, ShouldBeNil)
|
||||
err = GetUserOrgList(&q2)
|
||||
@@ -101,13 +101,10 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
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}
|
||||
|
||||
err := CreateUser(context.Background(), &ac1cmd)
|
||||
err = CreateUser(context.Background(), &ac2cmd)
|
||||
ac1, err := sqlStore.CreateUser(context.Background(), ac1cmd)
|
||||
ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ac1 := ac1cmd.Result
|
||||
ac2 := ac2cmd.Result
|
||||
|
||||
Convey("Should be able to read user info projection", func() {
|
||||
query := models.GetUserProfileQuery{UserId: ac1.Id}
|
||||
err = GetUserProfile(&query)
|
||||
@@ -266,9 +263,8 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Given an org user with dashboard permissions", func() {
|
||||
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)
|
||||
ac3 := ac3cmd.Result
|
||||
|
||||
orgUserCmd := models.AddOrgUserCommand{
|
||||
OrgId: ac1.OrgId,
|
||||
@@ -284,13 +280,17 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result), ShouldEqual, 3)
|
||||
|
||||
dash1 := insertTestDashboard(t, "1 test dash", ac1.OrgId, 0, false, "prod", "webapp")
|
||||
dash2 := insertTestDashboard(t, "2 test dash", ac3.OrgId, 0, false, "prod", "webapp")
|
||||
dash1 := insertTestDashboard(t, sqlStore, "1 test dash", ac1.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)
|
||||
|
||||
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)
|
||||
|
||||
Convey("When org user is deleted", func() {
|
||||
@@ -322,13 +322,16 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func testHelperUpdateDashboardAcl(dashboardId int64, items ...models.DashboardAcl) error {
|
||||
cmd := models.UpdateDashboardAclCommand{DashboardID: dashboardId}
|
||||
for _, i := range items {
|
||||
item := i
|
||||
func testHelperUpdateDashboardAcl(t *testing.T, sqlStore *SQLStore, dashboardID int64,
|
||||
items ...models.DashboardAcl) error {
|
||||
t.Helper()
|
||||
|
||||
var itemPtrs []*models.DashboardAcl
|
||||
for _, it := range items {
|
||||
item := it
|
||||
item.Created = 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() {
|
||||
bus.AddHandler("sql", GetPluginSettings)
|
||||
bus.AddHandler("sql", GetPluginSettingById)
|
||||
bus.AddHandler("sql", UpdatePluginSetting)
|
||||
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
|
||||
FROM plugin_setting `
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
if query.OrgId != 0 {
|
||||
if orgID != 0 {
|
||||
sql += "WHERE org_id=?"
|
||||
params = append(params, query.OrgId)
|
||||
params = append(params, orgID)
|
||||
}
|
||||
|
||||
sess := x.SQL(sql, params...)
|
||||
query.Result = make([]*models.PluginSettingInfoDTO, 0)
|
||||
return sess.Find(&query.Result)
|
||||
var rslt []*models.PluginSettingInfoDTO
|
||||
if err := sess.Find(&rslt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rslt, nil
|
||||
}
|
||||
|
||||
func GetPluginSettingById(query *models.GetPluginSettingByIdQuery) error {
|
||||
|
||||
@@ -5,7 +5,6 @@ package searchstore_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -34,13 +33,11 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
|
||||
}
|
||||
|
||||
db := setupTestEnvironment(t)
|
||||
err := createDashboards(0, 1, user.OrgId)
|
||||
require.NoError(t, err)
|
||||
createDashboards(t, db, 0, 1, user.OrgId)
|
||||
|
||||
// create one dashboard in another organization that shouldn't
|
||||
// be listed in the results.
|
||||
err = createDashboards(1, 2, 2)
|
||||
require.NoError(t, err)
|
||||
createDashboards(t, db, 1, 2, 2)
|
||||
|
||||
builder := &searchstore.Builder{
|
||||
Filters: []interface{}{
|
||||
@@ -51,7 +48,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
|
||||
}
|
||||
|
||||
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)
|
||||
return sess.SQL(sql, params...).Find(&res)
|
||||
})
|
||||
@@ -77,8 +74,7 @@ func TestBuilder_Pagination(t *testing.T) {
|
||||
}
|
||||
|
||||
db := setupTestEnvironment(t)
|
||||
err := createDashboards(0, 25, user.OrgId)
|
||||
require.NoError(t, err)
|
||||
createDashboards(t, db, 0, 25, user.OrgId)
|
||||
|
||||
builder := &searchstore.Builder{
|
||||
Filters: []interface{}{
|
||||
@@ -91,7 +87,7 @@ func TestBuilder_Pagination(t *testing.T) {
|
||||
resPg1 := []sqlstore.DashboardSearchProjection{}
|
||||
resPg2 := []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)
|
||||
err := sess.SQL(sql, params...).Find(&resPg1)
|
||||
if err != nil {
|
||||
@@ -124,8 +120,7 @@ func TestBuilder_Permissions(t *testing.T) {
|
||||
}
|
||||
|
||||
db := setupTestEnvironment(t)
|
||||
err := createDashboards(0, 1, user.OrgId)
|
||||
require.NoError(t, err)
|
||||
createDashboards(t, db, 0, 1, user.OrgId)
|
||||
|
||||
level := models.PERMISSION_EDIT
|
||||
|
||||
@@ -145,7 +140,7 @@ func TestBuilder_Permissions(t *testing.T) {
|
||||
}
|
||||
|
||||
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)
|
||||
return sess.SQL(sql, params...).Find(&res)
|
||||
})
|
||||
@@ -161,10 +156,10 @@ func setupTestEnvironment(t *testing.T) *sqlstore.SQLStore {
|
||||
return store
|
||||
}
|
||||
|
||||
func createDashboards(startID, endID int, orgID int64) error {
|
||||
if endID < startID {
|
||||
return fmt.Errorf("startID must be smaller than endID")
|
||||
}
|
||||
func createDashboards(t *testing.T, db *sqlstore.SQLStore, startID, endID int, orgID int64) {
|
||||
t.Helper()
|
||||
|
||||
require.GreaterOrEqual(t, endID, startID)
|
||||
|
||||
for i := startID; i < endID; i++ {
|
||||
dashboard, err := simplejson.NewJson([]byte(`{
|
||||
@@ -176,20 +171,15 @@ func createDashboards(startID, endID int, orgID int64) error {
|
||||
"schemaVersion": 16,
|
||||
"version": 0
|
||||
}`))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = sqlstore.SaveDashboard(&models.SaveDashboardCommand{
|
||||
require.NoError(t, err)
|
||||
_, err = db.SaveDashboard(models.SaveDashboardCommand{
|
||||
Dashboard: dashboard,
|
||||
UserId: 1,
|
||||
OrgId: orgID,
|
||||
UpdatedAt: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lexiCounter counts in a lexicographically sortable order.
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSQLBuilder(t *testing.T) {
|
||||
@@ -138,8 +139,8 @@ func TestSQLBuilder(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
var shouldFind = true
|
||||
var shouldNotFind = false
|
||||
const shouldFind = true
|
||||
const shouldNotFind = false
|
||||
|
||||
type DashboardProps struct {
|
||||
OrgId int64
|
||||
@@ -164,38 +165,35 @@ type dashboardResponse struct {
|
||||
}
|
||||
|
||||
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool) {
|
||||
// Will also cleanup the db
|
||||
sqlStore := InitTestDB(t)
|
||||
t.Helper()
|
||||
|
||||
dashboard, err := createDummyDashboard(dashboardProps)
|
||||
if !assert.Equal(t, nil, err) {
|
||||
return
|
||||
}
|
||||
t.Run("", func(t *testing.T) {
|
||||
// Will also cleanup the db
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
var aclUserId int64
|
||||
if dashboardPermission != nil {
|
||||
aclUserId, err = createDummyAcl(dashboardPermission, search, dashboard.Id)
|
||||
if !assert.Equal(t, nil, err) {
|
||||
return
|
||||
dashboard := createDummyDashboard(t, sqlStore, dashboardProps)
|
||||
|
||||
var aclUserID int64
|
||||
if dashboardPermission != nil {
|
||||
aclUserID = createDummyACL(t, sqlStore, dashboardPermission, search, dashboard.Id)
|
||||
t.Logf("Created ACL with user ID %d\n", aclUserID)
|
||||
}
|
||||
}
|
||||
dashboards, err := getDashboards(sqlStore, search, aclUserId)
|
||||
if !assert.Equal(t, nil, err) {
|
||||
return
|
||||
}
|
||||
dashboards := getDashboards(t, sqlStore, search, aclUserID)
|
||||
|
||||
if shouldFind {
|
||||
if assert.Equal(t, 1, len(dashboards), "Should return one dashboard") {
|
||||
assert.Equal(t, dashboards[0].Id, dashboard.Id, "Should return created dashboard")
|
||||
if shouldFind {
|
||||
require.Len(t, dashboards, 1, "Should return one 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))
|
||||
createUserCmd := &models.CreateUserCommand{
|
||||
createUserCmd := models.CreateUserCommand{
|
||||
Email: uid + "@example.com",
|
||||
Login: uid,
|
||||
Name: uid,
|
||||
@@ -207,33 +205,28 @@ func createDummyUser() (*models.User, error) {
|
||||
SkipOrgSetup: false,
|
||||
DefaultOrgRole: string(models.ROLE_VIEWER),
|
||||
}
|
||||
err := CreateUser(context.Background(), createUserCmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := sqlStore.CreateUser(context.Background(), createUserCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &createUserCmd.Result, nil
|
||||
return user
|
||||
}
|
||||
|
||||
func createDummyTeam() (*models.Team, error) {
|
||||
cmd := &models.CreateTeamCommand{
|
||||
// Does not matter in this tests actually
|
||||
OrgId: 1,
|
||||
Name: "test",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
err := CreateTeam(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func createDummyTeam(t *testing.T, sqlStore *SQLStore) models.Team {
|
||||
t.Helper()
|
||||
|
||||
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) {
|
||||
json, _ := simplejson.NewJson([]byte(`{"schemaVersion":17,"title":"gdev dashboards","uid":"","version":1}`))
|
||||
func createDummyDashboard(t *testing.T, sqlStore *SQLStore, dashboardProps DashboardProps) *models.Dashboard {
|
||||
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,
|
||||
UserId: 0,
|
||||
Overwrite: false,
|
||||
@@ -250,53 +243,40 @@ func createDummyDashboard(dashboardProps DashboardProps) (*models.Dashboard, err
|
||||
saveDashboardCmd.OrgId = 1
|
||||
}
|
||||
|
||||
err := SaveDashboard(saveDashboardCmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dash, err := sqlStore.SaveDashboard(saveDashboardCmd)
|
||||
require.NoError(t, 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{
|
||||
OrgID: 1,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
Permission: dashboardPermission.Permission,
|
||||
DashboardID: dashboardId,
|
||||
DashboardID: dashboardID,
|
||||
}
|
||||
|
||||
var user *models.User
|
||||
var err error
|
||||
if dashboardPermission.User {
|
||||
user, err = createDummyUser()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t.Logf("Creating user")
|
||||
user = createDummyUser(t, sqlStore)
|
||||
|
||||
acl.UserID = user.Id
|
||||
}
|
||||
|
||||
if dashboardPermission.Team {
|
||||
team, err := createDummyTeam()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t.Logf("Creating team")
|
||||
team := createDummyTeam(t, sqlStore)
|
||||
if search.UserFromACL {
|
||||
user, err = createDummyUser()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
addTeamMemberCmd := &models.AddTeamMemberCommand{
|
||||
UserId: user.Id,
|
||||
OrgId: 1,
|
||||
TeamId: team.Id,
|
||||
}
|
||||
err = AddTeamMember(addTeamMemberCmd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
user = createDummyUser(t, sqlStore)
|
||||
err := sqlStore.AddTeamMember(user.Id, 1, team.Id, false, 0)
|
||||
require.NoError(t, err)
|
||||
t.Logf("Created team member with ID %d", user.Id)
|
||||
}
|
||||
|
||||
acl.TeamID = team.Id
|
||||
@@ -306,18 +286,17 @@ func createDummyAcl(dashboardPermission *DashboardPermission, search Search, das
|
||||
acl.Role = &dashboardPermission.Role
|
||||
}
|
||||
|
||||
updateAclCmd := &models.UpdateDashboardAclCommand{
|
||||
DashboardID: dashboardId,
|
||||
Items: []*models.DashboardAcl{acl},
|
||||
}
|
||||
err = UpdateDashboardAcl(updateAclCmd)
|
||||
err := sqlStore.UpdateDashboardACL(dashboardID, []*models.DashboardAcl{acl})
|
||||
require.NoError(t, err)
|
||||
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{}
|
||||
signedInUser := &models.SignedInUser{
|
||||
UserId: 9999999999,
|
||||
@@ -335,12 +314,14 @@ func getDashboards(sqlStore *SQLStore, search Search, aclUserId int64) ([]*dashb
|
||||
signedInUser.OrgRole = models.ROLE_VIEWER
|
||||
}
|
||||
if search.UserFromACL {
|
||||
signedInUser.UserId = aclUserId
|
||||
signedInUser.UserId = aclUserID
|
||||
}
|
||||
|
||||
var res []*dashboardResponse
|
||||
builder.Write("SELECT * FROM dashboard WHERE true")
|
||||
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)
|
||||
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/sqlutil"
|
||||
"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/errutil"
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
func TestStatsDataAccess(t *testing.T) {
|
||||
InitTestDB(t)
|
||||
populateDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
populateDB(t, sqlStore)
|
||||
|
||||
t.Run("Get system stats should not results in error", func(t *testing.T) {
|
||||
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)
|
||||
for i := range users {
|
||||
cmd := &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: fmt.Sprintf("usertest%v@test.com", i),
|
||||
Name: fmt.Sprintf("user name %v", i),
|
||||
Login: fmt.Sprintf("user_test_%v_login", i),
|
||||
OrgName: fmt.Sprintf("Org #%v", i),
|
||||
}
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
user, err := sqlStore.CreateUser(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
users[i] = cmd.Result
|
||||
users[i] = *user
|
||||
}
|
||||
|
||||
// get 1st user's organisation
|
||||
|
||||
@@ -2,6 +2,7 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -11,14 +12,12 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", CreateTeam)
|
||||
bus.AddHandler("sql", UpdateTeam)
|
||||
bus.AddHandler("sql", DeleteTeam)
|
||||
bus.AddHandler("sql", SearchTeams)
|
||||
bus.AddHandler("sql", GetTeamById)
|
||||
bus.AddHandler("sql", GetTeamsByUser)
|
||||
|
||||
bus.AddHandler("sql", AddTeamMember)
|
||||
bus.AddHandler("sql", UpdateTeamMember)
|
||||
bus.AddHandler("sql", RemoveTeamMember)
|
||||
bus.AddHandler("sql", GetTeamMembers)
|
||||
@@ -43,7 +42,7 @@ func getFilteredUsers(signedInUser *models.SignedInUser, hiddenUsers map[string]
|
||||
|
||||
func getTeamMemberCount(filteredUsers []string) string {
|
||||
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
|
||||
WHERE team_member.team_id = team.id AND ` + dialect.Quote("user") + `.login NOT IN (?` +
|
||||
strings.Repeat(",?", len(filteredUsers)-1) + ")" +
|
||||
@@ -75,28 +74,25 @@ func getTeamSelectSQLBase(filteredUsers []string) string {
|
||||
` FROM team as team `
|
||||
}
|
||||
|
||||
func CreateTeam(cmd *models.CreateTeamCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, 0, sess); err != nil {
|
||||
func (ss *SQLStore) CreateTeam(name, email string, orgID int64) (models.Team, error) {
|
||||
team := models.Team{
|
||||
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
|
||||
} else if isNameTaken {
|
||||
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)
|
||||
|
||||
cmd.Result = team
|
||||
|
||||
return err
|
||||
})
|
||||
return team, err
|
||||
}
|
||||
|
||||
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) {
|
||||
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgId, teamId); err != nil {
|
||||
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 {
|
||||
return false, err
|
||||
} else if len(res) != 1 {
|
||||
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) {
|
||||
var team models.Team
|
||||
exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team)
|
||||
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
@@ -283,26 +278,27 @@ func GetTeamsByUser(query *models.GetTeamsByUserQuery) error {
|
||||
}
|
||||
|
||||
// AddTeamMember adds a user to a team
|
||||
func AddTeamMember(cmd *models.AddTeamMemberCommand) error {
|
||||
return inTransaction(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 {
|
||||
func (ss *SQLStore) AddTeamMember(userID, orgID, teamID int64, isExternal bool, permission models.PermissionType) 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=?",
|
||||
orgID, teamID, userID); err != nil {
|
||||
return err
|
||||
} else if len(res) == 1 {
|
||||
return models.ErrTeamMemberAlreadyAdded
|
||||
}
|
||||
|
||||
if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil {
|
||||
if _, err := teamExists(orgID, teamID, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entity := models.TeamMember{
|
||||
OrgId: cmd.OrgId,
|
||||
TeamId: cmd.TeamId,
|
||||
UserId: cmd.UserId,
|
||||
External: cmd.External,
|
||||
OrgId: orgID,
|
||||
TeamId: teamID,
|
||||
UserId: userID,
|
||||
External: isExternal,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
Permission: cmd.Permission,
|
||||
Permission: permission,
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&entity)
|
||||
|
||||
@@ -14,32 +14,29 @@ import (
|
||||
|
||||
func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
Convey("Testing Team commands & queries", t, func() {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
Convey("Given saved users and two teams", func() {
|
||||
var userIds []int64
|
||||
for i := 0; i < 5; i++ {
|
||||
userCmd := &models.CreateUserCommand{
|
||||
userCmd := models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
Login: fmt.Sprint("loginuser", i),
|
||||
}
|
||||
err := CreateUser(context.Background(), userCmd)
|
||||
user, err := sqlStore.CreateUser(context.Background(), userCmd)
|
||||
So(err, ShouldBeNil)
|
||||
userIds = append(userIds, userCmd.Result.Id)
|
||||
userIds = append(userIds, user.Id)
|
||||
}
|
||||
|
||||
var testOrgId int64 = 1
|
||||
group1 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group1 name", Email: "test1@test.com"}
|
||||
group2 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group2 name", Email: "test2@test.com"}
|
||||
|
||||
err := CreateTeam(&group1)
|
||||
const testOrgID int64 = 1
|
||||
team1, err := sqlStore.CreateTeam("group1 name", "test1@test.com", testOrgID)
|
||||
So(err, ShouldBeNil)
|
||||
err = CreateTeam(&group2)
|
||||
team2, err := sqlStore.CreateTeam("group2 name", "test2@test.com", testOrgID)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Page, ShouldEqual, 1)
|
||||
@@ -47,33 +44,33 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
team1 := query.Result.Teams[0]
|
||||
So(team1.Name, ShouldEqual, "group1 name")
|
||||
So(team1.Email, ShouldEqual, "test1@test.com")
|
||||
So(team1.OrgId, ShouldEqual, testOrgId)
|
||||
So(team1.OrgId, ShouldEqual, testOrgID)
|
||||
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)
|
||||
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)
|
||||
|
||||
q1 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id}
|
||||
q1 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id}
|
||||
err = GetTeamMembers(q1)
|
||||
So(err, ShouldBeNil)
|
||||
So(q1.Result, ShouldHaveLength, 2)
|
||||
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||
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].Login, ShouldEqual, "loginuser1")
|
||||
So(q1.Result[1].OrgId, ShouldEqual, testOrgId)
|
||||
So(q1.Result[1].OrgId, ShouldEqual, testOrgID)
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(q2.Result, ShouldHaveLength, 1)
|
||||
So(q2.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||
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)
|
||||
|
||||
err = SearchTeams(query)
|
||||
@@ -81,13 +78,13 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
team1 = query.Result.Teams[0]
|
||||
So(team1.MemberCount, ShouldEqual, 2)
|
||||
|
||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: team1.Id}
|
||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: team1.Id}
|
||||
err = GetTeamById(getTeamQuery)
|
||||
So(err, ShouldBeNil)
|
||||
team1 = getTeamQuery.Result
|
||||
So(team1.Name, ShouldEqual, "group1 name")
|
||||
So(team1.Email, ShouldEqual, "test1@test.com")
|
||||
So(team1.OrgId, ShouldEqual, testOrgId)
|
||||
So(team1.OrgId, ShouldEqual, testOrgID)
|
||||
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"})
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(teamQuery.Page, ShouldEqual, 1)
|
||||
|
||||
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)
|
||||
|
||||
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
|
||||
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true}
|
||||
err = GetTeamMembers(memberQuery)
|
||||
So(err, ShouldBeNil)
|
||||
So(memberQuery.Result, ShouldHaveLength, 1)
|
||||
So(memberQuery.Result[0].TeamId, ShouldEqual, team1.Id)
|
||||
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].AuthModule, ShouldEqual, "oauth_github")
|
||||
})
|
||||
|
||||
Convey("Should be able to update users in a team", func() {
|
||||
userId := userIds[0]
|
||||
team := group1.Result
|
||||
addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userId}
|
||||
err = AddTeamMember(&addMemberCmd)
|
||||
team := team1
|
||||
err = sqlStore.AddTeamMember(userId, testOrgID, team.Id, false, 0)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||
err = GetTeamMembers(qBeforeUpdate)
|
||||
So(err, ShouldBeNil)
|
||||
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
|
||||
|
||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
||||
UserId: userId,
|
||||
OrgId: testOrgId,
|
||||
OrgId: testOrgID,
|
||||
TeamId: team.Id,
|
||||
Permission: models.PERMISSION_ADMIN,
|
||||
})
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||
err = GetTeamMembers(qAfterUpdate)
|
||||
So(err, ShouldBeNil)
|
||||
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() {
|
||||
userID := userIds[0]
|
||||
team := group1.Result
|
||||
addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userID}
|
||||
err = AddTeamMember(&addMemberCmd)
|
||||
team := team1
|
||||
err = sqlStore.AddTeamMember(userID, testOrgID, team.Id, false, 0)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||
err = GetTeamMembers(qBeforeUpdate)
|
||||
So(err, ShouldBeNil)
|
||||
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
|
||||
@@ -159,14 +154,14 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
invalidPermissionLevel := models.PERMISSION_EDIT
|
||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
||||
UserId: userID,
|
||||
OrgId: testOrgId,
|
||||
OrgId: testOrgID,
|
||||
TeamId: team.Id,
|
||||
Permission: invalidPermissionLevel,
|
||||
})
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
|
||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
|
||||
err = GetTeamMembers(qAfterUpdate)
|
||||
So(err, ShouldBeNil)
|
||||
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() {
|
||||
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
|
||||
UserId: 1,
|
||||
OrgId: testOrgId,
|
||||
TeamId: group1.Result.Id,
|
||||
OrgId: testOrgID,
|
||||
TeamId: team1.Id,
|
||||
Permission: models.PERMISSION_ADMIN,
|
||||
})
|
||||
|
||||
@@ -184,24 +179,24 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
})
|
||||
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Teams), ShouldEqual, 2)
|
||||
So(query.Result.TotalCount, ShouldEqual, 2)
|
||||
|
||||
query2 := &models.SearchTeamsQuery{OrgId: testOrgId, Query: ""}
|
||||
query2 := &models.SearchTeamsQuery{OrgId: testOrgID, Query: ""}
|
||||
err = SearchTeams(query2)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query2.Result.Teams), ShouldEqual, 2)
|
||||
})
|
||||
|
||||
Convey("Should be able to return all teams a user is member of", func() {
|
||||
groupId := group2.Result.Id
|
||||
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
|
||||
groupId := team2.Id
|
||||
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := &models.GetTeamsByUserQuery{OrgId: testOrgId, UserId: userIds[0]}
|
||||
query := &models.GetTeamsByUserQuery{OrgId: testOrgID, UserId: userIds[0]}
|
||||
err = GetTeamsByUser(query)
|
||||
So(err, ShouldBeNil)
|
||||
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() {
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: group1.Result.Id}
|
||||
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id}
|
||||
err = GetTeamMembers(q2)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(q2.Result), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Should be able to remove a group with users and permissions", func() {
|
||||
groupId := group2.Result.Id
|
||||
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]})
|
||||
groupId := team2.Id
|
||||
err := sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, 0)
|
||||
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)
|
||||
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)
|
||||
err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgId, Id: groupId})
|
||||
err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgID, Id: groupId})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: groupId}
|
||||
query := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: groupId}
|
||||
err = GetTeamById(query)
|
||||
So(err, ShouldEqual, models.ErrTeamNotFound)
|
||||
|
||||
permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: testOrgId}
|
||||
permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: testOrgID}
|
||||
err = GetDashboardAclInfoList(permQuery)
|
||||
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() {
|
||||
groupId := group2.Result.Id
|
||||
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
|
||||
groupId := team2.Id
|
||||
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||
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)
|
||||
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result, ShouldBeTrue)
|
||||
@@ -295,29 +292,29 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
signedInUser := &models.SignedInUser{Login: "loginuser0"}
|
||||
hiddenUsers := map[string]struct{}{"loginuser0": {}, "loginuser1": {}}
|
||||
|
||||
teamId := group1.Result.Id
|
||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[0]})
|
||||
teamId := team1.Id
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, teamId, false, 0)
|
||||
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)
|
||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[2]})
|
||||
err = sqlStore.AddTeamMember(userIds[2], testOrgID, teamId, false, 0)
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(searchQuery.Result.Teams, ShouldHaveLength, 2)
|
||||
team1 := searchQuery.Result.Teams[0]
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(searchQueryFilteredByUser.Result.Teams, ShouldHaveLength, 1)
|
||||
team1 = searchQuery.Result.Teams[0]
|
||||
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)
|
||||
So(err, ShouldBeNil)
|
||||
So(getTeamQuery.Result.MemberCount, ShouldEqual, 2)
|
||||
|
||||
@@ -31,12 +31,10 @@ func (ss *SQLStore) addUserQueryAndCommandHandlers() {
|
||||
bus.AddHandler("sql", DisableUser)
|
||||
bus.AddHandler("sql", BatchDisableUsers)
|
||||
bus.AddHandler("sql", DeleteUser)
|
||||
bus.AddHandler("sql", UpdateUserPermissions)
|
||||
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 {
|
||||
return -1, nil
|
||||
}
|
||||
@@ -191,8 +189,9 @@ func (ss *SQLStore) createUser(ctx context.Context, args userCreationArgs, skipO
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
||||
return inTransactionCtx(ctx, func(sess *DBSession) error {
|
||||
func (ss *SQLStore) CreateUser(ctx context.Context, cmd models.CreateUserCommand) (*models.User, error) {
|
||||
var user *models.User
|
||||
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
orgId, err := getOrgIdForNewUser(sess, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -202,13 +201,16 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
||||
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 {
|
||||
return models.ErrUserAlreadyExists
|
||||
}
|
||||
|
||||
// create user
|
||||
user := models.User{
|
||||
user = &models.User{
|
||||
Email: cmd.Email,
|
||||
Name: cmd.Name,
|
||||
Login: cmd.Login,
|
||||
@@ -243,7 +245,7 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
||||
|
||||
sess.UseBool("is_admin")
|
||||
|
||||
if _, err := sess.Insert(&user); err != nil {
|
||||
if _, err := sess.Insert(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -255,8 +257,6 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
||||
Email: user.Email,
|
||||
})
|
||||
|
||||
cmd.Result = user
|
||||
|
||||
// create org user link
|
||||
if !cmd.SkipOrgSetup {
|
||||
orgUser := models.OrgUser{
|
||||
@@ -282,6 +282,8 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return user, err
|
||||
}
|
||||
|
||||
func GetUserById(query *models.GetUserByIdQuery) error {
|
||||
@@ -729,14 +731,14 @@ func deleteUserInTransaction(sess *DBSession, cmd *models.DeleteUserCommand) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateUserPermissions(cmd *models.UpdateUserPermissionsCommand) error {
|
||||
return inTransaction(func(sess *DBSession) error {
|
||||
user := models.User{}
|
||||
if _, err := sess.ID(cmd.UserId).Get(&user); err != nil {
|
||||
func (ss *SQLStore) UpdateUserPermissions(userID int64, isAdmin bool) error {
|
||||
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
var user models.User
|
||||
if _, err := sess.ID(userID).Get(&user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.IsAdmin = cmd.IsGrafanaAdmin
|
||||
user.IsAdmin = isAdmin
|
||||
sess.UseBool("is_admin")
|
||||
|
||||
_, err := sess.ID(user.Id).Update(&user)
|
||||
|
||||
@@ -15,18 +15,16 @@ import (
|
||||
|
||||
//nolint:goconst
|
||||
func TestUserAuth(t *testing.T) {
|
||||
InitTestDB(t)
|
||||
sqlStore := InitTestDB(t)
|
||||
|
||||
Convey("Given 5 users", t, func() {
|
||||
var err error
|
||||
var cmd *models.CreateUserCommand
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
Login: fmt.Sprint("loginuser", i),
|
||||
}
|
||||
err = CreateUser(context.Background(), cmd)
|
||||
_, err := sqlStore.CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
|
||||
@@ -46,7 +44,7 @@ func TestUserAuth(t *testing.T) {
|
||||
login := "loginuser0"
|
||||
|
||||
query := &models.GetUserByAuthInfoQuery{Login: login}
|
||||
err = GetUserByAuthInfo(query)
|
||||
err := GetUserByAuthInfo(query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.Login, ShouldEqual, login)
|
||||
@@ -82,7 +80,7 @@ func TestUserAuth(t *testing.T) {
|
||||
Convey("Can set & locate by AuthModule and AuthId", func() {
|
||||
// get nonexistent user_auth entry
|
||||
query := &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"}
|
||||
err = GetUserByAuthInfo(query)
|
||||
err := GetUserByAuthInfo(query)
|
||||
|
||||
So(err, ShouldEqual, models.ErrUserNotFound)
|
||||
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
|
||||
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test", AuthId: "test"}
|
||||
err = GetUserByAuthInfo(query)
|
||||
err := GetUserByAuthInfo(query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.Login, ShouldEqual, login)
|
||||
@@ -179,7 +177,7 @@ func TestUserAuth(t *testing.T) {
|
||||
// Make the first log-in during the past
|
||||
getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) }
|
||||
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test1", AuthId: "test1"}
|
||||
err = GetUserByAuthInfo(query)
|
||||
err := GetUserByAuthInfo(query)
|
||||
getTime = time.Now
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -20,17 +20,17 @@ func TestUserDataAccess(t *testing.T) {
|
||||
ss := InitTestDB(t)
|
||||
|
||||
Convey("Creates a user", func() {
|
||||
cmd := &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: "usertest@test.com",
|
||||
Name: "user name",
|
||||
Login: "user_test_login",
|
||||
}
|
||||
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
user, err := ss.CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Loading a user", func() {
|
||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
||||
query := models.GetUserByIdQuery{Id: user.Id}
|
||||
err := GetUserById(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -43,18 +43,18 @@ func TestUserDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Creates disabled user", func() {
|
||||
cmd := &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: "usertest@test.com",
|
||||
Name: "user name",
|
||||
Login: "user_test_login",
|
||||
IsDisabled: true,
|
||||
}
|
||||
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
user, err := ss.CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Loading a user", func() {
|
||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
||||
query := models.GetUserByIdQuery{Id: user.Id}
|
||||
err := GetUserById(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -78,18 +78,18 @@ func TestUserDataAccess(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Creates user assigned to other organization", func() {
|
||||
cmd := &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: "usertest@test.com",
|
||||
Name: "user name",
|
||||
Login: "user_test_login",
|
||||
OrgId: orgCmd.Result.Id,
|
||||
}
|
||||
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
user, err := ss.CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Loading a user", func() {
|
||||
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
|
||||
query := models.GetUserByIdQuery{Id: user.Id}
|
||||
err := GetUserById(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -104,20 +104,20 @@ func TestUserDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Don't create user assigned to unknown organization", func() {
|
||||
const nonExistingOrgID = 10000
|
||||
cmd := &models.CreateUserCommand{
|
||||
cmd := models.CreateUserCommand{
|
||||
Email: "usertest@test.com",
|
||||
Name: "user name",
|
||||
Login: "user_test_login",
|
||||
OrgId: nonExistingOrgID,
|
||||
}
|
||||
|
||||
err := CreateUser(context.Background(), cmd)
|
||||
_, err := ss.CreateUser(context.Background(), cmd)
|
||||
So(err, ShouldEqual, models.ErrOrgNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given 5 users", func() {
|
||||
users := createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
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() {
|
||||
ss = InitTestDB(t)
|
||||
createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
@@ -269,7 +269,7 @@ func TestUserDataAccess(t *testing.T) {
|
||||
So(third, ShouldBeTrue)
|
||||
|
||||
ss = InitTestDB(t)
|
||||
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
@@ -286,7 +286,7 @@ func TestUserDataAccess(t *testing.T) {
|
||||
})
|
||||
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,
|
||||
Permission: models.PERMISSION_EDIT,
|
||||
})
|
||||
@@ -365,7 +365,7 @@ func TestUserDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Should enable all users", func() {
|
||||
ss = InitTestDB(t)
|
||||
createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
@@ -392,7 +392,7 @@ func TestUserDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Should disable only specific users", func() {
|
||||
ss = InitTestDB(t)
|
||||
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
@@ -438,7 +438,7 @@ func TestUserDataAccess(t *testing.T) {
|
||||
|
||||
// Since previous tests were destructive
|
||||
ss = InitTestDB(t)
|
||||
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
|
||||
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
|
||||
return &models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
@@ -546,25 +546,22 @@ func TestUserDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Given one grafana admin user", func() {
|
||||
var err error
|
||||
createUserCmd := &models.CreateUserCommand{
|
||||
createUserCmd := models.CreateUserCommand{
|
||||
Email: fmt.Sprint("admin", "@test.com"),
|
||||
Name: "admin",
|
||||
Login: "admin",
|
||||
IsAdmin: true,
|
||||
}
|
||||
err = CreateUser(context.Background(), createUserCmd)
|
||||
user, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Cannot make themselves a non-admin", func() {
|
||||
updateUserPermsCmd := models.UpdateUserPermissionsCommand{IsGrafanaAdmin: false, UserId: 1}
|
||||
updatePermsError := UpdateUserPermissions(&updateUserPermsCmd)
|
||||
updatePermsError := ss.UpdateUserPermissions(1, false)
|
||||
|
||||
So(updatePermsError, ShouldEqual, models.ErrLastGrafanaAdmin)
|
||||
|
||||
query := models.GetUserByIdQuery{Id: createUserCmd.Result.Id}
|
||||
query := models.GetUserByIdQuery{Id: user.Id}
|
||||
getUserError := GetUserById(&query)
|
||||
|
||||
So(getUserError, ShouldBeNil)
|
||||
|
||||
So(query.Result.IsAdmin, ShouldEqual, true)
|
||||
@@ -574,33 +571,33 @@ func TestUserDataAccess(t *testing.T) {
|
||||
Convey("Given one user", func() {
|
||||
const email = "user@test.com"
|
||||
const username = "user"
|
||||
createUserCmd := &models.CreateUserCommand{
|
||||
createUserCmd := models.CreateUserCommand{
|
||||
Email: email,
|
||||
Name: "user",
|
||||
Login: username,
|
||||
}
|
||||
err := CreateUser(context.Background(), createUserCmd)
|
||||
_, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
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,
|
||||
Name: "user2",
|
||||
Login: "user2",
|
||||
SkipOrgSetup: true,
|
||||
}
|
||||
err := CreateUser(context.Background(), createUserCmd)
|
||||
_, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||
So(err, ShouldEqual, models.ErrUserAlreadyExists)
|
||||
})
|
||||
|
||||
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",
|
||||
Name: "user2",
|
||||
Login: username,
|
||||
SkipOrgSetup: true,
|
||||
}
|
||||
err := CreateUser(context.Background(), createUserCmd)
|
||||
_, err := ss.CreateUser(context.Background(), createUserCmd)
|
||||
So(err, ShouldEqual, models.ErrUserAlreadyExists)
|
||||
})
|
||||
})
|
||||
@@ -618,15 +615,15 @@ func GetOrgUsersForTest(query *models.GetOrgUsersQuery) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func createFiveTestUsers(fn func(i int) *models.CreateUserCommand) []models.User {
|
||||
var err error
|
||||
var cmd *models.CreateUserCommand
|
||||
func createFiveTestUsers(t *testing.T, sqlStore *SQLStore, fn func(i int) *models.CreateUserCommand) []models.User {
|
||||
t.Helper()
|
||||
|
||||
users := []models.User{}
|
||||
for i := 0; i < 5; i++ {
|
||||
cmd = fn(i)
|
||||
cmd := fn(i)
|
||||
|
||||
err = CreateUser(context.Background(), cmd)
|
||||
users = append(users, cmd.Result)
|
||||
user, err := sqlStore.CreateUser(context.Background(), *cmd)
|
||||
users = append(users, *user)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
@@ -27,8 +26,9 @@ import (
|
||||
|
||||
// ApplicationInsightsDatasource calls the application insights query API.
|
||||
type ApplicationInsightsDatasource struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
pluginManager plugins.Manager
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||
if plugin == nil {
|
||||
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/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
@@ -26,8 +25,9 @@ import (
|
||||
|
||||
// AzureLogAnalyticsDatasource calls the Azure Log Analytics API's
|
||||
type AzureLogAnalyticsDatasource struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
pluginManager plugins.Manager
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||
if plugin == nil {
|
||||
return nil, errors.New("unable to find datasource plugin Azure Monitor")
|
||||
}
|
||||
cloudName := dsInfo.JsonData.Get("cloudName").MustString("azuremonitor")
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
@@ -26,8 +25,9 @@ import (
|
||||
|
||||
// AzureMonitorDatasource calls the Azure Monitor API - one of the four API's supported
|
||||
type AzureMonitorDatasource struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
pluginManager plugins.Manager
|
||||
}
|
||||
|
||||
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) {
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||
if plugin == nil {
|
||||
return nil, errors.New("unable to find datasource plugin Azure Monitor")
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ func init() {
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
}
|
||||
|
||||
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
|
||||
type AzureMonitorExecutor struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
pluginManager plugins.Manager
|
||||
}
|
||||
|
||||
// NewAzureMonitorExecutor initializes a http client
|
||||
@@ -46,8 +48,9 @@ func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, er
|
||||
}
|
||||
|
||||
return &AzureMonitorExecutor{
|
||||
httpClient: httpClient,
|
||||
dsInfo: dsInfo,
|
||||
httpClient: httpClient,
|
||||
dsInfo: dsInfo,
|
||||
pluginManager: s.PluginManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -82,23 +85,27 @@ func (e *AzureMonitorExecutor) DataQuery(ctx context.Context, dsInfo *models.Dat
|
||||
}
|
||||
|
||||
azDatasource := &AzureMonitorDatasource{
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
pluginManager: e.pluginManager,
|
||||
}
|
||||
|
||||
aiDatasource := &ApplicationInsightsDatasource{
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
pluginManager: e.pluginManager,
|
||||
}
|
||||
|
||||
alaDatasource := &AzureLogAnalyticsDatasource{
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
pluginManager: e.pluginManager,
|
||||
}
|
||||
|
||||
iaDatasource := &InsightsAnalyticsDatasource{
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
httpClient: e.httpClient,
|
||||
dsInfo: e.dsInfo,
|
||||
pluginManager: e.pluginManager,
|
||||
}
|
||||
|
||||
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/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
@@ -23,8 +22,9 @@ import (
|
||||
)
|
||||
|
||||
type InsightsAnalyticsDatasource struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
pluginManager plugins.Manager
|
||||
}
|
||||
|
||||
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) {
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||
if plugin == nil {
|
||||
return nil, errors.New("unable to find datasource plugin Azure Application Insights")
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
@@ -73,6 +72,7 @@ func init() {
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
}
|
||||
|
||||
func (s *Service) Init() error {
|
||||
@@ -81,8 +81,9 @@ func (s *Service) Init() error {
|
||||
|
||||
// Executor executes queries for the CloudMonitoring datasource.
|
||||
type Executor struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
pluginManager plugins.Manager
|
||||
}
|
||||
|
||||
// NewExecutor returns an Executor.
|
||||
@@ -93,8 +94,9 @@ func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, er
|
||||
}
|
||||
|
||||
return &Executor{
|
||||
httpClient: httpClient,
|
||||
dsInfo: dsInfo,
|
||||
httpClient: httpClient,
|
||||
dsInfo: dsInfo,
|
||||
pluginManager: s.PluginManager,
|
||||
}, 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))
|
||||
|
||||
// find plugin
|
||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
plugin := e.pluginManager.GetDataSource(dsInfo.Type)
|
||||
if plugin == nil {
|
||||
return nil, errors.New("unable to find datasource plugin CloudMonitoring")
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||
@@ -46,7 +45,7 @@ type Service struct {
|
||||
PostgresService *postgres.PostgresService `inject:""`
|
||||
CloudMonitoringService *cloudmonitoring.Service `inject:""`
|
||||
AzureMonitorService *azuremonitor.Service `inject:""`
|
||||
PluginManager *manager.PluginManager `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
|
||||
registry map[string]func(*models.DataSource) (plugins.DataPlugin, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user