mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
WIP: folder api. #10630
This commit is contained in:
parent
ee57ed10f9
commit
f08932b78a
@ -240,6 +240,15 @@ func (hs *HttpServer) registerRoutes() {
|
|||||||
apiRoute.Any("/datasources/proxy/:id/*", reqSignedIn, hs.ProxyDataSourceRequest)
|
apiRoute.Any("/datasources/proxy/:id/*", reqSignedIn, hs.ProxyDataSourceRequest)
|
||||||
apiRoute.Any("/datasources/proxy/:id", reqSignedIn, hs.ProxyDataSourceRequest)
|
apiRoute.Any("/datasources/proxy/:id", reqSignedIn, hs.ProxyDataSourceRequest)
|
||||||
|
|
||||||
|
// Folders
|
||||||
|
apiRoute.Group("/folders", func(folderRoute RouteRegister) {
|
||||||
|
folderRoute.Get("/", wrap(GetFolders))
|
||||||
|
folderRoute.Get("/:id", wrap(GetFolderById))
|
||||||
|
folderRoute.Post("/", bind(m.CreateFolderCommand{}), wrap(CreateFolder))
|
||||||
|
folderRoute.Put("/:id", bind(m.UpdateFolderCommand{}), wrap(UpdateFolder))
|
||||||
|
folderRoute.Delete("/:id", wrap(DeleteFolder))
|
||||||
|
})
|
||||||
|
|
||||||
// Dashboard
|
// Dashboard
|
||||||
apiRoute.Group("/dashboards", func(dashboardRoute RouteRegister) {
|
apiRoute.Group("/dashboards", func(dashboardRoute RouteRegister) {
|
||||||
dashboardRoute.Get("/db/:slug", wrap(GetDashboard))
|
dashboardRoute.Get("/db/:slug", wrap(GetDashboard))
|
||||||
@ -252,8 +261,6 @@ func (hs *HttpServer) registerRoutes() {
|
|||||||
dashboardRoute.Get("/tags", GetDashboardTags)
|
dashboardRoute.Get("/tags", GetDashboardTags)
|
||||||
dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
|
dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
|
||||||
|
|
||||||
dashboardRoute.Get("/folders", wrap(GetFoldersForSignedInUser))
|
|
||||||
|
|
||||||
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute RouteRegister) {
|
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute RouteRegister) {
|
||||||
dashIdRoute.Get("/versions", wrap(GetDashboardVersions))
|
dashIdRoute.Get("/versions", wrap(GetDashboardVersions))
|
||||||
dashIdRoute.Get("/versions/:id", wrap(GetDashboardVersion))
|
dashIdRoute.Get("/versions/:id", wrap(GetDashboardVersion))
|
||||||
|
@ -130,6 +130,11 @@ func getDashboardHelper(orgId int64, slug string, id int64) (*m.Dashboard, Respo
|
|||||||
if err := bus.Dispatch(&query); err != nil {
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
return nil, ApiError(404, "Dashboard not found", err)
|
return nil, ApiError(404, "Dashboard not found", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if query.Result.IsFolder {
|
||||||
|
return nil, ApiError(404, "Dashboard not found", m.ErrDashboardNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
return query.Result, nil
|
return query.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +169,11 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
|
|||||||
// if new dashboard, use parent folder permissions instead
|
// if new dashboard, use parent folder permissions instead
|
||||||
if dashId == 0 {
|
if dashId == 0 {
|
||||||
dashId = cmd.FolderId
|
dashId = cmd.FolderId
|
||||||
|
} else {
|
||||||
|
_, rsp := getDashboardHelper(c.OrgId, "", dashId)
|
||||||
|
if rsp != nil {
|
||||||
|
return rsp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guardian := guardian.NewDashboardGuardian(dashId, c.OrgId, c.SignedInUser)
|
guardian := guardian.NewDashboardGuardian(dashId, c.OrgId, c.SignedInUser)
|
||||||
@ -439,19 +449,3 @@ func GetDashboardTags(c *middleware.Context) {
|
|||||||
|
|
||||||
c.JSON(200, query.Result)
|
c.JSON(200, query.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFoldersForSignedInUser(c *middleware.Context) Response {
|
|
||||||
title := c.Query("query")
|
|
||||||
query := m.GetFoldersForSignedInUserQuery{
|
|
||||||
OrgId: c.OrgId,
|
|
||||||
SignedInUser: c.SignedInUser,
|
|
||||||
Title: title,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := bus.Dispatch(&query)
|
|
||||||
if err != nil {
|
|
||||||
return ApiError(500, "Failed to get folders from database", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Json(200, query.Result)
|
|
||||||
}
|
|
||||||
|
@ -33,6 +33,44 @@ func (repo *fakeDashboardRepo) SaveDashboard(json *dashboards.SaveDashboardItem)
|
|||||||
var fakeRepo *fakeDashboardRepo
|
var fakeRepo *fakeDashboardRepo
|
||||||
|
|
||||||
func TestDashboardApiEndpoint(t *testing.T) {
|
func TestDashboardApiEndpoint(t *testing.T) {
|
||||||
|
Convey("Given a folder", t, func() {
|
||||||
|
fakeFolder := m.NewDashboardFolder("Folder")
|
||||||
|
fakeFolder.Id = 1
|
||||||
|
fakeFolder.HasAcl = false
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
|
||||||
|
query.Result = fakeFolder
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd := m.SaveDashboardCommand{
|
||||||
|
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"title": fakeFolder.Title,
|
||||||
|
"id": fakeFolder.Id,
|
||||||
|
}),
|
||||||
|
IsFolder: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey("When user is an Org Editor", func() {
|
||||||
|
role := m.ROLE_EDITOR
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/1", "/api/dashboards/:id", role, func(sc *scenarioContext) {
|
||||||
|
CallGetDashboard(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
postDashboardScenario("When calling POST on", "/api/dashboards", "/api/dashboards", role, cmd, func(sc *scenarioContext) {
|
||||||
|
CallPostDashboard(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/1", "/api/dashboards/:id", role, func(sc *scenarioContext) {
|
||||||
|
CallDeleteDashboard(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 404)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Given a dashboard with a parent folder which does not have an acl", t, func() {
|
Convey("Given a dashboard with a parent folder which does not have an acl", t, func() {
|
||||||
fakeDash := m.NewDashboard("Child dash")
|
fakeDash := m.NewDashboard("Child dash")
|
||||||
fakeDash.Id = 1
|
fakeDash.Id = 1
|
||||||
@ -426,8 +464,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
|
func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
|
||||||
sc.handlerFunc = GetDashboard
|
CallGetDashboard(sc)
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
||||||
|
|
||||||
So(sc.resp.Code, ShouldEqual, 200)
|
So(sc.resp.Code, ShouldEqual, 200)
|
||||||
|
|
||||||
@ -438,6 +475,11 @@ func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta
|
|||||||
return dash
|
return dash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CallGetDashboard(sc *scenarioContext) {
|
||||||
|
sc.handlerFunc = GetDashboard
|
||||||
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
func CallGetDashboardVersion(sc *scenarioContext) {
|
func CallGetDashboardVersion(sc *scenarioContext) {
|
||||||
bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
|
bus.AddHandler("test", func(query *m.GetDashboardVersionQuery) error {
|
||||||
query.Result = &m.DashboardVersion{}
|
query.Result = &m.DashboardVersion{}
|
||||||
|
18
pkg/api/dtos/folder.go
Normal file
18
pkg/api/dtos/folder.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package dtos
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
HasAcl bool `json:"hasAcl"`
|
||||||
|
CanSave bool `json:"canSave"`
|
||||||
|
CanEdit bool `json:"canEdit"`
|
||||||
|
CanAdmin bool `json:"canAdmin"`
|
||||||
|
CreatedBy string `json:"createdBy"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
UpdatedBy string `json:"updatedBy"`
|
||||||
|
Updated time.Time `json:"updated"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
}
|
221
pkg/api/folders.go
Normal file
221
pkg/api/folders.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getFolderHelper(orgId int64, slug string, id int64) (*m.Dashboard, Response) {
|
||||||
|
query := m.GetDashboardQuery{Slug: slug, Id: id, OrgId: orgId}
|
||||||
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
if err == m.ErrDashboardNotFound {
|
||||||
|
err = m.ErrFolderNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ApiError(404, "Folder not found", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !query.Result.IsFolder {
|
||||||
|
return nil, ApiError(404, "Folder not found", m.ErrFolderNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func folderGuardianResponse(err error) Response {
|
||||||
|
if err != nil {
|
||||||
|
return ApiError(500, "Error while checking folder permissions", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiError(403, "Access denied to this folder", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFolders(c *middleware.Context) Response {
|
||||||
|
title := c.Query("query")
|
||||||
|
query := m.GetFoldersQuery{
|
||||||
|
OrgId: c.OrgId,
|
||||||
|
SignedInUser: c.SignedInUser,
|
||||||
|
Title: title,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bus.Dispatch(&query)
|
||||||
|
if err != nil {
|
||||||
|
return ApiError(500, "Failed to retrieve folders", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, query.Result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFolderById(c *middleware.Context) Response {
|
||||||
|
folder, rsp := getFolderHelper(c.OrgId, "", c.ParamsInt64(":id"))
|
||||||
|
if rsp != nil {
|
||||||
|
return rsp
|
||||||
|
}
|
||||||
|
|
||||||
|
guardian := guardian.NewDashboardGuardian(folder.Id, c.OrgId, c.SignedInUser)
|
||||||
|
if canView, err := guardian.CanView(); err != nil || !canView {
|
||||||
|
fmt.Printf("%v", err)
|
||||||
|
return folderGuardianResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, toDto(guardian, folder))
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateFolder(c *middleware.Context, cmd m.CreateFolderCommand) Response {
|
||||||
|
cmd.OrgId = c.OrgId
|
||||||
|
cmd.UserId = c.UserId
|
||||||
|
|
||||||
|
dashFolder := m.NewDashboardFolder(cmd.Title)
|
||||||
|
|
||||||
|
guardian := guardian.NewDashboardGuardian(0, c.OrgId, c.SignedInUser)
|
||||||
|
if canSave, err := guardian.CanSave(); err != nil || !canSave {
|
||||||
|
return folderGuardianResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if Title is empty
|
||||||
|
if dashFolder.Title == "" {
|
||||||
|
return ApiError(400, m.ErrFolderTitleEmpty.Error(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
limitReached, err := middleware.QuotaReached(c, "folder")
|
||||||
|
if err != nil {
|
||||||
|
return ApiError(500, "failed to get quota", err)
|
||||||
|
}
|
||||||
|
if limitReached {
|
||||||
|
return ApiError(403, "Quota reached", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
dashFolder.CreatedBy = c.UserId
|
||||||
|
dashFolder.UpdatedBy = c.UserId
|
||||||
|
|
||||||
|
dashItem := &dashboards.SaveDashboardItem{
|
||||||
|
Dashboard: dashFolder,
|
||||||
|
OrgId: c.OrgId,
|
||||||
|
UserId: c.UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
folder, err := dashboards.GetRepository().SaveDashboard(dashItem)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return toFolderError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, toDto(guardian, folder))
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateFolder(c *middleware.Context, cmd m.UpdateFolderCommand) Response {
|
||||||
|
cmd.OrgId = c.OrgId
|
||||||
|
cmd.UserId = c.UserId
|
||||||
|
|
||||||
|
dashFolder, rsp := getFolderHelper(c.OrgId, "", c.ParamsInt64(":id"))
|
||||||
|
if rsp != nil {
|
||||||
|
return rsp
|
||||||
|
}
|
||||||
|
|
||||||
|
guardian := guardian.NewDashboardGuardian(dashFolder.Id, c.OrgId, c.SignedInUser)
|
||||||
|
if canSave, err := guardian.CanSave(); err != nil || !canSave {
|
||||||
|
return folderGuardianResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dashFolder.Data.Set("title", cmd.Title)
|
||||||
|
dashFolder.Title = cmd.Title
|
||||||
|
dashFolder.Data.Set("version", cmd.Version)
|
||||||
|
dashFolder.Version = cmd.Version
|
||||||
|
dashFolder.UpdatedBy = c.UserId
|
||||||
|
|
||||||
|
// Check if Title is empty
|
||||||
|
if dashFolder.Title == "" {
|
||||||
|
return ApiError(400, m.ErrFolderTitleEmpty.Error(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
dashItem := &dashboards.SaveDashboardItem{
|
||||||
|
Dashboard: dashFolder,
|
||||||
|
OrgId: c.OrgId,
|
||||||
|
UserId: c.UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
folder, err := dashboards.GetRepository().SaveDashboard(dashItem)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return toFolderError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, toDto(guardian, folder))
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteFolder(c *middleware.Context) Response {
|
||||||
|
dashFolder, rsp := getFolderHelper(c.OrgId, "", c.ParamsInt64(":id"))
|
||||||
|
if rsp != nil {
|
||||||
|
return rsp
|
||||||
|
}
|
||||||
|
|
||||||
|
guardian := guardian.NewDashboardGuardian(dashFolder.Id, c.OrgId, c.SignedInUser)
|
||||||
|
if canSave, err := guardian.CanSave(); err != nil || !canSave {
|
||||||
|
return folderGuardianResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCmd := m.DeleteDashboardCommand{OrgId: c.OrgId, Id: dashFolder.Id}
|
||||||
|
if err := bus.Dispatch(&deleteCmd); err != nil {
|
||||||
|
return ApiError(500, "Failed to delete folder", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp = map[string]interface{}{"title": dashFolder.Title}
|
||||||
|
return Json(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDto(guardian *guardian.DashboardGuardian, folder *m.Dashboard) dtos.Folder {
|
||||||
|
canEdit, _ := guardian.CanEdit()
|
||||||
|
canSave, _ := guardian.CanSave()
|
||||||
|
canAdmin, _ := guardian.CanAdmin()
|
||||||
|
|
||||||
|
// Finding creator and last updater of the folder
|
||||||
|
updater, creator := "Anonymous", "Anonymous"
|
||||||
|
if folder.UpdatedBy > 0 {
|
||||||
|
updater = getUserLogin(folder.UpdatedBy)
|
||||||
|
}
|
||||||
|
if folder.CreatedBy > 0 {
|
||||||
|
creator = getUserLogin(folder.CreatedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dtos.Folder{
|
||||||
|
Id: folder.Id,
|
||||||
|
Title: folder.Title,
|
||||||
|
Slug: folder.Slug,
|
||||||
|
HasAcl: folder.HasAcl,
|
||||||
|
CanSave: canSave,
|
||||||
|
CanEdit: canEdit,
|
||||||
|
CanAdmin: canAdmin,
|
||||||
|
CreatedBy: creator,
|
||||||
|
Created: folder.Created,
|
||||||
|
UpdatedBy: updater,
|
||||||
|
Updated: folder.Updated,
|
||||||
|
Version: folder.Version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toFolderError(err error) Response {
|
||||||
|
if err == m.ErrDashboardTitleEmpty {
|
||||||
|
return ApiError(400, m.ErrFolderTitleEmpty.Error(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == m.ErrDashboardWithSameNameExists {
|
||||||
|
return Json(412, util.DynMap{"status": "name-exists", "message": m.ErrFolderWithSameNameExists.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == m.ErrDashboardVersionMismatch {
|
||||||
|
return Json(412, util.DynMap{"status": "version-mismatch", "message": m.ErrFolderVersionMismatch.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == m.ErrDashboardNotFound {
|
||||||
|
return Json(404, util.DynMap{"status": "not-found", "message": m.ErrFolderNotFound.Error()})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiError(500, "Failed to create folder", err)
|
||||||
|
}
|
328
pkg/api/folders_test.go
Normal file
328
pkg/api/folders_test.go
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-macaron/session"
|
||||||
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
macaron "gopkg.in/macaron.v1"
|
||||||
|
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFoldersApiEndpoint(t *testing.T) {
|
||||||
|
Convey("Given a dashboard", t, func() {
|
||||||
|
fakeDash := m.NewDashboard("Child dash")
|
||||||
|
fakeDash.Id = 1
|
||||||
|
fakeDash.FolderId = 1
|
||||||
|
fakeDash.HasAcl = false
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
|
||||||
|
query.Result = fakeDash
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
updateFolderCmd := m.UpdateFolderCommand{}
|
||||||
|
|
||||||
|
Convey("When user is an Org Editor", func() {
|
||||||
|
role := m.ROLE_EDITOR
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
callGetFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
updateFolderScenario("When calling PUT on", "/api/folders/1", "/api/folders/:id", role, updateFolderCmd, func(sc *scenarioContext) {
|
||||||
|
callUpdateFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
callDeleteFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 404)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Given a folder which does not have an acl", t, func() {
|
||||||
|
fakeFolder := m.NewDashboardFolder("Folder")
|
||||||
|
fakeFolder.Id = 1
|
||||||
|
fakeFolder.HasAcl = false
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
|
||||||
|
query.Result = fakeFolder
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
viewerRole := m.ROLE_VIEWER
|
||||||
|
editorRole := m.ROLE_EDITOR
|
||||||
|
|
||||||
|
aclMockResp := []*m.DashboardAclInfoDTO{
|
||||||
|
{Role: &viewerRole, Permission: m.PERMISSION_VIEW},
|
||||||
|
{Role: &editorRole, Permission: m.PERMISSION_EDIT},
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
|
||||||
|
query.Result = aclMockResp
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
|
||||||
|
query.Result = []*m.Team{}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd := m.CreateFolderCommand{
|
||||||
|
Title: fakeFolder.Title,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey("When user is an Org Viewer", func() {
|
||||||
|
role := m.ROLE_VIEWER
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
folder := getFolderShouldReturn200(sc)
|
||||||
|
|
||||||
|
Convey("Should not be able to edit or save folder", func() {
|
||||||
|
So(folder.CanEdit, ShouldBeFalse)
|
||||||
|
So(folder.CanSave, ShouldBeFalse)
|
||||||
|
So(folder.CanAdmin, ShouldBeFalse)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
callDeleteFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
|
||||||
|
createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
|
||||||
|
callCreateFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When user is an Org Editor", func() {
|
||||||
|
role := m.ROLE_EDITOR
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
folder := getFolderShouldReturn200(sc)
|
||||||
|
|
||||||
|
Convey("Should be able to edit or save folder", func() {
|
||||||
|
So(folder.CanEdit, ShouldBeTrue)
|
||||||
|
So(folder.CanSave, ShouldBeTrue)
|
||||||
|
So(folder.CanAdmin, ShouldBeFalse)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
callDeleteFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 200)
|
||||||
|
})
|
||||||
|
|
||||||
|
createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
|
||||||
|
callCreateFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 200)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Given a folder which have an acl", t, func() {
|
||||||
|
fakeFolder := m.NewDashboardFolder("Folder")
|
||||||
|
fakeFolder.Id = 1
|
||||||
|
fakeFolder.HasAcl = true
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
|
||||||
|
query.Result = fakeFolder
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
aclMockResp := []*m.DashboardAclInfoDTO{
|
||||||
|
{
|
||||||
|
DashboardId: 1,
|
||||||
|
Permission: m.PERMISSION_EDIT,
|
||||||
|
UserId: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
|
||||||
|
query.Result = aclMockResp
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
|
||||||
|
query.Result = []*m.Team{}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd := m.CreateFolderCommand{
|
||||||
|
Title: fakeFolder.Title,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey("When user is an Org Viewer and has no permissions for this folder", func() {
|
||||||
|
role := m.ROLE_VIEWER
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
sc.handlerFunc = GetFolderById
|
||||||
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
||||||
|
Convey("Should be denied access", func() {
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
callDeleteFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
|
||||||
|
createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
|
||||||
|
callCreateFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("When user is an Org Editor and has no permissions for this folder", func() {
|
||||||
|
role := m.ROLE_EDITOR
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
sc.handlerFunc = GetFolderById
|
||||||
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
||||||
|
Convey("Should be denied access", func() {
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/folders/1", "/api/folders/:id", role, func(sc *scenarioContext) {
|
||||||
|
callDeleteFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
|
||||||
|
createFolderScenario("When calling POST on", "/api/folders", "/api/folders", role, cmd, func(sc *scenarioContext) {
|
||||||
|
callCreateFolder(sc)
|
||||||
|
So(sc.resp.Code, ShouldEqual, 403)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFolderShouldReturn200(sc *scenarioContext) dtos.Folder {
|
||||||
|
callGetFolder(sc)
|
||||||
|
|
||||||
|
So(sc.resp.Code, ShouldEqual, 200)
|
||||||
|
|
||||||
|
folder := dtos.Folder{}
|
||||||
|
err := json.NewDecoder(sc.resp.Body).Decode(&folder)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
return folder
|
||||||
|
}
|
||||||
|
|
||||||
|
func callGetFolder(sc *scenarioContext) {
|
||||||
|
sc.handlerFunc = GetFolderById
|
||||||
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
func callDeleteFolder(sc *scenarioContext) {
|
||||||
|
bus.AddHandler("test", func(cmd *m.DeleteDashboardCommand) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
sc.handlerFunc = DeleteFolder
|
||||||
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
func callCreateFolder(sc *scenarioContext) {
|
||||||
|
bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
|
||||||
|
cmd.Result = &m.Dashboard{Id: 1, Slug: "folder", Version: 2}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
func callUpdateFolder(sc *scenarioContext) {
|
||||||
|
bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
|
||||||
|
cmd.Result = &m.Dashboard{Id: 1, Slug: "folder", Version: 2}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFolderScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.CreateFolderCommand, fn scenarioFunc) {
|
||||||
|
Convey(desc+" "+url, func() {
|
||||||
|
defer bus.ClearBusHandlers()
|
||||||
|
|
||||||
|
sc := &scenarioContext{
|
||||||
|
url: url,
|
||||||
|
}
|
||||||
|
viewsPath, _ := filepath.Abs("../../public/views")
|
||||||
|
|
||||||
|
sc.m = macaron.New()
|
||||||
|
sc.m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||||
|
Directory: viewsPath,
|
||||||
|
Delims: macaron.Delims{Left: "[[", Right: "]]"},
|
||||||
|
}))
|
||||||
|
|
||||||
|
sc.m.Use(middleware.GetContextHandler())
|
||||||
|
sc.m.Use(middleware.Sessioner(&session.Options{}))
|
||||||
|
|
||||||
|
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
|
||||||
|
sc.context = c
|
||||||
|
sc.context.UserId = TestUserID
|
||||||
|
sc.context.OrgId = TestOrgID
|
||||||
|
sc.context.OrgRole = role
|
||||||
|
|
||||||
|
return CreateFolder(c, cmd)
|
||||||
|
})
|
||||||
|
|
||||||
|
fakeRepo = &fakeDashboardRepo{}
|
||||||
|
dashboards.SetRepository(fakeRepo)
|
||||||
|
|
||||||
|
sc.m.Post(routePattern, sc.defaultHandler)
|
||||||
|
|
||||||
|
fn(sc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateFolderScenario(desc string, url string, routePattern string, role m.RoleType, cmd m.UpdateFolderCommand, fn scenarioFunc) {
|
||||||
|
Convey(desc+" "+url, func() {
|
||||||
|
defer bus.ClearBusHandlers()
|
||||||
|
|
||||||
|
sc := &scenarioContext{
|
||||||
|
url: url,
|
||||||
|
}
|
||||||
|
viewsPath, _ := filepath.Abs("../../public/views")
|
||||||
|
|
||||||
|
sc.m = macaron.New()
|
||||||
|
sc.m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||||
|
Directory: viewsPath,
|
||||||
|
Delims: macaron.Delims{Left: "[[", Right: "]]"},
|
||||||
|
}))
|
||||||
|
|
||||||
|
sc.m.Use(middleware.GetContextHandler())
|
||||||
|
sc.m.Use(middleware.Sessioner(&session.Options{}))
|
||||||
|
|
||||||
|
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
|
||||||
|
sc.context = c
|
||||||
|
sc.context.UserId = TestUserID
|
||||||
|
sc.context.OrgId = TestOrgID
|
||||||
|
sc.context.OrgRole = role
|
||||||
|
|
||||||
|
return UpdateFolder(c, cmd)
|
||||||
|
})
|
||||||
|
|
||||||
|
fakeRepo = &fakeDashboardRepo{}
|
||||||
|
dashboards.SetRepository(fakeRepo)
|
||||||
|
|
||||||
|
sc.m.Put(routePattern, sc.defaultHandler)
|
||||||
|
|
||||||
|
fn(sc)
|
||||||
|
})
|
||||||
|
}
|
@ -73,9 +73,9 @@ func NewDashboard(title string) *Dashboard {
|
|||||||
// NewDashboardFolder creates a new dashboard folder
|
// NewDashboardFolder creates a new dashboard folder
|
||||||
func NewDashboardFolder(title string) *Dashboard {
|
func NewDashboardFolder(title string) *Dashboard {
|
||||||
folder := NewDashboard(title)
|
folder := NewDashboard(title)
|
||||||
|
folder.IsFolder = true
|
||||||
folder.Data.Set("schemaVersion", 16)
|
folder.Data.Set("schemaVersion", 16)
|
||||||
folder.Data.Set("editable", true)
|
folder.Data.Set("version", 0)
|
||||||
folder.Data.Set("hideControls", true)
|
|
||||||
return folder
|
return folder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,15 +209,3 @@ type GetDashboardSlugByIdQuery struct {
|
|||||||
Id int64
|
Id int64
|
||||||
Result string
|
Result string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetFoldersForSignedInUserQuery struct {
|
|
||||||
OrgId int64
|
|
||||||
SignedInUser *SignedInUser
|
|
||||||
Title string
|
|
||||||
Result []*DashboardFolder
|
|
||||||
}
|
|
||||||
|
|
||||||
type DashboardFolder struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
}
|
|
||||||
|
63
pkg/models/folders.go
Normal file
63
pkg/models/folders.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Typed errors
|
||||||
|
var (
|
||||||
|
ErrFolderNotFound = errors.New("Folder not found")
|
||||||
|
ErrFolderVersionMismatch = errors.New("The folder has been changed by someone else")
|
||||||
|
ErrFolderTitleEmpty = errors.New("Folder title cannot be empty")
|
||||||
|
ErrFolderWithSameNameExists = errors.New("A folder/dashboard with the same title already exists")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
Id int64
|
||||||
|
Title string
|
||||||
|
Slug string
|
||||||
|
OrgId int64
|
||||||
|
Version int
|
||||||
|
|
||||||
|
Created time.Time
|
||||||
|
Updated time.Time
|
||||||
|
|
||||||
|
UpdatedBy int64
|
||||||
|
CreatedBy int64
|
||||||
|
HasAcl bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetFoldersQueryHitResult struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// COMMANDS
|
||||||
|
//
|
||||||
|
|
||||||
|
type CreateFolderCommand struct {
|
||||||
|
OrgId int64 `json:"-"`
|
||||||
|
UserId int64 `json:"userId"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
|
||||||
|
Result *Folder
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateFolderCommand struct {
|
||||||
|
OrgId int64 `json:"-"`
|
||||||
|
UserId int64 `json:"userId"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
|
||||||
|
Result *Folder
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetFoldersQuery struct {
|
||||||
|
OrgId int64
|
||||||
|
SignedInUser *SignedInUser
|
||||||
|
Title string
|
||||||
|
Result []*GetFoldersQueryHitResult
|
||||||
|
}
|
@ -50,6 +50,9 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
|||||||
if existing.PluginId != "" && cmd.Overwrite == false {
|
if existing.PluginId != "" && cmd.Overwrite == false {
|
||||||
return m.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
return m.UpdatePluginDashboardError{PluginId: existing.PluginId}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dash.Created = existing.Created
|
||||||
|
dash.CreatedBy = existing.CreatedBy
|
||||||
}
|
}
|
||||||
|
|
||||||
sameTitleExists, err := sess.Where("org_id=? AND slug=?", dash.OrgId, dash.Slug).Get(&sameTitle)
|
sameTitleExists, err := sess.Where("org_id=? AND slug=?", dash.OrgId, dash.Slug).Get(&sameTitle)
|
||||||
@ -66,6 +69,9 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
|||||||
} else {
|
} else {
|
||||||
return m.ErrDashboardWithSameNameExists
|
return m.ErrDashboardWithSameNameExists
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
dash.Created = sameTitle.Created
|
||||||
|
dash.CreatedBy = sameTitle.CreatedBy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +140,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Result = dash
|
cmd.Result = dash
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -292,19 +299,26 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFoldersForSignedInUser(query *m.GetFoldersForSignedInUserQuery) error {
|
func GetFoldersForSignedInUser(query *m.GetFoldersQuery) error {
|
||||||
query.Result = make([]*m.DashboardFolder, 0)
|
query.Result = make([]*m.GetFoldersQueryHitResult, 0)
|
||||||
var err error
|
var err error
|
||||||
|
params := make([]interface{}, 0)
|
||||||
|
|
||||||
if query.SignedInUser.OrgRole == m.ROLE_ADMIN {
|
if query.SignedInUser.OrgRole == m.ROLE_ADMIN {
|
||||||
sql := `SELECT distinct d.id, d.title
|
sql := `SELECT distinct d.id, d.title, d.slug
|
||||||
FROM dashboard AS d WHERE d.is_folder = ?
|
FROM dashboard AS d WHERE d.is_folder = ?`
|
||||||
ORDER BY d.title ASC`
|
params = append(params, dialect.BooleanStr(true))
|
||||||
|
|
||||||
err = x.Sql(sql, dialect.BooleanStr(true)).Find(&query.Result)
|
if len(query.Title) > 0 {
|
||||||
|
sql += " AND d.title " + dialect.LikeStr() + " ?"
|
||||||
|
params = append(params, "%"+query.Title+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += ` ORDER BY d.title ASC`
|
||||||
|
|
||||||
|
err = x.Sql(sql, params...).Find(&query.Result)
|
||||||
} else {
|
} else {
|
||||||
params := make([]interface{}, 0)
|
sql := `SELECT distinct d.id, d.title, d.slug
|
||||||
sql := `SELECT distinct d.id, d.title
|
|
||||||
FROM dashboard AS d
|
FROM dashboard AS d
|
||||||
LEFT JOIN dashboard_acl AS da ON d.id = da.dashboard_id
|
LEFT JOIN dashboard_acl AS da ON d.id = da.dashboard_id
|
||||||
LEFT JOIN team_member AS ugm ON ugm.team_id = da.team_id
|
LEFT JOIN team_member AS ugm ON ugm.team_id = da.team_id
|
||||||
@ -315,14 +329,17 @@ func GetFoldersForSignedInUser(query *m.GetFoldersForSignedInUserQuery) error {
|
|||||||
|
|
||||||
sql += `WHERE
|
sql += `WHERE
|
||||||
d.org_id = ? AND
|
d.org_id = ? AND
|
||||||
d.is_folder = 1 AND
|
d.is_folder = ? AND
|
||||||
(
|
(
|
||||||
(d.has_acl = 1 AND da.permission > 1 AND (da.user_id = ? OR ugm.user_id = ? OR ou.id IS NOT NULL))
|
(d.has_acl = ? AND da.permission > 1 AND (da.user_id = ? OR ugm.user_id = ? OR ou.id IS NOT NULL))
|
||||||
OR (d.has_acl = 0 AND ouRole.id IS NOT NULL)
|
OR (d.has_acl = 0 AND ouRole.id IS NOT NULL)
|
||||||
)`
|
)`
|
||||||
params = append(params, query.OrgId)
|
params = append(params, query.OrgId)
|
||||||
|
params = append(params, dialect.BooleanStr(true))
|
||||||
|
params = append(params, dialect.BooleanStr(true))
|
||||||
params = append(params, query.SignedInUser.UserId)
|
params = append(params, query.SignedInUser.UserId)
|
||||||
params = append(params, query.SignedInUser.UserId)
|
params = append(params, query.SignedInUser.UserId)
|
||||||
|
params = append(params, dialect.BooleanStr(false))
|
||||||
|
|
||||||
if len(query.Title) > 0 {
|
if len(query.Title) > 0 {
|
||||||
sql += " AND d.title " + dialect.LikeStr() + " ?"
|
sql += " AND d.title " + dialect.LikeStr() + " ?"
|
||||||
|
@ -470,7 +470,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Admin users", func() {
|
Convey("Admin users", func() {
|
||||||
Convey("Should have write access to all dashboard folders", func() {
|
Convey("Should have write access to all dashboard folders", func() {
|
||||||
query := m.GetFoldersForSignedInUserQuery{
|
query := m.GetFoldersQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgRole: m.ROLE_ADMIN},
|
SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgRole: m.ROLE_ADMIN},
|
||||||
}
|
}
|
||||||
@ -485,7 +485,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Editor users", func() {
|
Convey("Editor users", func() {
|
||||||
query := m.GetFoldersForSignedInUserQuery{
|
query := m.GetFoldersQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgRole: m.ROLE_EDITOR},
|
SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgRole: m.ROLE_EDITOR},
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Viewer users", func() {
|
Convey("Viewer users", func() {
|
||||||
query := m.GetFoldersForSignedInUserQuery{
|
query := m.GetFoldersQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgRole: m.ROLE_VIEWER},
|
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgRole: m.ROLE_VIEWER},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user