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:
Arve Knudsen
2021-03-17 16:06:10 +01:00
committed by GitHub
parent f1df32ac03
commit 87c3a2b790
95 changed files with 2455 additions and 2426 deletions

10
pkg/api/acl.go Normal file
View 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)
}

View File

@@ -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) {

View File

@@ -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})

View File

@@ -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)

View File

@@ -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()
}

View File

@@ -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: &quota.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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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
View 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
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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(),
},

View File

@@ -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()

View File

@@ -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:""`

View File

@@ -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",

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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) {