publicdashboards: split create/update api paths (#57940)
This PR splits the create and update paths for public dashboards and includes assorted refactors toward a proper REST API. Additionally, we removed the concept of a "public dashboard config" in favor of "public dashboard" Co-authored-by: juanicabanas <juan.cabanas@grafana.com> Co-authored-by: Ezequiel Victorero <ezequiel.victorero@grafana.com>
This commit is contained in:
co-authored by
juanicabanas
Ezequiel Victorero
parent
0367f61bb3
commit
6fcc5b42c0
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
||||
"github.com/grafana/grafana/pkg/services/publicdashboards/internal/tokens"
|
||||
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
@@ -73,10 +73,15 @@ func (api *Api) RegisterAPIEndpoints() {
|
||||
auth(middleware.ReqSignedIn, accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, uidScope)),
|
||||
routing.Wrap(api.GetPublicDashboard))
|
||||
|
||||
// Create/Update Public Dashboard
|
||||
// Create Public Dashboard
|
||||
api.RouteRegister.Post("/api/dashboards/uid/:dashboardUid/public-dashboards",
|
||||
auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
||||
routing.Wrap(api.SavePublicDashboard))
|
||||
routing.Wrap(api.CreatePublicDashboard))
|
||||
|
||||
// Update Public Dashboard
|
||||
api.RouteRegister.Put("/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
||||
auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
||||
routing.Wrap(api.UpdatePublicDashboard))
|
||||
|
||||
// Delete Public dashboard
|
||||
api.RouteRegister.Delete("/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
||||
@@ -94,59 +99,103 @@ func (api *Api) ListPublicDashboards(c *models.ReqContext) response.Response {
|
||||
return response.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetPublicDashboard Gets public dashboard configuration for dashboard
|
||||
// GET /api/dashboards/uid/:uid/public-config
|
||||
// GetPublicDashboard Gets public dashboard for dashboard
|
||||
// GET /api/dashboards/uid/:uid/public-dashboards
|
||||
func (api *Api) GetPublicDashboard(c *models.ReqContext) response.Response {
|
||||
// exit if we don't have a valid dashboardUid
|
||||
dashboardUid := web.Params(c.Req)[":dashboardUid"]
|
||||
if dashboardUid == "" || !util.IsValidShortUID(dashboardUid) {
|
||||
if !tokens.IsValidShortUID(dashboardUid) {
|
||||
api.handleError(c.Req.Context(), http.StatusBadRequest, "GetPublicDashboard: no valid dashboardUid", dashboards.ErrDashboardIdentifierNotSet)
|
||||
}
|
||||
|
||||
pdc, err := api.PublicDashboardService.FindByDashboardUid(c.Req.Context(), c.OrgID, web.Params(c.Req)[":dashboardUid"])
|
||||
pd, err := api.PublicDashboardService.FindByDashboardUid(c.Req.Context(), c.OrgID, web.Params(c.Req)[":dashboardUid"])
|
||||
|
||||
if err != nil {
|
||||
return api.handleError(c.Req.Context(), http.StatusInternalServerError, "GetPublicDashboardConfig: failed to get public dashboard config", err)
|
||||
return api.handleError(c.Req.Context(), http.StatusInternalServerError, "GetPublicDashboard: failed to get public dashboard ", err)
|
||||
}
|
||||
return response.JSON(http.StatusOK, pdc)
|
||||
|
||||
if pd == nil {
|
||||
return api.handleError(c.Req.Context(), http.StatusNotFound, "GetPublicDashboard: public dashboard not found", ErrPublicDashboardNotFound)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, pd)
|
||||
}
|
||||
|
||||
// SavePublicDashboard Sets public dashboard configuration for dashboard
|
||||
// POST /api/dashboards/uid/:uid/public-config
|
||||
func (api *Api) SavePublicDashboard(c *models.ReqContext) response.Response {
|
||||
// CreatePublicDashboard Sets public dashboard for dashboard
|
||||
// POST /api/dashboards/uid/:uid/public-dashboards
|
||||
func (api *Api) CreatePublicDashboard(c *models.ReqContext) response.Response {
|
||||
// exit if we don't have a valid dashboardUid
|
||||
dashboardUid := web.Params(c.Req)[":dashboardUid"]
|
||||
if dashboardUid == "" || !util.IsValidShortUID(dashboardUid) {
|
||||
api.handleError(c.Req.Context(), http.StatusBadRequest, "SavePublicDashboard: invalid dashboardUid", dashboards.ErrDashboardIdentifierNotSet)
|
||||
if !tokens.IsValidShortUID(dashboardUid) {
|
||||
return api.handleError(c.Req.Context(), http.StatusBadRequest, "CreatePublicDashboard: invalid dashboardUid", dashboards.ErrDashboardIdentifierInvalid)
|
||||
}
|
||||
|
||||
pubdash := &PublicDashboard{}
|
||||
if err := web.Bind(c.Req, pubdash); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "SavePublicDashboard: bad request data", err)
|
||||
pd := &PublicDashboard{}
|
||||
if err := web.Bind(c.Req, pd); err != nil {
|
||||
return api.handleError(c.Req.Context(), http.StatusBadRequest, "CreatePublicDashboard: bad request data", err)
|
||||
}
|
||||
|
||||
// Always set the orgID and userID from the session
|
||||
pubdash.OrgId = c.OrgID
|
||||
pd.OrgId = c.OrgID
|
||||
dto := SavePublicDashboardDTO{
|
||||
UserId: c.UserID,
|
||||
OrgId: c.OrgID,
|
||||
DashboardUid: dashboardUid,
|
||||
PublicDashboard: pubdash,
|
||||
PublicDashboard: pd,
|
||||
}
|
||||
|
||||
//Create the public dashboard
|
||||
pd, err := api.PublicDashboardService.Create(c.Req.Context(), c.SignedInUser, &dto)
|
||||
if err != nil {
|
||||
return api.handleError(c.Req.Context(), http.StatusInternalServerError, "CreatePublicDashboard: failed to create public dashboard", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, pd)
|
||||
}
|
||||
|
||||
// UpdatePublicDashboard Sets public dashboard for dashboard
|
||||
// PUT /api/dashboards/uid/:uid/public-dashboards
|
||||
func (api *Api) UpdatePublicDashboard(c *models.ReqContext) response.Response {
|
||||
// exit if we don't have a valid dashboardUid
|
||||
dashboardUid := web.Params(c.Req)[":dashboardUid"]
|
||||
if !tokens.IsValidShortUID(dashboardUid) {
|
||||
return api.handleError(c.Req.Context(), http.StatusBadRequest, "UpdatePublicDashboard: invalid dashboardUid", dashboards.ErrDashboardIdentifierInvalid)
|
||||
}
|
||||
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
if !tokens.IsValidShortUID(uid) {
|
||||
return api.handleError(c.Req.Context(), http.StatusBadRequest, "UpdatePublicDashboard: invalid public dashboard uid", ErrPublicDashboardIdentifierNotSet)
|
||||
}
|
||||
|
||||
pd := &PublicDashboard{}
|
||||
if err := web.Bind(c.Req, pd); err != nil {
|
||||
return api.handleError(c.Req.Context(), http.StatusBadRequest, "UpdatePublicDashboard: bad request data", err)
|
||||
}
|
||||
|
||||
// Always set the orgID and userID from the session
|
||||
pd.OrgId = c.OrgID
|
||||
pd.Uid = uid
|
||||
dto := SavePublicDashboardDTO{
|
||||
UserId: c.UserID,
|
||||
OrgId: c.OrgID,
|
||||
DashboardUid: dashboardUid,
|
||||
PublicDashboard: pd,
|
||||
}
|
||||
|
||||
// Save the public dashboard
|
||||
pubdash, err := api.PublicDashboardService.Save(c.Req.Context(), c.SignedInUser, &dto)
|
||||
pd, err := api.PublicDashboardService.Update(c.Req.Context(), c.SignedInUser, &dto)
|
||||
if err != nil {
|
||||
return api.handleError(c.Req.Context(), http.StatusInternalServerError, "SavePublicDashboardConfig: failed to save public dashboard configuration", err)
|
||||
return api.handleError(c.Req.Context(), http.StatusInternalServerError, "UpdatePublicDashboard: failed to update public dashboard", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, pubdash)
|
||||
return response.JSON(http.StatusOK, pd)
|
||||
}
|
||||
|
||||
// Delete a public dashboard
|
||||
// DELETE /api/dashboards/uid/:dashboardUid/public-dashboards/:uid
|
||||
func (api *Api) DeletePublicDashboard(c *models.ReqContext) response.Response {
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
if uid == "" || !util.IsValidShortUID(uid) {
|
||||
if !tokens.IsValidShortUID(uid) {
|
||||
return api.handleError(c.Req.Context(), http.StatusBadRequest, "DeletePublicDashboard: invalid dashboard uid", dashboards.ErrDashboardIdentifierNotSet)
|
||||
}
|
||||
|
||||
@@ -171,7 +220,6 @@ func (api *Api) handleError(ctx context.Context, code int, message string, err e
|
||||
return response.Error(publicDashboardErr.StatusCode, publicDashboardErr.Error(), publicDashboardErr)
|
||||
}
|
||||
|
||||
// handle dashboard errors as well
|
||||
var dashboardErr dashboards.DashboardErr
|
||||
if ok := errors.As(err, &dashboardErr); ok {
|
||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), dashboardErr)
|
||||
|
||||
@@ -57,10 +57,20 @@ func TestAPIFeatureFlag(t *testing.T) {
|
||||
Path: "/api/dashboards/uid/abc123/public-dashboards",
|
||||
},
|
||||
{
|
||||
Name: "API: Save Public Dashboard",
|
||||
Name: "API: Create Public Dashboard",
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/dashboards/uid/abc123/public-dashboards",
|
||||
},
|
||||
{
|
||||
Name: "API: Update Public Dashboard",
|
||||
Method: http.MethodPut,
|
||||
Path: "/api/dashboards/uid/abc123/public-dashboards",
|
||||
},
|
||||
{
|
||||
Name: "API: Delete Public Dashboard",
|
||||
Method: http.MethodDelete,
|
||||
Path: "/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -148,6 +158,354 @@ func TestAPIListPublicDashboard(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGetPublicDashboard(t *testing.T) {
|
||||
pubdash := &PublicDashboard{IsEnabled: true}
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
DashboardUid string
|
||||
ExpectedHttpResponse int
|
||||
PublicDashboardResult *PublicDashboard
|
||||
PublicDashboardErr error
|
||||
User *user.SignedInUser
|
||||
AccessControlEnabled bool
|
||||
ShouldCallService bool
|
||||
}{
|
||||
{
|
||||
Name: "retrieves public dashboard when dashboard is found",
|
||||
DashboardUid: "1",
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
PublicDashboardResult: pubdash,
|
||||
PublicDashboardErr: nil,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 404 when dashboard not found",
|
||||
DashboardUid: "77777",
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
PublicDashboardResult: nil,
|
||||
PublicDashboardErr: dashboards.ErrDashboardNotFound,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 500 when internal server error",
|
||||
DashboardUid: "1",
|
||||
ExpectedHttpResponse: http.StatusInternalServerError,
|
||||
PublicDashboardResult: nil,
|
||||
PublicDashboardErr: errors.New("database broken"),
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "retrieves public dashboard when dashboard is found RBAC on",
|
||||
DashboardUid: "1",
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
PublicDashboardResult: pubdash,
|
||||
PublicDashboardErr: nil,
|
||||
User: userViewerRBAC,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 403 when no permissions RBAC on",
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
PublicDashboardResult: pubdash,
|
||||
PublicDashboardErr: nil,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||
|
||||
if test.ShouldCallService {
|
||||
service.On("FindByDashboardUid", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).
|
||||
Return(test.PublicDashboardResult, test.PublicDashboardErr)
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.RBACEnabled = test.AccessControlEnabled
|
||||
|
||||
testServer := setupTestServer(
|
||||
t,
|
||||
cfg,
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
||||
service,
|
||||
nil,
|
||||
test.User,
|
||||
)
|
||||
|
||||
response := callAPI(
|
||||
testServer,
|
||||
http.MethodGet,
|
||||
"/api/dashboards/uid/1/public-dashboards",
|
||||
nil,
|
||||
t,
|
||||
)
|
||||
|
||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||
|
||||
if response.Code == http.StatusOK {
|
||||
var pdcResp PublicDashboard
|
||||
err := json.Unmarshal(response.Body.Bytes(), &pdcResp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.PublicDashboardResult, &pdcResp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiCreatePublicDashboard(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
DashboardUid string
|
||||
publicDashboard *PublicDashboard
|
||||
ExpectedHttpResponse int
|
||||
SaveDashboardErr error
|
||||
User *user.SignedInUser
|
||||
AccessControlEnabled bool
|
||||
ShouldCallService bool
|
||||
}{
|
||||
{
|
||||
Name: "returns 200 when update persists",
|
||||
DashboardUid: "1",
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
SaveDashboardErr: nil,
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 500 when not persisted",
|
||||
ExpectedHttpResponse: http.StatusInternalServerError,
|
||||
publicDashboard: &PublicDashboard{},
|
||||
SaveDashboardErr: errors.New("backend failed to save"),
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 404 when dashboard not found",
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
publicDashboard: &PublicDashboard{},
|
||||
SaveDashboardErr: dashboards.ErrDashboardNotFound,
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 200 when update persists RBAC on",
|
||||
DashboardUid: "1",
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
SaveDashboardErr: nil,
|
||||
User: userAdminRBAC,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 403 when no permissions",
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
SaveDashboardErr: nil,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
{
|
||||
Name: "returns 403 when no permissions RBAC on",
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
SaveDashboardErr: nil,
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||
|
||||
// this is to avoid AssertExpectations fail at t.Cleanup when the middleware returns before calling the service
|
||||
if test.ShouldCallService {
|
||||
service.On("Create", mock.Anything, mock.Anything, mock.AnythingOfType("*models.SavePublicDashboardDTO")).
|
||||
Return(&PublicDashboard{IsEnabled: true}, test.SaveDashboardErr)
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.RBACEnabled = test.AccessControlEnabled
|
||||
|
||||
testServer := setupTestServer(
|
||||
t,
|
||||
cfg,
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
||||
service,
|
||||
nil,
|
||||
test.User,
|
||||
)
|
||||
|
||||
response := callAPI(
|
||||
testServer,
|
||||
http.MethodPost,
|
||||
"/api/dashboards/uid/1/public-dashboards",
|
||||
strings.NewReader(`{ "isPublic": true }`),
|
||||
t,
|
||||
)
|
||||
|
||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||
|
||||
//check the result if it's a 200
|
||||
if response.Code == http.StatusOK {
|
||||
val, err := json.Marshal(test.publicDashboard)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(val), response.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIUpdatePublicDashboard(t *testing.T) {
|
||||
dashboardUid := "abc1234"
|
||||
publicDashboardUid := "1234asdfasdf"
|
||||
|
||||
adminUser := &user.SignedInUser{UserID: 4, OrgID: 1, OrgRole: org.RoleEditor, Login: "testEditorUser", Permissions: map[int64]map[string][]string{1: {dashboards.ActionDashboardsPublicWrite: {dashboards.ScopeDashboardsAll}}}}
|
||||
|
||||
userEditorPublicDashboard := &user.SignedInUser{UserID: 4, OrgID: 1, OrgRole: org.RoleEditor, Login: "testEditorUser", Permissions: map[int64]map[string][]string{1: {dashboards.ActionDashboardsPublicWrite: {fmt.Sprintf("dashboards:uid:%s", dashboardUid)}}}}
|
||||
|
||||
userEditorAnotherPublicDashboard := &user.SignedInUser{UserID: 4, OrgID: 1, OrgRole: org.RoleEditor, Login: "testEditorUser", Permissions: map[int64]map[string][]string{1: {dashboards.ActionDashboardsPublicWrite: {"another-uid"}}}}
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
User *user.SignedInUser
|
||||
DashboardUid string
|
||||
PublicDashboardUid string
|
||||
PublicDashboardRes *PublicDashboard
|
||||
PublicDashboardErr error
|
||||
ExpectedHttpResponse int
|
||||
ShouldCallService bool
|
||||
}{
|
||||
{
|
||||
Name: "Invalid dashboardUid",
|
||||
User: adminUser,
|
||||
DashboardUid: "",
|
||||
PublicDashboardUid: "",
|
||||
PublicDashboardRes: nil,
|
||||
PublicDashboardErr: dashboards.ErrDashboardIdentifierInvalid,
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
{
|
||||
Name: "Invalid public dashboard uid",
|
||||
User: adminUser,
|
||||
DashboardUid: dashboardUid,
|
||||
PublicDashboardUid: "",
|
||||
PublicDashboardRes: nil,
|
||||
PublicDashboardErr: ErrPublicDashboardNotFound,
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
{
|
||||
Name: "Service Error",
|
||||
User: adminUser,
|
||||
DashboardUid: dashboardUid,
|
||||
PublicDashboardUid: publicDashboardUid,
|
||||
PublicDashboardRes: nil,
|
||||
PublicDashboardErr: dashboards.ErrDashboardNotFound,
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "Success",
|
||||
User: adminUser,
|
||||
DashboardUid: dashboardUid,
|
||||
PublicDashboardUid: publicDashboardUid,
|
||||
PublicDashboardRes: &PublicDashboard{Uid: "success"},
|
||||
PublicDashboardErr: nil,
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
|
||||
// permissions
|
||||
{
|
||||
Name: "User can update this public dashboard",
|
||||
User: userEditorPublicDashboard,
|
||||
DashboardUid: dashboardUid,
|
||||
PublicDashboardUid: publicDashboardUid,
|
||||
PublicDashboardRes: &PublicDashboard{Uid: "success"},
|
||||
PublicDashboardErr: nil,
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "User has permissions on another dashboard",
|
||||
User: userEditorAnotherPublicDashboard,
|
||||
PublicDashboardUid: publicDashboardUid,
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
{
|
||||
Name: "Viewer cannot update any dashboard",
|
||||
User: userViewer,
|
||||
PublicDashboardUid: publicDashboardUid,
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||
|
||||
if test.ShouldCallService {
|
||||
service.On("Update", mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(test.PublicDashboardRes, test.PublicDashboardErr)
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
features := featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards)
|
||||
testServer := setupTestServer(t, cfg, features, service, nil, test.User)
|
||||
url := fmt.Sprintf("/api/dashboards/uid/%s/public-dashboards/%s", test.DashboardUid, test.PublicDashboardUid)
|
||||
body := strings.NewReader(fmt.Sprintf(`{ "uid": "%s"}`, test.PublicDashboardUid))
|
||||
|
||||
response := callAPI(testServer, http.MethodPut, url, body, t)
|
||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||
|
||||
// check whether service called
|
||||
if !test.ShouldCallService {
|
||||
service.AssertNotCalled(t, "Update")
|
||||
}
|
||||
|
||||
fmt.Println(response.Body.String())
|
||||
|
||||
// check response
|
||||
if response.Code == http.StatusOK {
|
||||
val, err := json.Marshal(test.PublicDashboardRes)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(val), response.Body.String())
|
||||
|
||||
// verify 4XXs except 403 && 404
|
||||
} else if test.ExpectedHttpResponse > 200 &&
|
||||
test.ExpectedHttpResponse != 403 &&
|
||||
test.ExpectedHttpResponse != 404 {
|
||||
var errResp JsonErrResponse
|
||||
err := json.Unmarshal(response.Body.Bytes(), &errResp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.PublicDashboardErr.Error(), errResp.Error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIDeletePublicDashboard(t *testing.T) {
|
||||
dashboardUid := "abc1234"
|
||||
publicDashboardUid := "1234asdfasdf"
|
||||
@@ -275,219 +633,3 @@ func TestAPIDeletePublicDashboard(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGetPublicDashboard(t *testing.T) {
|
||||
pubdash := &PublicDashboard{IsEnabled: true}
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
DashboardUid string
|
||||
ExpectedHttpResponse int
|
||||
PublicDashboardResult *PublicDashboard
|
||||
PublicDashboardErr error
|
||||
User *user.SignedInUser
|
||||
AccessControlEnabled bool
|
||||
ShouldCallService bool
|
||||
}{
|
||||
{
|
||||
Name: "retrieves public dashboard when dashboard is found",
|
||||
DashboardUid: "1",
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
PublicDashboardResult: pubdash,
|
||||
PublicDashboardErr: nil,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 404 when dashboard not found",
|
||||
DashboardUid: "77777",
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
PublicDashboardResult: nil,
|
||||
PublicDashboardErr: dashboards.ErrDashboardNotFound,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 500 when internal server error",
|
||||
DashboardUid: "1",
|
||||
ExpectedHttpResponse: http.StatusInternalServerError,
|
||||
PublicDashboardResult: nil,
|
||||
PublicDashboardErr: errors.New("database broken"),
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "retrieves public dashboard when dashboard is found RBAC on",
|
||||
DashboardUid: "1",
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
PublicDashboardResult: pubdash,
|
||||
PublicDashboardErr: nil,
|
||||
User: userViewerRBAC,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 403 when no permissions RBAC on",
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
PublicDashboardResult: pubdash,
|
||||
PublicDashboardErr: nil,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||
|
||||
if test.ShouldCallService {
|
||||
service.On("FindByDashboardUid", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).
|
||||
Return(test.PublicDashboardResult, test.PublicDashboardErr)
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.RBACEnabled = test.AccessControlEnabled
|
||||
|
||||
testServer := setupTestServer(
|
||||
t,
|
||||
cfg,
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
||||
service,
|
||||
nil,
|
||||
test.User,
|
||||
)
|
||||
|
||||
response := callAPI(
|
||||
testServer,
|
||||
http.MethodGet,
|
||||
"/api/dashboards/uid/1/public-dashboards",
|
||||
nil,
|
||||
t,
|
||||
)
|
||||
|
||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||
|
||||
if response.Code == http.StatusOK {
|
||||
var pdcResp PublicDashboard
|
||||
err := json.Unmarshal(response.Body.Bytes(), &pdcResp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.PublicDashboardResult, &pdcResp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiSavePublicDashboard(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
DashboardUid string
|
||||
publicDashboard *PublicDashboard
|
||||
ExpectedHttpResponse int
|
||||
SaveDashboardErr error
|
||||
User *user.SignedInUser
|
||||
AccessControlEnabled bool
|
||||
ShouldCallService bool
|
||||
}{
|
||||
{
|
||||
Name: "returns 200 when update persists",
|
||||
DashboardUid: "1",
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
SaveDashboardErr: nil,
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 500 when not persisted",
|
||||
ExpectedHttpResponse: http.StatusInternalServerError,
|
||||
publicDashboard: &PublicDashboard{},
|
||||
SaveDashboardErr: errors.New("backend failed to save"),
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 404 when dashboard not found",
|
||||
ExpectedHttpResponse: http.StatusNotFound,
|
||||
publicDashboard: &PublicDashboard{},
|
||||
SaveDashboardErr: dashboards.ErrDashboardNotFound,
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 200 when update persists RBAC on",
|
||||
DashboardUid: "1",
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
ExpectedHttpResponse: http.StatusOK,
|
||||
SaveDashboardErr: nil,
|
||||
User: userAdminRBAC,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: true,
|
||||
},
|
||||
{
|
||||
Name: "returns 403 when no permissions",
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
SaveDashboardErr: nil,
|
||||
User: userViewer,
|
||||
AccessControlEnabled: false,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
{
|
||||
Name: "returns 403 when no permissions RBAC on",
|
||||
ExpectedHttpResponse: http.StatusForbidden,
|
||||
publicDashboard: &PublicDashboard{IsEnabled: true},
|
||||
SaveDashboardErr: nil,
|
||||
User: userAdmin,
|
||||
AccessControlEnabled: true,
|
||||
ShouldCallService: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||
|
||||
// this is to avoid AssertExpectations fail at t.Cleanup when the middleware returns before calling the service
|
||||
if test.ShouldCallService {
|
||||
service.On("Save", mock.Anything, mock.Anything, mock.AnythingOfType("*models.SavePublicDashboardDTO")).
|
||||
Return(&PublicDashboard{IsEnabled: true}, test.SaveDashboardErr)
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.RBACEnabled = test.AccessControlEnabled
|
||||
|
||||
testServer := setupTestServer(
|
||||
t,
|
||||
cfg,
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
||||
service,
|
||||
nil,
|
||||
test.User,
|
||||
)
|
||||
|
||||
response := callAPI(
|
||||
testServer,
|
||||
http.MethodPost,
|
||||
"/api/dashboards/uid/1/public-dashboards",
|
||||
strings.NewReader(`{ "isPublic": true }`),
|
||||
t,
|
||||
)
|
||||
|
||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||
|
||||
//check the result if it's a 200
|
||||
if response.Code == http.StatusOK {
|
||||
val, err := json.Marshal(test.publicDashboard)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(val), response.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
|
||||
ac := acmock.New()
|
||||
cfg.RBACEnabled = false
|
||||
service := publicdashboardsService.ProvideService(cfg, store, qds, annotationsService, ac)
|
||||
pubdash, err := service.Save(context.Background(), &user.SignedInUser{}, savePubDashboardCmd)
|
||||
pubdash, err := service.Create(context.Background(), &user.SignedInUser{}, savePubDashboardCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
// setup test server
|
||||
|
||||
Reference in New Issue
Block a user