Remove bus from dashboard api (#44923)

* Remove bus from dashboard api

* Polish api dashboard tests

* Remove Delete Slug method

* Fix sqlstore dashboard test

* Remove bus from dashboard permission

* Remove GetDashboardsBySlug from sqlstore
pull/44982/head
idafurjes 3 years ago committed by GitHub
parent 08ee093dfe
commit caa5f356be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      pkg/api/api.go
  2. 71
      pkg/api/dashboard.go
  3. 4
      pkg/api/dashboard_permission.go
  4. 89
      pkg/api/dashboard_permission_test.go
  5. 476
      pkg/api/dashboard_test.go
  6. 14
      pkg/api/folder.go
  7. 16
      pkg/services/sqlstore/dashboard.go
  8. 2
      pkg/services/sqlstore/dashboard_test.go
  9. 26
      pkg/services/sqlstore/mockstore/mockstore.go
  10. 1
      pkg/services/sqlstore/store.go

@ -341,11 +341,11 @@ func (hs *HTTPServer) registerRoutes() {
dashboardRoute.Post("/db", routing.Wrap(hs.PostDashboard))
dashboardRoute.Get("/home", routing.Wrap(hs.GetHomeDashboard))
dashboardRoute.Get("/tags", GetDashboardTags)
dashboardRoute.Get("/tags", hs.GetDashboardTags)
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) {
dashIdRoute.Get("/versions", routing.Wrap(GetDashboardVersions))
dashIdRoute.Get("/versions/:id", routing.Wrap(GetDashboardVersion))
dashIdRoute.Get("/versions", routing.Wrap(hs.GetDashboardVersions))
dashIdRoute.Get("/versions/:id", routing.Wrap(hs.GetDashboardVersion))
dashIdRoute.Post("/restore", routing.Wrap(hs.RestoreDashboardVersion))
dashIdRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {

@ -13,7 +13,6 @@ import (
"github.com/grafana/grafana/pkg/api/apierrors"
"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/components/dashdiffs"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
@ -29,13 +28,13 @@ const (
anonString = "Anonymous"
)
func isDashboardStarredByUser(c *models.ReqContext, dashID int64) (bool, error) {
func (hs *HTTPServer) isDashboardStarredByUser(c *models.ReqContext, dashID int64) (bool, error) {
if !c.IsSignedIn {
return false, nil
}
query := models.IsStarredByUserQuery{UserId: c.UserId, DashboardId: dashID}
if err := bus.Dispatch(c.Req.Context(), &query); err != nil {
if err := hs.SQLStore.IsStarredByUserCtx(c.Req.Context(), &query); err != nil {
return false, err
}
@ -46,7 +45,6 @@ func dashboardGuardianResponse(err error) response.Response {
if err != nil {
return response.Error(500, "Error while checking dashboard permissions", err)
}
return response.Error(403, "Access denied to this dashboard", nil)
}
@ -78,11 +76,10 @@ func (hs *HTTPServer) TrimDashboard(c *models.ReqContext) response.Response {
func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
uid := web.Params(c.Req)[":uid"]
dash, rsp := getDashboardHelper(c.Req.Context(), c.OrgId, 0, uid)
dash, rsp := hs.getDashboardHelper(c.Req.Context(), c.OrgId, 0, uid)
if rsp != nil {
return rsp
}
// When dash contains only keys id, uid that means dashboard data is not valid and json decode failed.
if dash.Data != nil {
isEmptyData := true
@ -96,28 +93,25 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
return response.Error(500, "Error while loading dashboard, dashboard data is invalid", nil)
}
}
guardian := guardian.New(c.Req.Context(), dash.Id, c.OrgId, c.SignedInUser)
if canView, err := guardian.CanView(); err != nil || !canView {
return dashboardGuardianResponse(err)
}
canEdit, _ := guardian.CanEdit()
canSave, _ := guardian.CanSave()
canAdmin, _ := guardian.CanAdmin()
isStarred, err := isDashboardStarredByUser(c, dash.Id)
isStarred, err := hs.isDashboardStarredByUser(c, dash.Id)
if err != nil {
return response.Error(500, "Error while checking if dashboard was starred by user", err)
}
// Finding creator and last updater of the dashboard
updater, creator := anonString, anonString
if dash.UpdatedBy > 0 {
updater = getUserLogin(c.Req.Context(), dash.UpdatedBy)
updater = hs.getUserLogin(c.Req.Context(), dash.UpdatedBy)
}
if dash.CreatedBy > 0 {
creator = getUserLogin(c.Req.Context(), dash.CreatedBy)
creator = hs.getUserLogin(c.Req.Context(), dash.CreatedBy)
}
meta := dtos.DashboardMeta{
@ -143,7 +137,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
// lookup folder title
if dash.FolderId > 0 {
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
if err := bus.Dispatch(c.Req.Context(), &query); err != nil {
if err := hs.SQLStore.GetDashboard(c.Req.Context(), &query); err != nil {
if errors.Is(err, models.ErrFolderNotFound) {
return response.Error(404, "Folder not found", err)
}
@ -195,16 +189,16 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
return response.JSON(200, dto)
}
func getUserLogin(ctx context.Context, userID int64) string {
func (hs *HTTPServer) getUserLogin(ctx context.Context, userID int64) string {
query := models.GetUserByIdQuery{Id: userID}
err := bus.Dispatch(ctx, &query)
err := hs.SQLStore.GetUserById(ctx, &query)
if err != nil {
return anonString
}
return query.Result.Login
}
func getDashboardHelper(ctx context.Context, orgID int64, id int64, uid string) (*models.Dashboard, response.Response) {
func (hs *HTTPServer) getDashboardHelper(ctx context.Context, orgID int64, id int64, uid string) (*models.Dashboard, response.Response) {
var query models.GetDashboardQuery
if len(uid) > 0 {
@ -213,37 +207,22 @@ func getDashboardHelper(ctx context.Context, orgID int64, id int64, uid string)
query = models.GetDashboardQuery{Id: id, OrgId: orgID}
}
if err := bus.Dispatch(ctx, &query); err != nil {
if err := hs.SQLStore.GetDashboard(ctx, &query); err != nil {
return nil, response.Error(404, "Dashboard not found", err)
}
return query.Result, nil
}
func (hs *HTTPServer) DeleteDashboardBySlug(c *models.ReqContext) response.Response {
query := models.GetDashboardsBySlugQuery{OrgId: c.OrgId, Slug: web.Params(c.Req)[":slug"]}
if err := bus.Dispatch(c.Req.Context(), &query); err != nil {
return response.Error(500, "Failed to retrieve dashboards by slug", err)
}
if len(query.Result) > 1 {
return response.JSON(412, util.DynMap{"status": "multiple-slugs-exists", "message": models.ErrDashboardsWithSameSlugExists.Error()})
}
return hs.deleteDashboard(c)
}
func (hs *HTTPServer) DeleteDashboardByUID(c *models.ReqContext) response.Response {
return hs.deleteDashboard(c)
}
func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
dash, rsp := getDashboardHelper(c.Req.Context(), c.OrgId, 0, web.Params(c.Req)[":uid"])
dash, rsp := hs.getDashboardHelper(c.Req.Context(), c.OrgId, 0, web.Params(c.Req)[":uid"])
if rsp != nil {
return rsp
}
guardian := guardian.New(c.Req.Context(), dash.Id, c.OrgId, c.SignedInUser)
if canSave, err := guardian.CanSave(); err != nil || !canSave {
return dashboardGuardianResponse(err)
@ -254,7 +233,6 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
if err != nil {
hs.log.Error("Failed to disconnect library elements", "dashboard", dash.Id, "user", c.SignedInUser.UserId, "error", err)
}
svc := dashboards.NewService(hs.SQLStore)
err = svc.DeleteDashboard(c.Req.Context(), dash.Id, c.OrgId)
if err != nil {
@ -264,17 +242,14 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
}
}
return response.Error(500, "Failed to delete dashboard", err)
}
if hs.Live != nil {
err := hs.Live.GrafanaScope.Dashboards.DashboardDeleted(c.OrgId, c.ToUserDisplayDTO(), dash.Uid)
if err != nil {
hs.log.Error("Failed to broadcast delete info", "dashboard", dash.Uid, "error", err)
}
}
return response.JSON(200, util.DynMap{
"title": dash.Title,
"message": fmt.Sprintf("Dashboard %s deleted", dash.Title),
@ -415,7 +390,7 @@ func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) response.Response {
prefsQuery := models.GetPreferencesWithDefaultsQuery{User: c.SignedInUser}
homePage := hs.Cfg.HomePage
if err := hs.Bus.Dispatch(c.Req.Context(), &prefsQuery); err != nil {
if err := hs.SQLStore.GetPreferencesWithDefaults(c.Req.Context(), &prefsQuery); err != nil {
return response.Error(500, "Failed to get preferences", err)
}
@ -426,7 +401,7 @@ func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) response.Response {
if prefsQuery.Result.HomeDashboardId != 0 {
slugQuery := models.GetDashboardRefByIdQuery{Id: prefsQuery.Result.HomeDashboardId}
err := hs.Bus.Dispatch(c.Req.Context(), &slugQuery)
err := hs.SQLStore.GetDashboardUIDById(c.Req.Context(), &slugQuery)
if err == nil {
url := models.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
@ -495,7 +470,7 @@ func (hs *HTTPServer) addGettingStartedPanelToHomeDashboard(c *models.ReqContext
}
// GetDashboardVersions returns all dashboard versions as JSON
func GetDashboardVersions(c *models.ReqContext) response.Response {
func (hs *HTTPServer) GetDashboardVersions(c *models.ReqContext) response.Response {
dashID, err := strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
@ -513,7 +488,7 @@ func GetDashboardVersions(c *models.ReqContext) response.Response {
Start: c.QueryInt("start"),
}
if err := bus.Dispatch(c.Req.Context(), &query); err != nil {
if err := hs.SQLStore.GetDashboardVersions(c.Req.Context(), &query); err != nil {
return response.Error(404, fmt.Sprintf("No versions found for dashboardId %d", dashID), err)
}
@ -537,7 +512,7 @@ func GetDashboardVersions(c *models.ReqContext) response.Response {
}
// GetDashboardVersion returns the dashboard version with the given ID.
func GetDashboardVersion(c *models.ReqContext) response.Response {
func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Response {
dashID, err := strconv.ParseInt(web.Params(c.Req)[":dashboardId"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
@ -555,13 +530,13 @@ func GetDashboardVersion(c *models.ReqContext) response.Response {
Version: int(version),
}
if err := bus.Dispatch(c.Req.Context(), &query); err != nil {
if err := hs.SQLStore.GetDashboardVersion(c.Req.Context(), &query); err != nil {
return response.Error(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashID), err)
}
creator := anonString
if query.Result.CreatedBy > 0 {
creator = getUserLogin(c.Req.Context(), query.Result.CreatedBy)
creator = hs.getUserLogin(c.Req.Context(), query.Result.CreatedBy)
}
dashVersionMeta := &models.DashboardVersionMeta{
@ -638,7 +613,7 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext) response.Res
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
}
dash, rsp := getDashboardHelper(c.Req.Context(), c.OrgId, dashboardId, "")
dash, rsp := hs.getDashboardHelper(c.Req.Context(), c.OrgId, dashboardId, "")
if rsp != nil {
return rsp
}
@ -649,7 +624,7 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext) response.Res
}
versionQuery := models.GetDashboardVersionQuery{DashboardId: dash.Id, Version: apiCmd.Version, OrgId: c.OrgId}
if err := bus.Dispatch(c.Req.Context(), &versionQuery); err != nil {
if err := hs.SQLStore.GetDashboardVersion(c.Req.Context(), &versionQuery); err != nil {
return response.Error(404, "Dashboard version not found", nil)
}
@ -668,9 +643,9 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext) response.Res
return hs.postDashboard(c, saveCmd)
}
func GetDashboardTags(c *models.ReqContext) {
func (hs *HTTPServer) GetDashboardTags(c *models.ReqContext) {
query := models.GetDashboardTagsQuery{OrgId: c.OrgId}
err := bus.Dispatch(c.Req.Context(), &query)
err := hs.SQLStore.GetDashboardTags(c.Req.Context(), &query)
if err != nil {
c.JsonApiErr(500, "Failed to get tags from database", err)
return

@ -19,7 +19,7 @@ func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) response.
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
}
_, rsp := getDashboardHelper(c.Req.Context(), c.OrgId, dashID, "")
_, rsp := hs.getDashboardHelper(c.Req.Context(), c.OrgId, dashID, "")
if rsp != nil {
return rsp
}
@ -70,7 +70,7 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext) response.
return response.Error(http.StatusBadRequest, "dashboardId is invalid", err)
}
_, rsp := getDashboardHelper(c.Req.Context(), c.OrgId, dashID, "")
_, rsp := hs.getDashboardHelper(c.Req.Context(), c.OrgId, dashID, "")
if rsp != nil {
return rsp
}

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"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"
@ -23,21 +22,19 @@ import (
func TestDashboardPermissionAPIEndpoint(t *testing.T) {
t.Run("Dashboard permissions test", func(t *testing.T) {
settings := setting.NewCfg()
hs := &HTTPServer{Cfg: settings}
mockSQLStore := mockstore.NewSQLStoreMock()
hs := &HTTPServer{
Cfg: settings,
SQLStore: mockSQLStore,
}
t.Run("Given dashboard not exists", func(t *testing.T) {
setUp := func() {
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
return models.ErrDashboardNotFound
})
}
mock := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedError = models.ErrDashboardNotFound
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:dashboardId/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
callGetDashboardPermissions(sc, hs)
assert.Equal(t, 404, sc.resp.Code)
}, mock)
}, mockSQLStore)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -51,7 +48,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(t, sc)
assert.Equal(t, 404, sc.resp.Code)
},
@ -68,19 +64,15 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
getDashboardQueryResult := models.NewDashboard("Dash")
setUp := func() {
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
mock := mockstore.NewSQLStoreMock()
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = getDashboardQueryResult
mockSQLStore.ExpectedError = nil
hs.SQLStore = mockSQLStore
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:dashboardId/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
callGetDashboardPermissions(sc, hs)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -94,7 +86,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(t, sc)
assert.Equal(t, 403, sc.resp.Code)
},
@ -119,18 +110,12 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
},
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
mock := mockstore.NewSQLStoreMock()
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = models.NewDashboard("Dash")
hs.SQLStore = mockSQLStore
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:dashboardId/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
setUp()
callGetDashboardPermissions(sc, hs)
assert.Equal(t, 200, sc.resp.Code)
@ -141,7 +126,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
assert.Len(t, resp, 5)
assert.Equal(t, int64(2), resp[0].UserId)
assert.Equal(t, models.PERMISSION_VIEW, resp[0].Permission)
}, mock)
}, mockSQLStore)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -155,7 +140,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(t, sc)
assert.Equal(t, 200, sc.resp.Code)
},
@ -173,14 +157,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
CheckPermissionBeforeUpdateValue: true,
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserID: 1000, TeamID: 1, Permission: models.PERMISSION_ADMIN},
@ -193,7 +169,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(t, sc)
assert.Equal(t, 400, sc.resp.Code)
respJSON, err := jsonMap(sc.resp.Body.Bytes())
@ -214,14 +189,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserID: 1000, Permission: models.PERMISSION_ADMIN},
@ -234,7 +201,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(t, sc)
assert.Equal(t, 400, sc.resp.Code)
},
@ -285,14 +251,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
)
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserID: 1000, Permission: models.PERMISSION_ADMIN},
@ -305,7 +263,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(t, sc)
assert.Equal(t, 400, sc.resp.Code)
},
@ -336,14 +293,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
},
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
mock := mockstore.NewSQLStoreMock()
mockSQLStore := mockstore.NewSQLStoreMock()
var resp []*models.DashboardAclInfoDTO
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:dashboardId/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
@ -359,7 +309,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
assert.Equal(t, models.PERMISSION_EDIT, resp[0].Permission)
assert.Equal(t, int64(4), resp[1].UserId)
assert.Equal(t, models.PERMISSION_ADMIN, resp[1].Permission)
}, mock)
}, mockSQLStore)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -380,7 +330,6 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
routePattern: "/api/dashboards/id/:dashboardId/permissions",
cmd: cmd,
fn: func(sc *scenarioContext) {
setUp()
// TODO: Replace this fake with a fake SQLStore instead (once we can use an interface in its stead)
origUpdateDashboardACL := updateDashboardACL
t.Cleanup(func() {
@ -430,8 +379,6 @@ type updatePermissionContext struct {
func updateDashboardPermissionScenario(t *testing.T, ctx updatePermissionContext, hs *HTTPServer) {
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, ctx.url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {

@ -43,13 +43,8 @@ func TestGetHomeDashboard(t *testing.T) {
hs := &HTTPServer{
Cfg: cfg, Bus: bus.New(),
pluginStore: &fakePluginStore{},
SQLStore: mockstore.NewSQLStoreMock(),
}
hs.Bus.AddHandler(func(_ context.Context, query *models.GetPreferencesWithDefaultsQuery) error {
query.Result = &models.Preferences{
HomeDashboardId: 0,
}
return nil
})
tests := []struct {
name string
@ -85,10 +80,6 @@ func TestGetHomeDashboard(t *testing.T) {
}
}
type testState struct {
dashQueries []*models.GetDashboardQuery
}
func newTestLive(t *testing.T) *live.GrafanaLive {
features := featuremgmt.WithFeatures()
cfg := &setting.Cfg{AppURL: "http://localhost:3000/"}
@ -113,26 +104,23 @@ func newTestLive(t *testing.T) *live.GrafanaLive {
func TestDashboardAPIEndpoint(t *testing.T) {
t.Run("Given a dashboard with a parent folder which does not have an ACL", func(t *testing.T) {
setUp := func() *testState {
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 1
fakeDash.FolderId = 1
fakeDash.HasAcl = false
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardsBySlugQuery) error {
dashboards := []*models.Dashboard{fakeDash}
query.Result = dashboards
return nil
})
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 1
fakeDash.FolderId = 1
fakeDash.HasAcl = false
state := &testState{}
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = fakeDash
mockSQLStore.ExpectedDashboardVersion = &models.DashboardVersion{}
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = fakeDash
state.dashQueries = append(state.dashQueries, query)
return nil
})
hs := &HTTPServer{
Cfg: setting.NewCfg(),
pluginStore: &fakePluginStore{},
SQLStore: mockSQLStore,
}
hs.SQLStore = mockSQLStore
setUp := func() {
viewerRole := models.ROLE_VIEWER
editorRole := models.ROLE_EDITOR
@ -145,13 +133,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
query.Result = aclMockResp
return nil
})
bus.AddHandler("test", func(ctx context.Context, query *models.GetTeamsByUserQuery) error {
query.Result = []*models.TeamDTO{}
return nil
})
return state
}
// This tests two scenarios:
@ -160,127 +141,94 @@ func TestDashboardAPIEndpoint(t *testing.T) {
t.Run("When user is an Org Viewer", func(t *testing.T) {
role := models.ROLE_VIEWER
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
setUp()
sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200(sc)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.False(t, dash.Meta.CanEdit)
assert.False(t, dash.Meta.CanSave)
assert.False(t, dash.Meta.CanAdmin)
}, mock)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
callDeleteDashboardBySlug(sc, &HTTPServer{
Cfg: setting.NewCfg(),
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
})
assert.Equal(t, 403, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUp()
sc.sqlStore = mockSQLStore
callGetDashboardVersion(sc)
hs.callGetDashboardVersion(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUp()
sc.sqlStore = mockSQLStore
callGetDashboardVersions(sc)
hs.callGetDashboardVersions(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
t.Run("When user is an Org Editor", func(t *testing.T) {
role := models.ROLE_EDITOR
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
setUp()
sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200(sc)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.True(t, dash.Meta.CanEdit)
assert.True(t, dash.Meta.CanSave)
assert.False(t, dash.Meta.CanAdmin)
}, mock)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
callDeleteDashboardBySlug(sc, &HTTPServer{
Cfg: setting.NewCfg(),
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
SQLStore: mock,
})
assert.Equal(t, 200, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUp()
sc.sqlStore = mockSQLStore
hs.callGetDashboardVersion(sc)
callGetDashboardVersion(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUp()
hs.callGetDashboardVersions(sc)
callGetDashboardVersions(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
})
t.Run("Given a dashboard with a parent folder which has an ACL", func(t *testing.T) {
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 1
fakeDash.FolderId = 1
fakeDash.HasAcl = true
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = fakeDash
mockSQLStore.ExpectedDashboardVersion = &models.DashboardVersion{}
hs := &HTTPServer{
Cfg: setting.NewCfg(),
Live: newTestLive(t),
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
SQLStore: mockstore.NewSQLStoreMock(),
SQLStore: mockSQLStore,
}
hs.SQLStore = mockSQLStore
setUp := func() *testState {
state := &testState{}
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 1
fakeDash.FolderId = 1
fakeDash.HasAcl = true
setUp := func() {
origCanEdit := setting.ViewersCanEdit
t.Cleanup(func() {
setting.ViewersCanEdit = origCanEdit
})
setting.ViewersCanEdit = false
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardsBySlugQuery) error {
dashboards := []*models.Dashboard{fakeDash}
query.Result = dashboards
return nil
})
aclMockResp := []*models.DashboardAclInfoDTO{
{
DashboardId: 1,
@ -293,19 +241,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
query.Result = aclMockResp
return nil
})
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = fakeDash
state.dashQueries = append(state.dashQueries, query)
return nil
})
bus.AddHandler("test", func(ctx context.Context, query *models.GetTeamsByUserQuery) error {
query.Result = []*models.TeamDTO{}
return nil
})
return state
}
// This tests six scenarios:
@ -318,81 +253,78 @@ func TestDashboardAPIEndpoint(t *testing.T) {
t.Run("When user is an Org Viewer and has no permissions for this dashboard", func(t *testing.T) {
role := models.ROLE_VIEWER
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
setUp()
sc.sqlStore = mockSQLStore
sc.handlerFunc = hs.GetDashboard
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
setUp()
sc.sqlStore = mockSQLStore
hs.callDeleteDashboardByUID(t, sc, nil)
callDeleteDashboardByUID(sc, hs)
assert.Equal(t, 403, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUp()
sc.sqlStore = mockSQLStore
hs.callGetDashboardVersion(sc)
callGetDashboardVersion(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUp()
hs.callGetDashboardVersions(sc)
callGetDashboardVersions(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
t.Run("When user is an Org Editor and has no permissions for this dashboard", func(t *testing.T) {
role := models.ROLE_EDITOR
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
setUp()
sc.sqlStore = mockSQLStore
sc.handlerFunc = hs.GetDashboard
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUp()
setUp()
hs.callDeleteDashboardByUID(t, sc, nil)
callDeleteDashboardByUID(sc, hs)
assert.Equal(t, 403, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1",
"/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUp()
hs.callGetDashboardVersion(sc)
callGetDashboardVersion(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions",
"/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUp()
hs.callGetDashboardVersions(sc)
callGetDashboardVersions(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
t.Run("When user is an Org Viewer but has an edit permission", func(t *testing.T) {
@ -402,54 +334,64 @@ func TestDashboardAPIEndpoint(t *testing.T) {
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_EDIT},
}
setUpInner := func() *testState {
state := setUp()
setUpInner := func() {
setUp()
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardAclInfoListQuery) error {
query.Result = mockResult
return nil
})
return state
}
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
setUpInner()
sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200(sc)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.True(t, dash.Meta.CanEdit)
assert.True(t, dash.Meta.CanSave)
assert.False(t, dash.Meta.CanAdmin)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
callDeleteDashboardByUID(sc, hs)
setUpInner()
mockDashboard := &dashboards.FakeDashboardService{
SaveDashboardResult: &models.Dashboard{
Id: fakeDash.Id,
Uid: "uid",
Title: "Dash",
Slug: "dash",
Version: 2,
},
}
hs.callDeleteDashboardByUID(t, sc, mockDashboard)
assert.Equal(t, 200, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
mockSQLStore.ExpectedDashboardVersion = &models.DashboardVersion{}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUpInner()
sc.sqlStore = mockSQLStore
hs.callGetDashboardVersion(sc)
callGetDashboardVersion(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUpInner()
hs.callGetDashboardVersions(sc)
callGetDashboardVersions(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
t.Run("When user is an Org Viewer and viewers can edit", func(t *testing.T) {
role := models.ROLE_VIEWER
setUpInner := func() *testState {
state := setUp()
setUpInner := func() {
setUp()
mockResult := []*models.DashboardAclInfoDTO{
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
@ -465,36 +407,33 @@ func TestDashboardAPIEndpoint(t *testing.T) {
setting.ViewersCanEdit = origCanEdit
})
setting.ViewersCanEdit = true
return state
}
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
setUpInner()
require.True(t, setting.ViewersCanEdit)
sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200(sc)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.True(t, dash.Meta.CanEdit)
assert.False(t, dash.Meta.CanSave)
assert.False(t, dash.Meta.CanAdmin)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
setUpInner()
callDeleteDashboardByUID(sc, hs)
hs.callDeleteDashboardByUID(t, sc, nil)
assert.Equal(t, 403, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
})
t.Run("When user is an Org Viewer but has an admin permission", func(t *testing.T) {
role := models.ROLE_VIEWER
setUpInner := func() *testState {
state := setUp()
setUpInner := func() {
setUp()
mockResult := []*models.DashboardAclInfoDTO{
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_ADMIN},
@ -503,48 +442,46 @@ func TestDashboardAPIEndpoint(t *testing.T) {
query.Result = mockResult
return nil
})
return state
}
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner()
sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200(sc)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.True(t, dash.Meta.CanEdit)
assert.True(t, dash.Meta.CanSave)
assert.True(t, dash.Meta.CanAdmin)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
setUpInner()
sc.sqlStore = mockSQLStore
hs.callDeleteDashboardByUID(t, sc, &dashboards.FakeDashboardService{})
callDeleteDashboardByUID(sc, hs)
assert.Equal(t, 200, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUpInner()
callGetDashboardVersion(sc)
hs.callGetDashboardVersion(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUpInner()
callGetDashboardVersions(sc)
hs.callGetDashboardVersions(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
t.Run("When user is an Org Editor but has a view permission", func(t *testing.T) {
role := models.ROLE_EDITOR
setUpInner := func() *testState {
state := setUp()
setUpInner := func() {
setUp()
mockResult := []*models.DashboardAclInfoDTO{
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
@ -553,39 +490,37 @@ func TestDashboardAPIEndpoint(t *testing.T) {
query.Result = mockResult
return nil
})
return state
}
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner()
sc.sqlStore = mockSQLStore
dash := getDashboardShouldReturn200(sc)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
assert.False(t, dash.Meta.CanEdit)
assert.False(t, dash.Meta.CanSave)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
state := setUpInner()
setUpInner()
hs.callDeleteDashboardByUID(t, sc, nil)
callDeleteDashboardByUID(sc, hs)
assert.Equal(t, 403, sc.resp.Code)
assert.Equal(t, "abcdefghi", state.dashQueries[0].Uid)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUpInner()
hs.callGetDashboardVersion(sc)
callGetDashboardVersion(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUpInner()
hs.callGetDashboardVersions(sc)
callGetDashboardVersions(sc)
assert.Equal(t, 403, sc.resp.Code)
}, mock)
}, mockSQLStore)
})
})
@ -599,12 +534,6 @@ func TestDashboardAPIEndpoint(t *testing.T) {
dashTwo.Id = 4
dashTwo.FolderId = 3
dashTwo.HasAcl = false
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardsBySlugQuery) error {
dashboards := []*models.Dashboard{dashOne, dashTwo}
query.Result = dashboards
return nil
})
})
t.Run("Post dashboard response tests", func(t *testing.T) {
@ -842,26 +771,10 @@ func TestDashboardAPIEndpoint(t *testing.T) {
t.Run("Given dashboard in folder being restored should restore to folder", func(t *testing.T) {
const folderID int64 = 1
setUp := func() {
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 2
fakeDash.FolderId = folderID
fakeDash.HasAcl = false
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = fakeDash
return nil
})
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardVersionQuery) error {
query.Result = &models.DashboardVersion{
DashboardId: 2,
Version: 1,
Data: fakeDash.Data,
}
return nil
})
}
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 2
fakeDash.FolderId = folderID
fakeDash.HasAcl = false
mock := &dashboards.FakeDashboardService{
SaveDashboardResult: &models.Dashboard{
@ -876,40 +789,29 @@ func TestDashboardAPIEndpoint(t *testing.T) {
cmd := dtos.RestoreDashboardVersionCommand{
Version: 1,
}
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = fakeDash
mockSQLStore.ExpectedDashboardVersion = &models.DashboardVersion{
DashboardId: 2,
Version: 1,
Data: fakeDash.Data,
}
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
"/api/dashboards/id/:dashboardId/restore", mock, cmd, func(sc *scenarioContext) {
setUp()
callRestoreDashboardVersion(sc)
assert.Equal(t, 200, sc.resp.Code)
dto := mock.SavedDashboards[0]
assert.Equal(t, folderID, dto.Dashboard.FolderId)
assert.Equal(t, "Child dash", dto.Dashboard.Title)
assert.Equal(t, "Restored from version 1", dto.Message)
})
}, mockSQLStore)
})
t.Run("Given dashboard in general folder being restored should restore to general folder", func(t *testing.T) {
setUp := func() {
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 2
fakeDash.HasAcl = false
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = fakeDash
return nil
})
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardVersionQuery) error {
query.Result = &models.DashboardVersion{
DashboardId: 2,
Version: 1,
Data: fakeDash.Data,
}
return nil
})
}
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 2
fakeDash.HasAcl = false
mock := &dashboards.FakeDashboardService{
SaveDashboardResult: &models.Dashboard{
@ -924,33 +826,27 @@ func TestDashboardAPIEndpoint(t *testing.T) {
cmd := dtos.RestoreDashboardVersionCommand{
Version: 1,
}
mockSQLStore := mockstore.NewSQLStoreMock()
mockSQLStore.ExpectedDashboard = fakeDash
mockSQLStore.ExpectedDashboardVersion = &models.DashboardVersion{
DashboardId: 2,
Version: 1,
Data: fakeDash.Data,
}
restoreDashboardVersionScenario(t, "When calling POST on", "/api/dashboards/id/1/restore",
"/api/dashboards/id/:dashboardId/restore", mock, cmd, func(sc *scenarioContext) {
setUp()
callRestoreDashboardVersion(sc)
assert.Equal(t, 200, sc.resp.Code)
dto := mock.SavedDashboards[0]
assert.Equal(t, int64(0), dto.Dashboard.FolderId)
assert.Equal(t, "Child dash", dto.Dashboard.Title)
assert.Equal(t, "Restored from version 1", dto.Message)
})
}, mockSQLStore)
})
t.Run("Given provisioned dashboard", func(t *testing.T) {
setUp := func() {
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardsBySlugQuery) error {
query.Result = []*models.Dashboard{{}}
return nil
})
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
require.NoError(t, err)
query.Result = &models.Dashboard{Id: 1, Data: dataValue}
return nil
})
origGetProvisionedData := dashboards.GetProvisionedData
t.Cleanup(func() {
dashboards.GetProvisionedData = origGetProvisionedData
@ -966,25 +862,17 @@ func TestDashboardAPIEndpoint(t *testing.T) {
return nil
})
}
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/db/abcdefghi", "/api/dashboards/db/:uid", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
callDeleteDashboardBySlug(sc, &HTTPServer{
Cfg: setting.NewCfg(),
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
SQLStore: mock,
})
assert.Equal(t, 400, sc.resp.Code)
result := sc.ToJSON()
assert.Equal(t, models.ErrDashboardCannotDeleteProvisionedDashboard.Error(), result.Get("error").MustString())
}, mock)
mockSQLStore := mockstore.NewSQLStoreMock()
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
require.NoError(t, err)
mockSQLStore.ExpectedDashboard = &models.Dashboard{Id: 1, Data: dataValue}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
require.NoError(t, err)
mockSQLStore.ExpectedDashboard = &models.Dashboard{Id: 1, Data: dataValue}
sc.sqlStore = mockSQLStore
mock := provisioning.NewProvisioningServiceMock(context.Background())
mock.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
return "/tmp/grafana/dashboards"
@ -993,9 +881,8 @@ func TestDashboardAPIEndpoint(t *testing.T) {
dash := getDashboardShouldReturn200WithConfig(sc, mock)
assert.Equal(t, filepath.Join("test", "dashboard1.json"), dash.Meta.ProvisionedExternalId)
}, mock)
}, mockSQLStore)
mockSQLStore := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When allowUiUpdates is true and calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
@ -1014,7 +901,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
LibraryElementService: &mockLibraryElementService{},
SQLStore: mockSQLStore,
}
callGetDashboard(sc, hs)
hs.callGetDashboard(sc)
assert.Equal(t, 200, sc.resp.Code)
@ -1023,7 +910,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, false, dash.Meta.Provisioned)
}, mock)
}, mockSQLStore)
})
}
@ -1044,7 +931,7 @@ func getDashboardShouldReturn200WithConfig(sc *scenarioContext, provisioningServ
SQLStore: sc.sqlStore,
}
callGetDashboard(sc, hs)
hs.callGetDashboard(sc)
require.Equal(sc.t, 200, sc.resp.Code)
@ -1059,44 +946,41 @@ func getDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta
return getDashboardShouldReturn200WithConfig(sc, nil)
}
func callGetDashboard(sc *scenarioContext, hs *HTTPServer) {
func (hs *HTTPServer) callGetDashboard(sc *scenarioContext) {
sc.handlerFunc = hs.GetDashboard
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
}
func callGetDashboardVersion(sc *scenarioContext) {
func (hs *HTTPServer) callGetDashboardVersion(sc *scenarioContext) {
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardVersionQuery) error {
query.Result = &models.DashboardVersion{}
return nil
})
sc.handlerFunc = GetDashboardVersion
sc.handlerFunc = hs.GetDashboardVersion
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
}
func callGetDashboardVersions(sc *scenarioContext) {
func (hs *HTTPServer) callGetDashboardVersions(sc *scenarioContext) {
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardVersionsQuery) error {
query.Result = []*models.DashboardVersionDTO{}
return nil
})
sc.handlerFunc = GetDashboardVersions
sc.handlerFunc = hs.GetDashboardVersions
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
}
func callDeleteDashboardBySlug(sc *scenarioContext, hs *HTTPServer) {
func (hs *HTTPServer) callDeleteDashboardByUID(t *testing.T, sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService) {
bus.AddHandler("test", func(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
return nil
})
sc.handlerFunc = hs.DeleteDashboardBySlug
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
}
func callDeleteDashboardByUID(sc *scenarioContext, hs *HTTPServer) {
bus.AddHandler("test", func(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
return nil
origNewDashboardService := dashboards.NewService
t.Cleanup(func() {
dashboards.NewService = origNewDashboardService
})
dashboards.MockDashboardService(mockDashboard)
sc.handlerFunc = hs.DeleteDashboardByUID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
@ -1189,11 +1073,13 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
}
func restoreDashboardVersionScenario(t *testing.T, desc string, url string, routePattern string,
mock *dashboards.FakeDashboardService, cmd dtos.RestoreDashboardVersionCommand, fn scenarioFunc) {
mock *dashboards.FakeDashboardService, cmd dtos.RestoreDashboardVersionCommand, fn scenarioFunc,
sqlStore sqlstore.Store) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
cfg := setting.NewCfg()
mockSQLStore := mockstore.NewSQLStoreMock()
hs := HTTPServer{
Cfg: cfg,
Bus: bus.GetBus(),
@ -1202,9 +1088,11 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
QuotaService: &quota.QuotaService{Cfg: cfg},
LibraryPanelService: &mockLibraryPanelService{},
LibraryElementService: &mockLibraryElementService{},
SQLStore: sqlStore,
}
sc := setupScenarioContext(t, url)
sc.sqlStore = mockSQLStore
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c

@ -47,7 +47,7 @@ func (hs *HTTPServer) GetFolderByUID(c *models.ReqContext) response.Response {
}
g := guardian.New(c.Req.Context(), folder.Id, c.OrgId, c.SignedInUser)
return response.JSON(200, toFolderDto(c.Req.Context(), g, folder))
return response.JSON(200, hs.toFolderDto(c.Req.Context(), g, folder))
}
func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
@ -64,7 +64,7 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
}
g := guardian.New(c.Req.Context(), folder.Id, c.OrgId, c.SignedInUser)
return response.JSON(200, toFolderDto(c.Req.Context(), g, folder))
return response.JSON(200, hs.toFolderDto(c.Req.Context(), g, folder))
}
func (hs *HTTPServer) CreateFolder(c *models.ReqContext) response.Response {
@ -86,7 +86,7 @@ func (hs *HTTPServer) CreateFolder(c *models.ReqContext) response.Response {
}
g := guardian.New(c.Req.Context(), folder.Id, c.OrgId, c.SignedInUser)
return response.JSON(200, toFolderDto(c.Req.Context(), g, folder))
return response.JSON(200, hs.toFolderDto(c.Req.Context(), g, folder))
}
func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
@ -101,7 +101,7 @@ func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
}
g := guardian.New(c.Req.Context(), cmd.Result.Id, c.OrgId, c.SignedInUser)
return response.JSON(200, toFolderDto(c.Req.Context(), g, cmd.Result))
return response.JSON(200, hs.toFolderDto(c.Req.Context(), g, cmd.Result))
}
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
@ -126,7 +126,7 @@ func (hs *HTTPServer) DeleteFolder(c *models.ReqContext) response.Response { //
})
}
func toFolderDto(ctx context.Context, g guardian.DashboardGuardian, folder *models.Folder) dtos.Folder {
func (hs *HTTPServer) toFolderDto(ctx context.Context, g guardian.DashboardGuardian, folder *models.Folder) dtos.Folder {
canEdit, _ := g.CanEdit()
canSave, _ := g.CanSave()
canAdmin, _ := g.CanAdmin()
@ -134,10 +134,10 @@ func toFolderDto(ctx context.Context, g guardian.DashboardGuardian, folder *mode
// Finding creator and last updater of the folder
updater, creator := anonString, anonString
if folder.CreatedBy > 0 {
creator = getUserLogin(ctx, folder.CreatedBy)
creator = hs.getUserLogin(ctx, folder.CreatedBy)
}
if folder.UpdatedBy > 0 {
updater = getUserLogin(ctx, folder.UpdatedBy)
updater = hs.getUserLogin(ctx, folder.UpdatedBy)
}
return dtos.Folder{

@ -27,11 +27,9 @@ var shadowSearchCounter = prometheus.NewCounterVec(
)
func init() {
bus.AddHandler("sql", GetDashboardTags)
bus.AddHandler("sql", GetDashboardSlugById)
bus.AddHandler("sql", GetDashboardsByPluginId)
bus.AddHandler("sql", GetDashboardPermissionsForUser)
bus.AddHandler("sql", GetDashboardsBySlug)
bus.AddHandler("sql", HasAdminPermissionInFolders)
prometheus.MustRegister(shadowSearchCounter)
@ -40,6 +38,7 @@ func init() {
func (ss *SQLStore) addDashboardQueryAndCommandHandlers() {
bus.AddHandler("sql", ss.GetDashboard)
bus.AddHandler("sql", ss.GetDashboardUIDById)
bus.AddHandler("sql", ss.GetDashboardTags)
bus.AddHandler("sql", ss.SearchDashboards)
bus.AddHandler("sql", ss.DeleteDashboard)
bus.AddHandler("sql", ss.GetDashboards)
@ -375,7 +374,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard
}
}
func GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error {
func (ss *SQLStore) GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error {
sql := `SELECT
COUNT(*) as count,
term
@ -601,17 +600,6 @@ func GetDashboardSlugById(ctx context.Context, query *models.GetDashboardSlugByI
return nil
}
func GetDashboardsBySlug(ctx context.Context, query *models.GetDashboardsBySlugQuery) error {
var dashboards []*models.Dashboard
if err := x.Where("org_id=? AND slug=?", query.OrgId, query.Slug).Find(&dashboards); err != nil {
return err
}
query.Result = dashboards
return nil
}
func (ss *SQLStore) GetDashboardUIDById(ctx context.Context, query *models.GetDashboardRefByIdQuery) error {
return ss.WithDbSession(ctx, func(dbSession *DBSession) error {
var rawSQL = `SELECT uid, slug from dashboard WHERE Id=?`

@ -292,7 +292,7 @@ func TestDashboardDataAccess(t *testing.T) {
setup()
query := models.GetDashboardTagsQuery{OrgId: 1}
err := GetDashboardTags(context.Background(), &query)
err := sqlStore.GetDashboardTags(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(query.Result), 2)

@ -12,10 +12,14 @@ type SQLStoreMock struct {
LastGetAlertsQuery *models.GetAlertsQuery
LatestUserId int64
ExpectedUser *models.User
ExpectedDatasource *models.DataSource
ExpectedAlert *models.Alert
ExpectedPluginSetting *models.PluginSetting
ExpectedUser *models.User
ExpectedDatasource *models.DataSource
ExpectedAlert *models.Alert
ExpectedPluginSetting *models.PluginSetting
ExpectedDashboard *models.Dashboard
ExpectedDashboards []*models.Dashboard
ExpectedDashboardVersion *models.DashboardVersion
ExpectedDashboardAclInfoList []*models.DashboardAclInfoDTO
ExpectedError error
}
@ -73,7 +77,7 @@ func (m *SQLStoreMock) DeleteOrg(ctx context.Context, cmd *models.DeleteOrgComma
}
func (m *SQLStoreMock) GetProvisionedDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) {
return nil, m.ExpectedError
return &models.DashboardProvisioning{}, m.ExpectedError
}
func (m *SQLStoreMock) GetProvisionedDataByDashboardUID(orgID int64, dashboardUID string) (*models.DashboardProvisioning, error) {
@ -204,6 +208,7 @@ func (m *SQLStoreMock) GetTeamById(ctx context.Context, query *models.GetTeamByI
}
func (m *SQLStoreMock) GetTeamsByUser(ctx context.Context, query *models.GetTeamsByUserQuery) error {
query.Result = []*models.TeamDTO{}
return m.ExpectedError
}
@ -236,6 +241,7 @@ func (m *SQLStoreMock) WithDbSession(ctx context.Context, callback sqlstore.DBTr
}
func (m *SQLStoreMock) GetPreferencesWithDefaults(ctx context.Context, query *models.GetPreferencesWithDefaultsQuery) error {
query.Result = &models.Preferences{}
return m.ExpectedError
}
@ -265,6 +271,7 @@ func (m *SQLStoreMock) UpdatePluginSettingVersion(ctx context.Context, cmd *mode
}
func (m *SQLStoreMock) IsStarredByUserCtx(ctx context.Context, query *models.IsStarredByUserQuery) error {
query.Result = false
return m.ExpectedError
}
@ -317,6 +324,7 @@ func (m *SQLStoreMock) InTransaction(ctx context.Context, fn func(ctx context.Co
}
func (m *SQLStoreMock) GetDashboardVersion(ctx context.Context, query *models.GetDashboardVersionQuery) error {
query.Result = m.ExpectedDashboardVersion
return m.ExpectedError
}
@ -337,6 +345,7 @@ func (m *SQLStoreMock) UpdateDashboardACLCtx(ctx context.Context, dashboardID in
}
func (m *SQLStoreMock) GetDashboardAclInfoList(ctx context.Context, query *models.GetDashboardAclInfoListQuery) error {
query.Result = m.ExpectedDashboardAclInfoList
return m.ExpectedError
}
@ -423,9 +432,14 @@ func (m *SQLStoreMock) SaveDashboard(cmd models.SaveDashboardCommand) (*models.D
}
func (m *SQLStoreMock) GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error {
query.Result = m.ExpectedDashboard
return m.ExpectedError
}
func (m *SQLStoreMock) GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error {
return nil // TODO: Implement
}
func (m *SQLStoreMock) GetFolderByTitle(orgID int64, title string) (*models.Dashboard, error) {
return nil, m.ExpectedError
}
@ -435,6 +449,8 @@ func (m *SQLStoreMock) SearchDashboards(ctx context.Context, query *search.FindP
}
func (m *SQLStoreMock) DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error {
cmd.Id = m.ExpectedDashboard.Id
cmd.OrgId = m.ExpectedDashboard.OrgId
return m.ExpectedError
}

@ -106,6 +106,7 @@ type Store interface {
RemoveOrgUser(ctx context.Context, cmd *models.RemoveOrgUserCommand) error
SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error)
GetDashboard(ctx context.Context, query *models.GetDashboardQuery) error
GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error
GetFolderByTitle(orgID int64, title string) (*models.Dashboard, error)
SearchDashboards(ctx context.Context, query *search.FindPersistedDashboardsQuery) error
DeleteDashboard(ctx context.Context, cmd *models.DeleteDashboardCommand) error

Loading…
Cancel
Save