mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
LibraryPanels: Adds folder checks and permissions (#31473)
* Refactor: adds permissions for library panel creation * Refactor: checks folder permissions for patch requests * Chore: changes after PR comments * Refactor: adds permissions to delete * Refactor: moves get all permission tests out of get all tests * Chore: move out get all tests to a separate file * Refactor: adds permissions to get handler * Refactor: fixes a bug with getting library panels in General folder * Refactor: adds permissions for connect/disconnect * Refactor: adds permissions and tests for get connected dashboards * Tests: adds tests for connected dashboards in General Folder
This commit is contained in:
parent
475fd994a0
commit
583b94557b
@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-macaron/binding"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
@ -35,6 +36,12 @@ func (lps *LibraryPanelService) createHandler(c *models.ReqContext, cmd createLi
|
||||
if errors.Is(err, errLibraryPanelAlreadyExists) {
|
||||
return response.Error(400, errLibraryPanelAlreadyExists.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(404, models.ErrFolderNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
||||
return response.Error(403, models.ErrFolderAccessDenied.Error(), err)
|
||||
}
|
||||
return response.Error(500, "Failed to create library panel", err)
|
||||
}
|
||||
|
||||
@ -47,6 +54,12 @@ func (lps *LibraryPanelService) connectHandler(c *models.ReqContext) response.Re
|
||||
if errors.Is(err, errLibraryPanelNotFound) {
|
||||
return response.Error(404, errLibraryPanelNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(404, models.ErrFolderNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
||||
return response.Error(403, models.ErrFolderAccessDenied.Error(), err)
|
||||
}
|
||||
return response.Error(500, "Failed to connect library panel", err)
|
||||
}
|
||||
|
||||
@ -60,6 +73,12 @@ func (lps *LibraryPanelService) deleteHandler(c *models.ReqContext) response.Res
|
||||
if errors.Is(err, errLibraryPanelNotFound) {
|
||||
return response.Error(404, errLibraryPanelNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(404, models.ErrFolderNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
||||
return response.Error(403, models.ErrFolderAccessDenied.Error(), err)
|
||||
}
|
||||
return response.Error(500, "Failed to delete library panel", err)
|
||||
}
|
||||
|
||||
@ -76,6 +95,12 @@ func (lps *LibraryPanelService) disconnectHandler(c *models.ReqContext) response
|
||||
if errors.Is(err, errLibraryPanelDashboardNotFound) {
|
||||
return response.Error(404, errLibraryPanelDashboardNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(404, models.ErrFolderNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
||||
return response.Error(403, models.ErrFolderAccessDenied.Error(), err)
|
||||
}
|
||||
return response.Error(500, "Failed to disconnect library panel", err)
|
||||
}
|
||||
|
||||
@ -128,6 +153,12 @@ func (lps *LibraryPanelService) patchHandler(c *models.ReqContext, cmd patchLibr
|
||||
if errors.Is(err, errLibraryPanelNotFound) {
|
||||
return response.Error(404, errLibraryPanelNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(404, models.ErrFolderNotFound.Error(), err)
|
||||
}
|
||||
if errors.Is(err, models.ErrFolderAccessDenied) {
|
||||
return response.Error(403, models.ErrFolderAccessDenied.Error(), err)
|
||||
}
|
||||
return response.Error(500, "Failed to update library panel", err)
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,9 @@ func (lps *LibraryPanelService) createLibraryPanel(c *models.ReqContext, cmd cre
|
||||
}
|
||||
|
||||
err := lps.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, cmd.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := session.Insert(&libraryPanel); err != nil {
|
||||
if lps.SQLStore.Dialect.IsUniqueConstraintViolation(err) {
|
||||
return errLibraryPanelAlreadyExists
|
||||
@ -108,6 +111,9 @@ func connectDashboard(session *sqlstore.DBSession, dialect migrator.Dialect, use
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := requirePermissionsOnFolder(user, panel.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO add check that dashboard exists
|
||||
|
||||
@ -161,7 +167,9 @@ func (lps *LibraryPanelService) deleteLibraryPanel(c *models.ReqContext, uid str
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := session.Exec("DELETE FROM library_panel_dashboard WHERE librarypanel_id=?", panel.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -187,6 +195,9 @@ func (lps *LibraryPanelService) disconnectDashboard(c *models.ReqContext, uid st
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := requirePermissionsOnFolder(c.SignedInUser, panel.FolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := session.Exec("DELETE FROM library_panel_dashboard WHERE librarypanel_id=? and dashboard_id=?", panel.ID, dashboardID)
|
||||
if err != nil {
|
||||
@ -242,9 +253,31 @@ func getLibraryPanel(session *sqlstore.DBSession, uid string, orgID int64) (Libr
|
||||
func (lps *LibraryPanelService) getLibraryPanel(c *models.ReqContext, uid string) (LibraryPanelDTO, error) {
|
||||
var libraryPanel LibraryPanelWithMeta
|
||||
err := lps.SQLStore.WithDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||
var err error
|
||||
libraryPanel, err = getLibraryPanel(session, uid, c.SignedInUser.OrgId)
|
||||
return err
|
||||
libraryPanels := make([]LibraryPanelWithMeta, 0)
|
||||
builder := sqlstore.SQLBuilder{}
|
||||
builder.Write(sqlStatmentLibrayPanelDTOWithMeta)
|
||||
builder.Write(` WHERE lp.uid=? AND lp.org_id=? AND lp.folder_id=0`, uid, c.SignedInUser.OrgId)
|
||||
builder.Write(" UNION ")
|
||||
builder.Write(sqlStatmentLibrayPanelDTOWithMeta)
|
||||
builder.Write(" INNER JOIN dashboard AS dashboard on lp.folder_id = dashboard.id AND lp.folder_id <> 0")
|
||||
builder.Write(` WHERE lp.uid=? AND lp.org_id=?`, uid, c.SignedInUser.OrgId)
|
||||
if c.SignedInUser.OrgRole != models.ROLE_ADMIN {
|
||||
builder.WriteDashboardPermissionFilter(c.SignedInUser, models.PERMISSION_VIEW)
|
||||
}
|
||||
builder.Write(` OR dashboard.id=0`)
|
||||
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&libraryPanels); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(libraryPanels) == 0 {
|
||||
return errLibraryPanelNotFound
|
||||
}
|
||||
if len(libraryPanels) > 1 {
|
||||
return fmt.Errorf("found %d panels, while expecting at most one", len(libraryPanels))
|
||||
}
|
||||
|
||||
libraryPanel = libraryPanels[0]
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
dto := LibraryPanelDTO{
|
||||
@ -281,8 +314,11 @@ func (lps *LibraryPanelService) getAllLibraryPanels(c *models.ReqContext, limit
|
||||
err := lps.SQLStore.WithDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
|
||||
builder := sqlstore.SQLBuilder{}
|
||||
builder.Write(sqlStatmentLibrayPanelDTOWithMeta)
|
||||
builder.Write(" LEFT JOIN dashboard AS dashboard on lp.folder_id = dashboard.id")
|
||||
builder.Write(` WHERE lp.org_id = ?`, c.SignedInUser.OrgId)
|
||||
builder.Write(` WHERE lp.org_id=? AND lp.folder_id=0`, c.SignedInUser.OrgId)
|
||||
builder.Write(" UNION ")
|
||||
builder.Write(sqlStatmentLibrayPanelDTOWithMeta)
|
||||
builder.Write(" INNER JOIN dashboard AS dashboard on lp.folder_id = dashboard.id AND lp.folder_id<>0")
|
||||
builder.Write(` WHERE lp.org_id=?`, c.SignedInUser.OrgId)
|
||||
if c.SignedInUser.OrgRole != models.ROLE_ADMIN {
|
||||
builder.WriteDashboardPermissionFilter(c.SignedInUser, models.PERMISSION_VIEW)
|
||||
}
|
||||
@ -336,12 +372,15 @@ func (lps *LibraryPanelService) getConnectedDashboards(c *models.ReqContext, uid
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var libraryPanelDashboards []libraryPanelDashboard
|
||||
session.Table("library_panel_dashboard")
|
||||
session.Where("librarypanel_id=?", panel.ID)
|
||||
err = session.Find(&libraryPanelDashboards)
|
||||
if err != nil {
|
||||
builder := sqlstore.SQLBuilder{}
|
||||
builder.Write("SELECT lpd.* FROM library_panel_dashboard lpd")
|
||||
builder.Write(" INNER JOIN dashboard AS dashboard on lpd.dashboard_id = dashboard.id")
|
||||
builder.Write(` WHERE lpd.librarypanel_id=?`, panel.ID)
|
||||
if c.SignedInUser.OrgRole != models.ROLE_ADMIN {
|
||||
builder.WriteDashboardPermissionFilter(c.SignedInUser, models.PERMISSION_VIEW)
|
||||
}
|
||||
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&libraryPanelDashboards); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -399,6 +438,29 @@ func (lps *LibraryPanelService) getLibraryPanelsForDashboardID(c *models.ReqCont
|
||||
return libraryPanelMap, err
|
||||
}
|
||||
|
||||
func handleFolderIDPatches(panelToPatch *LibraryPanel, fromFolderID int64, toFolderID int64, user *models.SignedInUser) error {
|
||||
// FolderID was not provided in the PATCH request
|
||||
if toFolderID == -1 {
|
||||
toFolderID = fromFolderID
|
||||
}
|
||||
|
||||
// FolderID was provided in the PATCH request
|
||||
if toFolderID != -1 && toFolderID != fromFolderID {
|
||||
if err := requirePermissionsOnFolder(user, toFolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Always check permissions for the folder where library panel resides
|
||||
if err := requirePermissionsOnFolder(user, fromFolderID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
panelToPatch.FolderID = toFolderID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// patchLibraryPanel updates a Library Panel.
|
||||
func (lps *LibraryPanelService) patchLibraryPanel(c *models.ReqContext, cmd patchLibraryPanelCommand, uid string) (LibraryPanelDTO, error) {
|
||||
var dto LibraryPanelDTO
|
||||
@ -421,19 +483,18 @@ func (lps *LibraryPanelService) patchLibraryPanel(c *models.ReqContext, cmd patc
|
||||
UpdatedBy: c.SignedInUser.UserId,
|
||||
}
|
||||
|
||||
if cmd.FolderID == 0 {
|
||||
libraryPanel.FolderID = panelInDB.FolderID
|
||||
}
|
||||
if cmd.Name == "" {
|
||||
libraryPanel.Name = panelInDB.Name
|
||||
}
|
||||
if cmd.Model == nil {
|
||||
libraryPanel.Model = panelInDB.Model
|
||||
}
|
||||
if err := handleFolderIDPatches(&libraryPanel, panelInDB.FolderID, cmd.FolderID, c.SignedInUser); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := syncTitleWithName(&libraryPanel); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rowsAffected, err := session.ID(panelInDB.ID).Update(&libraryPanel); err != nil {
|
||||
if lps.SQLStore.Dialect.IsUniqueConstraintViolation(err) {
|
||||
return errLibraryPanelAlreadyExists
|
||||
|
39
pkg/services/librarypanels/guard.go
Normal file
39
pkg/services/librarypanels/guard.go
Normal file
@ -0,0 +1,39 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
)
|
||||
|
||||
func isGeneralFolder(folderID int64) bool {
|
||||
return folderID == 0
|
||||
}
|
||||
|
||||
func requirePermissionsOnFolder(user *models.SignedInUser, folderID int64) error {
|
||||
if isGeneralFolder(folderID) && user.HasRole(models.ROLE_EDITOR) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isGeneralFolder(folderID) && user.HasRole(models.ROLE_VIEWER) {
|
||||
return models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
s := dashboards.NewFolderService(user.OrgId, user)
|
||||
folder, err := s.GetFolderByID(folderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g := guardian.New(folder.Id, user.OrgId, user)
|
||||
|
||||
canEdit, err := g.CanEdit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !canEdit {
|
||||
return models.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
97
pkg/services/librarypanels/librarypanels_connections_test.go
Normal file
97
pkg/services/librarypanels/librarypanels_connections_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConnectLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a connection for a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown", ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a connection that already exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
func TestDisconnectLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to remove a connection with a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown", ":dashboardId": "1"})
|
||||
resp := sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to remove a connection that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to remove a connection that does exist, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
resp = sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetConnectedDashboards(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
|
||||
resp := sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that exists, but has no connections, it should return none",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var dashResult libraryPanelDashboardsResult
|
||||
err := json.Unmarshal(resp.Body(), &dashResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(dashResult.Result))
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that exists and has connections, it should return connected dashboard IDs",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
firstDash := createDashboard(t, sc.user, "Dash 1", 0)
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": strconv.FormatInt(firstDash.Id, 10)})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
secondDash := createDashboard(t, sc.user, "Dash 2", 0)
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": strconv.FormatInt(secondDash.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp = sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var dashResult libraryPanelDashboardsResult
|
||||
err := json.Unmarshal(resp.Body(), &dashResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(dashResult.Result))
|
||||
require.Equal(t, firstDash.Id, dashResult.Result[0])
|
||||
require.Equal(t, secondDash.Id, dashResult.Result[1])
|
||||
})
|
||||
}
|
96
pkg/services/librarypanels/librarypanels_create_test.go
Normal file
96
pkg/services/librarypanels/librarypanels_create_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a library panel that already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a library panel that does not exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: sc.initialResult.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, sc.initialResult, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
testScenario(t, "When an admin tries to create a library panel where name and panel title differ, it should update panel title",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(1, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result.UID,
|
||||
Name: "Library Panel Name",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Library Panel Name",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
33
pkg/services/librarypanels/librarypanels_delete_test.go
Normal file
33
pkg/services/librarypanels/librarypanels_delete_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func TestDeleteLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to delete a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to delete a library panel that exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to delete a library panel in another org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.reqContext.SignedInUser.OrgId = 2
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
}
|
152
pkg/services/librarypanels/librarypanels_get_all_test.go
Normal file
152
pkg/services/librarypanels/librarypanels_get_all_test.go
Normal file
@ -0,0 +1,152 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func TestGetAllLibraryPanels(t *testing.T) {
|
||||
testScenario(t, "When an admin tries to get all library panels and none exists, it should return none",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.Result)
|
||||
require.Equal(t, 0, len(result.Result))
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get all library panels and two exist, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel2")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
var expected = libraryPanelsResult{
|
||||
Result: []libraryPanel{
|
||||
{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result[0].Meta.Created,
|
||||
Updated: result.Result[0].Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: UserInDbName,
|
||||
AvatarUrl: UserInDbAvatar,
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: UserInDbName,
|
||||
AvatarUrl: UserInDbAvatar,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result[1].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel2",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result[1].Meta.Created,
|
||||
Updated: result.Result[1].Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: UserInDbName,
|
||||
AvatarUrl: UserInDbAvatar,
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: UserInDbName,
|
||||
AvatarUrl: UserInDbAvatar,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get all library panels and two exist but only one is connected, it should succeed and return correct connected dashboards",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel2")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": "1"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": "2"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var results libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &results)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), results.Result[0].Meta.ConnectedDashboards)
|
||||
require.Equal(t, int64(2), results.Result[1].Meta.ConnectedDashboards)
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get all library panels in a different org, none should be returned",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(result.Result))
|
||||
require.Equal(t, int64(1), result.Result[0].FolderID)
|
||||
require.Equal(t, "Text - Library Panel", result.Result[0].Name)
|
||||
|
||||
sc.reqContext.SignedInUser.OrgId = 2
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
result = libraryPanelsResult{}
|
||||
err = json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.Result)
|
||||
require.Equal(t, 0, len(result.Result))
|
||||
})
|
||||
}
|
85
pkg/services/librarypanels/librarypanels_get_test.go
Normal file
85
pkg/services/librarypanels/librarypanels_get_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func TestGetLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel that exists, it should succeed and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: UserInDbName,
|
||||
AvatarUrl: UserInDbAvatar,
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: UserInDbName,
|
||||
AvatarUrl: UserInDbAvatar,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel that exists in an other org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.reqContext.SignedInUser.OrgId = 2
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel with 2 connected dashboards, it should succeed and return correct connected dashboards",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "2"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp = sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
require.Equal(t, int64(2), result.Result.Meta.ConnectedDashboards)
|
||||
})
|
||||
}
|
189
pkg/services/librarypanels/librarypanels_patch_test.go
Normal file
189
pkg/services/librarypanels/librarypanels_patch_test.go
Normal file
@ -0,0 +1,189 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPatchLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel that exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "2"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: newFolder.Id,
|
||||
Name: "Panel - New name",
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
"title": "Model - New name",
|
||||
"type": "text"
|
||||
}
|
||||
`),
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: newFolder.Id,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Panel - New name",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Panel - New name",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 2,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: newFolder.Id,
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.FolderID = newFolder.Id
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with name only, it should change name successfully, sync title and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: -1,
|
||||
Name: "New Name",
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.Name = "New Name"
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
sc.initialResult.Result.Model["title"] = "New Name"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with model only, it should change model successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: -1,
|
||||
Model: []byte(`{ "title": "New Model Title", "name": "New Model Name" }`),
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.Model = map[string]interface{}{
|
||||
"title": "Text - Library Panel",
|
||||
"name": "New Model Name",
|
||||
}
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When another admin tries to patch a library panel, it should change UpdatedBy successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{FolderID: -1}
|
||||
sc.reqContext.UserId = 2
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.Meta.UpdatedBy.ID = int64(2)
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with a name that already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Another Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
cmd := patchLibraryPanelCommand{
|
||||
Name: "Text - Library Panel",
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolderWithACL(t, "NewFolder", sc.user, []folderACLItem{})
|
||||
command := getCreateCommand(newFolder.Id, "Text - Library Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: 1,
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: sc.folder.Id,
|
||||
}
|
||||
sc.reqContext.OrgId = 2
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
}
|
459
pkg/services/librarypanels/librarypanels_permissions_test.go
Normal file
459
pkg/services/librarypanels/librarypanels_permissions_test.go
Normal file
@ -0,0 +1,459 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLibraryPanelPermissions(t *testing.T) {
|
||||
var defaultPermissions = []folderACLItem{}
|
||||
var adminOnlyPermissions = []folderACLItem{{models.ROLE_ADMIN, models.PERMISSION_EDIT}}
|
||||
var editorOnlyPermissions = []folderACLItem{{models.ROLE_EDITOR, models.PERMISSION_EDIT}}
|
||||
var editorAndViewerPermissions = []folderACLItem{{models.ROLE_EDITOR, models.PERMISSION_EDIT}, {models.ROLE_VIEWER, models.PERMISSION_EDIT}}
|
||||
var viewerOnlyPermissions = []folderACLItem{{models.ROLE_VIEWER, models.PERMISSION_EDIT}}
|
||||
var everyonePermissions = []folderACLItem{{models.ROLE_ADMIN, models.PERMISSION_EDIT}, {models.ROLE_EDITOR, models.PERMISSION_EDIT}, {models.ROLE_VIEWER, models.PERMISSION_EDIT}}
|
||||
var noPermissions = []folderACLItem{{models.ROLE_VIEWER, models.PERMISSION_VIEW}}
|
||||
var folderCases = [][]folderACLItem{
|
||||
defaultPermissions,
|
||||
adminOnlyPermissions,
|
||||
editorOnlyPermissions,
|
||||
editorAndViewerPermissions,
|
||||
viewerOnlyPermissions,
|
||||
everyonePermissions,
|
||||
noPermissions,
|
||||
}
|
||||
var defaultDesc = "default permissions"
|
||||
var adminOnlyDesc = "admin only permissions"
|
||||
var editorOnlyDesc = "editor only permissions"
|
||||
var editorAndViewerDesc = "editor and viewer permissions"
|
||||
var viewerOnlyDesc = "viewer only permissions"
|
||||
var everyoneDesc = "everyone has editor permissions"
|
||||
var noDesc = "everyone has view permissions"
|
||||
var accessCases = []struct {
|
||||
role models.RoleType
|
||||
items []folderACLItem
|
||||
desc string
|
||||
status int
|
||||
}{
|
||||
{models.ROLE_ADMIN, defaultPermissions, defaultDesc, 200},
|
||||
{models.ROLE_ADMIN, adminOnlyPermissions, adminOnlyDesc, 200},
|
||||
{models.ROLE_ADMIN, editorOnlyPermissions, editorOnlyDesc, 200},
|
||||
{models.ROLE_ADMIN, editorAndViewerPermissions, editorAndViewerDesc, 200},
|
||||
{models.ROLE_ADMIN, viewerOnlyPermissions, viewerOnlyDesc, 200},
|
||||
{models.ROLE_ADMIN, everyonePermissions, everyoneDesc, 200},
|
||||
{models.ROLE_ADMIN, noPermissions, noDesc, 200},
|
||||
{models.ROLE_EDITOR, defaultPermissions, defaultDesc, 200},
|
||||
{models.ROLE_EDITOR, adminOnlyPermissions, adminOnlyDesc, 403},
|
||||
{models.ROLE_EDITOR, editorOnlyPermissions, editorOnlyDesc, 200},
|
||||
{models.ROLE_EDITOR, editorAndViewerPermissions, editorAndViewerDesc, 200},
|
||||
{models.ROLE_EDITOR, viewerOnlyPermissions, viewerOnlyDesc, 403},
|
||||
{models.ROLE_EDITOR, everyonePermissions, everyoneDesc, 200},
|
||||
{models.ROLE_EDITOR, noPermissions, noDesc, 403},
|
||||
{models.ROLE_VIEWER, defaultPermissions, defaultDesc, 403},
|
||||
{models.ROLE_VIEWER, adminOnlyPermissions, adminOnlyDesc, 403},
|
||||
{models.ROLE_VIEWER, editorOnlyPermissions, editorOnlyDesc, 403},
|
||||
{models.ROLE_VIEWER, editorAndViewerPermissions, editorAndViewerDesc, 200},
|
||||
{models.ROLE_VIEWER, viewerOnlyPermissions, viewerOnlyDesc, 200},
|
||||
{models.ROLE_VIEWER, everyonePermissions, everyoneDesc, 200},
|
||||
{models.ROLE_VIEWER, noPermissions, noDesc, 403},
|
||||
}
|
||||
|
||||
for _, testCase := range accessCases {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to create a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
command := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
fromFolder := createFolderWithACL(t, "Everyone", sc.user, everyonePermissions)
|
||||
command := getCreateCommand(fromFolder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
toFolder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := patchLibraryPanelCommand{FolderID: toFolder.Id}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
fromFolder := createFolderWithACL(t, "Everyone", sc.user, testCase.items)
|
||||
command := getCreateCommand(fromFolder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
toFolder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := patchLibraryPanelCommand{FolderID: toFolder.Id}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to delete a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to connect a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to disconnect a library panel in a folder with %s, it should return correct status", testCase.role, testCase.desc),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, testCase.items)
|
||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
||||
cmd := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
var generalFolderCases = []struct {
|
||||
role models.RoleType
|
||||
status int
|
||||
}{
|
||||
{models.ROLE_ADMIN, 200},
|
||||
{models.ROLE_EDITOR, 200},
|
||||
{models.ROLE_VIEWER, 403},
|
||||
}
|
||||
|
||||
for _, testCase := range generalFolderCases {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to create a library panel in the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
command := getCreateCommand(0, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
||||
command := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := patchLibraryPanelCommand{FolderID: 0}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it from the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
||||
command := getCreateCommand(0, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := patchLibraryPanelCommand{FolderID: folder.Id}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to delete a library panel in the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreateCommand(0, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to connect a library panel in the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashboard := createDashboard(t, sc.user, "General Folder Dash", 0)
|
||||
cmd := getCreateCommand(0, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to disconnect a library panel in the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashboard := createDashboard(t, sc.user, "General Folder Dash", 0)
|
||||
cmd := getCreateCommand(0, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.status, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get connected dashboards in the General folder for a library panel in the General folder, it should return correct status", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashboard := createDashboard(t, sc.user, "General Folder Dash", 0)
|
||||
cmd := getCreateCommand(0, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var dashResult libraryPanelDashboardsResult
|
||||
err := json.Unmarshal(resp.Body(), &dashResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 1, len(dashResult.Result))
|
||||
require.Equal(t, dashboard.Id, dashResult.Result[0])
|
||||
})
|
||||
}
|
||||
|
||||
var missingFolderCases = []struct {
|
||||
role models.RoleType
|
||||
}{
|
||||
{models.ROLE_ADMIN},
|
||||
{models.ROLE_EDITOR},
|
||||
{models.ROLE_VIEWER},
|
||||
}
|
||||
|
||||
for _, testCase := range missingFolderCases {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to create a library panel in a folder that doesn't exist, it should fail", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
command := getCreateCommand(-100, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to patch a library panel by moving it to a folder that doesn't exist, it should fail", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
folder := createFolderWithACL(t, "Folder", sc.user, everyonePermissions)
|
||||
command := getCreateCommand(folder.Id, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
cmd := patchLibraryPanelCommand{FolderID: -100}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
var getAllCases = []struct {
|
||||
role models.RoleType
|
||||
panels int
|
||||
folderIndexes []int
|
||||
}{
|
||||
{models.ROLE_ADMIN, 7, []int{0, 1, 2, 3, 4, 5, 6}},
|
||||
{models.ROLE_EDITOR, 6, []int{0, 2, 3, 4, 5, 6}},
|
||||
{models.ROLE_VIEWER, 5, []int{0, 3, 4, 5, 6}},
|
||||
}
|
||||
|
||||
for _, testCase := range getAllCases {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get all library panels, it should return correct response", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
var results []libraryPanel
|
||||
for i, folderCase := range folderCases {
|
||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
cmd := getCreateCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
result.Result.Meta.CreatedBy.Name = UserInDbName
|
||||
result.Result.Meta.CreatedBy.AvatarUrl = UserInDbAvatar
|
||||
result.Result.Meta.UpdatedBy.Name = UserInDbName
|
||||
result.Result.Meta.UpdatedBy.AvatarUrl = UserInDbAvatar
|
||||
results = append(results, result.Result)
|
||||
}
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
resp := sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var actual libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &actual)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testCase.panels, len(actual.Result))
|
||||
for _, folderIndex := range testCase.folderIndexes {
|
||||
var folderID = int64(folderIndex + 2) // testScenario creates one folder and general folder doesn't count
|
||||
var foundResult libraryPanel
|
||||
var actualResult libraryPanel
|
||||
for _, result := range results {
|
||||
if result.FolderID == folderID {
|
||||
foundResult = result
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmpty(t, foundResult)
|
||||
|
||||
for _, result := range actual.Result {
|
||||
if result.FolderID == folderID {
|
||||
actualResult = result
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmpty(t, actualResult)
|
||||
|
||||
if diff := cmp.Diff(foundResult, actualResult, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get all library panels from General folder, it should return correct response", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreateCommand(0, "Library Panel in General Folder")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
result.Result.Meta.CreatedBy.Name = UserInDbName
|
||||
result.Result.Meta.CreatedBy.AvatarUrl = UserInDbAvatar
|
||||
result.Result.Meta.UpdatedBy.Name = UserInDbName
|
||||
result.Result.Meta.UpdatedBy.AvatarUrl = UserInDbAvatar
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var actual libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &actual)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(actual.Result))
|
||||
if diff := cmp.Diff(result.Result, actual.Result[0], getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get connected dashboards for a library panel, it should return correct connected dashboard IDs", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreateCommand(0, "Library Panel in General Folder")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
for i, folderCase := range folderCases {
|
||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
dashboard := createDashboard(t, sc.user, "Some Folder Dash", folder.Id)
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": strconv.FormatInt(dashboard.Id, 10)})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
}
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var dashResult libraryPanelDashboardsResult
|
||||
err := json.Unmarshal(resp.Body(), &dashResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testCase.panels, len(dashResult.Result))
|
||||
})
|
||||
}
|
||||
|
||||
var getCases = []struct {
|
||||
role models.RoleType
|
||||
statuses []int
|
||||
}{
|
||||
{models.ROLE_ADMIN, []int{200, 200, 200, 200, 200, 200, 200}},
|
||||
{models.ROLE_EDITOR, []int{200, 404, 200, 200, 200, 200, 200}},
|
||||
{models.ROLE_VIEWER, []int{200, 404, 404, 200, 200, 200, 200}},
|
||||
}
|
||||
|
||||
for _, testCase := range getCases {
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get a library panel, it should return correct response", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
var results []libraryPanel
|
||||
for i, folderCase := range folderCases {
|
||||
folder := createFolderWithACL(t, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
|
||||
cmd := getCreateCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
result.Result.Meta.CreatedBy.Name = UserInDbName
|
||||
result.Result.Meta.CreatedBy.AvatarUrl = UserInDbAvatar
|
||||
result.Result.Meta.UpdatedBy.Name = UserInDbName
|
||||
result.Result.Meta.UpdatedBy.AvatarUrl = UserInDbAvatar
|
||||
results = append(results, result.Result)
|
||||
}
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
for i, result := range results {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.UID})
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, testCase.statuses[i], resp.Status())
|
||||
}
|
||||
})
|
||||
|
||||
testScenario(t, fmt.Sprintf("When %s tries to get a library panel from General folder, it should return correct response", testCase.role),
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := getCreateCommand(0, "Library Panel in General Folder")
|
||||
resp := sc.service.createHandler(sc.reqContext, cmd)
|
||||
result := validateAndUnMarshalResponse(t, resp)
|
||||
result.Result.Meta.CreatedBy.Name = UserInDbName
|
||||
result.Result.Meta.CreatedBy.AvatarUrl = UserInDbAvatar
|
||||
result.Result.Meta.UpdatedBy.Name = UserInDbName
|
||||
result.Result.Meta.UpdatedBy.AvatarUrl = UserInDbAvatar
|
||||
sc.reqContext.SignedInUser.OrgRole = testCase.role
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var actual libraryPanelResult
|
||||
err := json.Unmarshal(resp.Body(), &actual)
|
||||
require.NoError(t, err)
|
||||
if diff := cmp.Diff(result.Result, actual.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -22,652 +22,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestCreateLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a library panel that already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a library panel that does not exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: sc.initialResult.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, sc.initialResult, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
testScenario(t, "When an admin tries to create a library panel where name and panel title differ, it should update panel title",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(1, "Library Panel Name")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result.UID,
|
||||
Name: "Library Panel Name",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Library Panel Name",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestConnectLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a connection for a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown", ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to create a connection that already exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to delete a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to delete a library panel that exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to delete a library panel in another org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.reqContext.SignedInUser.OrgId = 2
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
func TestDisconnectLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to remove a connection with a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown", ":dashboardId": "1"})
|
||||
resp := sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to remove a connection that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to remove a connection that does exist, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
resp = sc.service.disconnectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel that exists, it should succeed and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result.UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel that exists in an other org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.reqContext.SignedInUser.OrgId = 2
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
resp := sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get a library panel with 2 connected dashboards, it should succeed and return correct connected dashboards",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "2"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp = sc.service.getHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
require.Equal(t, int64(2), result.Result.Meta.ConnectedDashboards)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetAllLibraryPanels(t *testing.T) {
|
||||
testScenario(t, "When an admin tries to get all library panels and none exists, it should return none",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.Result)
|
||||
require.Equal(t, 0, len(result.Result))
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get all library panels and two exist, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel2")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
var expected = libraryPanelsResult{
|
||||
Result: []libraryPanel{
|
||||
{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result[0].UID,
|
||||
Name: "Text - Library Panel",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result[0].Meta.Created,
|
||||
Updated: result.Result[0].Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
FolderID: 1,
|
||||
UID: result.Result[1].UID,
|
||||
Name: "Text - Library Panel2",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Text - Library Panel2",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result[1].Meta.Created,
|
||||
Updated: result.Result[1].Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get all library panels and two exist but only one is connected, it should succeed and return correct connected dashboards",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel2")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": "1"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID, ":dashboardId": "2"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var results libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &results)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), results.Result[0].Meta.ConnectedDashboards)
|
||||
require.Equal(t, int64(2), results.Result[1].Meta.ConnectedDashboards)
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get all library panels in a different org, none should be returned",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(result.Result))
|
||||
require.Equal(t, int64(1), result.Result[0].FolderID)
|
||||
require.Equal(t, "Text - Library Panel", result.Result[0].Name)
|
||||
|
||||
sc.reqContext.SignedInUser.OrgId = 2
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
result = libraryPanelsResult{}
|
||||
err = json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.Result)
|
||||
require.Equal(t, 0, len(result.Result))
|
||||
})
|
||||
|
||||
testScenario(t, "When an user tries to get all library panels, library panels in folders where the user has no access should not be returned",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
updateFolderACL(t, sc, models.ROLE_EDITOR, models.PERMISSION_EDIT)
|
||||
|
||||
command := getCreateCommand(sc.folder.Id, "Editor - Library Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
cmd := models.CreateFolderCommand{
|
||||
Uid: "AdminOnlyFolder",
|
||||
Title: "Admin Only Folder",
|
||||
}
|
||||
createFolder(t, &sc, &cmd)
|
||||
updateFolderACL(t, sc, models.ROLE_ADMIN, models.PERMISSION_ADMIN)
|
||||
|
||||
command = getCreateCommand(sc.folder.Id, "Admin - Library Panel")
|
||||
resp = sc.service.createHandler(sc.reqContext, command)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result libraryPanelsResult
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(result.Result))
|
||||
require.Equal(t, int64(1), result.Result[0].FolderID)
|
||||
require.Equal(t, int64(2), cmd.Result.Id)
|
||||
require.Equal(t, "Editor - Library Panel", result.Result[0].Name)
|
||||
require.Equal(t, "Admin - Library Panel", result.Result[1].Name)
|
||||
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_EDITOR
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
result = libraryPanelsResult{}
|
||||
err = json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(result.Result))
|
||||
require.Equal(t, int64(1), result.Result[0].FolderID)
|
||||
require.Equal(t, "Editor - Library Panel", result.Result[0].Name)
|
||||
|
||||
sc.reqContext.SignedInUser.OrgRole = models.ROLE_VIEWER
|
||||
resp = sc.service.getAllHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
result = libraryPanelsResult{}
|
||||
err = json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result.Result)
|
||||
require.Equal(t, 0, len(result.Result))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetConnectedDashboards(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
|
||||
resp := sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that exists, but has no connections, it should return none",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var dashResult libraryPanelDashboardsResult
|
||||
err := json.Unmarshal(resp.Body(), &dashResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(dashResult.Result))
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to get connected dashboards for a library panel that exists and has connections, it should return connected dashboard IDs",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "11"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "12"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp = sc.service.getConnectedDashboardsHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var dashResult libraryPanelDashboardsResult
|
||||
err := json.Unmarshal(resp.Body(), &dashResult)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(dashResult.Result))
|
||||
require.Equal(t, int64(11), dashResult.Result[0])
|
||||
require.Equal(t, int64(12), dashResult.Result[1])
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchLibraryPanel(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": "unknown"})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel that exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "1"})
|
||||
resp := sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID, ":dashboardId": "2"})
|
||||
resp = sc.service.connectHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: 2,
|
||||
Name: "Panel - New name",
|
||||
Model: []byte(`
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": 1,
|
||||
"title": "Model - New name",
|
||||
"type": "text"
|
||||
}
|
||||
`),
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
var expected = libraryPanelResult{
|
||||
Result: libraryPanel{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
FolderID: 2,
|
||||
UID: sc.initialResult.Result.UID,
|
||||
Name: "Panel - New name",
|
||||
Model: map[string]interface{}{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"id": float64(1),
|
||||
"title": "Panel - New name",
|
||||
"type": "text",
|
||||
},
|
||||
Meta: LibraryPanelDTOMeta{
|
||||
CanEdit: true,
|
||||
ConnectedDashboards: 2,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
CreatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "user_in_db",
|
||||
AvatarUrl: "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
},
|
||||
UpdatedBy: LibraryPanelDTOMetaUser{
|
||||
ID: 1,
|
||||
Name: "signed_in_user",
|
||||
AvatarUrl: "/avatar/37524e1eb8b3e32850b57db0a19af93b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: 100,
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.FolderID = int64(100)
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with name only, it should change name successfully, sync title and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
Name: "New Name",
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.Name = "New Name"
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
sc.initialResult.Result.Model["title"] = "New Name"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with model only, it should change model successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
Model: []byte(`{ "title": "New Model Title", "name": "New Model Name" }`),
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.Model = map[string]interface{}{
|
||||
"title": "Text - Library Panel",
|
||||
"name": "New Model Name",
|
||||
}
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When another admin tries to patch a library panel, it should change UpdatedBy successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{}
|
||||
sc.reqContext.UserId = 2
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.initialResult.Result.Meta.UpdatedBy.ID = int64(2)
|
||||
sc.initialResult.Result.Meta.CreatedBy.Name = "user_in_db"
|
||||
sc.initialResult.Result.Meta.CreatedBy.AvatarUrl = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
if diff := cmp.Diff(sc.initialResult.Result, result.Result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with a name that already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := getCreateCommand(sc.folder.Id, "Another Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
cmd := patchLibraryPanelCommand{
|
||||
Name: "Text - Library Panel",
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
createFolder(t, &sc, &models.CreateFolderCommand{
|
||||
Uid: "NewTestFolder",
|
||||
Title: "New Test Folder",
|
||||
})
|
||||
command := getCreateCommand(sc.folder.Id, "Text - Library Panel")
|
||||
resp := sc.service.createHandler(sc.reqContext, command)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: 1,
|
||||
}
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": result.Result.UID})
|
||||
resp = sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
cmd := patchLibraryPanelCommand{
|
||||
FolderID: sc.folder.Id,
|
||||
}
|
||||
sc.reqContext.OrgId = 2
|
||||
sc.reqContext.ReplaceAllParams(map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.patchHandler(sc.reqContext, cmd)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
}
|
||||
const UserInDbName = "user_in_db"
|
||||
const UserInDbAvatar = "/avatar/402d08de060496d6b6874495fe20f5ad"
|
||||
|
||||
func TestLoadLibraryPanelsForDashboard(t *testing.T) {
|
||||
scenarioWithLibraryPanel(t, "When an admin tries to load a dashboard with a library panel, it should copy JSON properties from library panel",
|
||||
@ -739,13 +95,13 @@ func TestLoadLibraryPanelsForDashboard(t *testing.T) {
|
||||
"updated": sc.initialResult.Result.Meta.Updated,
|
||||
"createdBy": map[string]interface{}{
|
||||
"id": sc.initialResult.Result.Meta.CreatedBy.ID,
|
||||
"name": "user_in_db",
|
||||
"avatarUrl": "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
"name": UserInDbName,
|
||||
"avatarUrl": UserInDbAvatar,
|
||||
},
|
||||
"updatedBy": map[string]interface{}{
|
||||
"id": sc.initialResult.Result.Meta.UpdatedBy.ID,
|
||||
"name": "user_in_db",
|
||||
"avatarUrl": "/avatar/402d08de060496d6b6874495fe20f5ad",
|
||||
"name": UserInDbName,
|
||||
"avatarUrl": UserInDbAvatar,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1343,26 +699,76 @@ type scenarioContext struct {
|
||||
initialResult libraryPanelResult
|
||||
}
|
||||
|
||||
func createFolder(t *testing.T, sc *scenarioContext, cmd *models.CreateFolderCommand) {
|
||||
s := dashboards.NewFolderService(sc.user.OrgId, &sc.user)
|
||||
err := s.CreateFolder(cmd)
|
||||
require.NoError(t, err)
|
||||
sc.folder = cmd.Result
|
||||
type folderACLItem struct {
|
||||
roleType models.RoleType
|
||||
permission models.PermissionType
|
||||
}
|
||||
|
||||
func updateFolderACL(t *testing.T, sc scenarioContext, roleType models.RoleType, permission models.PermissionType) {
|
||||
cmd := models.UpdateDashboardAclCommand{
|
||||
DashboardID: sc.folder.Id,
|
||||
Items: []*models.DashboardAcl{
|
||||
{
|
||||
DashboardID: sc.folder.Id,
|
||||
Role: &roleType,
|
||||
Permission: permission,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
},
|
||||
},
|
||||
func createDashboard(t *testing.T, user models.SignedInUser, title string, folderID int64) *models.Dashboard {
|
||||
dash := models.NewDashboard(title)
|
||||
dash.FolderId = folderID
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
Dashboard: dash,
|
||||
Message: "",
|
||||
OrgId: user.OrgId,
|
||||
User: &user,
|
||||
Overwrite: false,
|
||||
}
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
bus.AddHandler("test", func(cmd *models.ValidateDashboardBeforeSaveCommand) error {
|
||||
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
|
||||
return nil
|
||||
})
|
||||
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||
cmd.Result = nil
|
||||
return nil
|
||||
})
|
||||
bus.AddHandler("test", func(cmd *models.UpdateDashboardAlertsCommand) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
dashboard, err := dashboards.NewService().SaveDashboard(dashItem, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dashboard
|
||||
}
|
||||
|
||||
func createFolderWithACL(t *testing.T, title string, user models.SignedInUser, items []folderACLItem) *models.Folder {
|
||||
s := dashboards.NewFolderService(user.OrgId, &user)
|
||||
folderCmd := models.CreateFolderCommand{
|
||||
Uid: title,
|
||||
Title: title,
|
||||
}
|
||||
err := s.CreateFolder(&folderCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
updateFolderACL(t, folderCmd.Result.Id, items)
|
||||
|
||||
return folderCmd.Result
|
||||
}
|
||||
|
||||
func updateFolderACL(t *testing.T, folderID int64, items []folderACLItem) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := models.UpdateDashboardAclCommand{
|
||||
DashboardID: folderID,
|
||||
}
|
||||
for _, item := range items {
|
||||
role := item.roleType
|
||||
permission := item.permission
|
||||
cmd.Items = append(cmd.Items, &models.DashboardAcl{
|
||||
DashboardID: folderID,
|
||||
Role: &role,
|
||||
Permission: permission,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
err := bus.Dispatch(&cmd)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@ -1428,7 +834,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
cmd := &models.CreateUserCommand{
|
||||
Email: "user.in.db@test.com",
|
||||
Name: "User In DB",
|
||||
Login: "user_in_db",
|
||||
Login: UserInDbName,
|
||||
}
|
||||
err := sqlstore.CreateUser(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
@ -1443,12 +849,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
},
|
||||
}
|
||||
|
||||
folderCmd := models.CreateFolderCommand{
|
||||
Uid: "testFolder",
|
||||
Title: "TestFolder",
|
||||
}
|
||||
|
||||
createFolder(t, &sc, &folderCmd)
|
||||
sc.folder = createFolderWithACL(t, "ScenarioFolder", sc.user, []folderACLItem{})
|
||||
|
||||
fn(t, sc)
|
||||
})
|
||||
|
@ -109,7 +109,7 @@ type createLibraryPanelCommand struct {
|
||||
|
||||
// patchLibraryPanelCommand is the command for patching a LibraryPanel
|
||||
type patchLibraryPanelCommand struct {
|
||||
FolderID int64 `json:"folderId"`
|
||||
FolderID int64 `json:"folderId" binding:"Default(-1)"`
|
||||
Name string `json:"name"`
|
||||
Model json.RawMessage `json:"model"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user