Chore: Refactor api handlers to use web.Bind (#42199)

* Chore: Refactor api handlers to use web.Bind

* fix comments

* fix comment

* trying to fix most of the tests and force routing.Wrap type check

* fix library panels tests

* fix frontend logging tests

* allow passing nil as a response to skip writing

* return nil instead of the response

* rewrite login handler function types

* remove handlerFuncCtx

* make linter happy

* remove old bindings from the libraryelements

* restore comments
pull/40068/head
Serge Zaitsev 4 years ago committed by GitHub
parent 9cbc872f22
commit d9cdcb550e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      pkg/api/admin_users.go
  2. 9
      pkg/api/admin_users_test.go
  3. 43
      pkg/api/alerting.go
  4. 3
      pkg/api/alerting_test.go
  5. 32
      pkg/api/annotations.go
  6. 12
      pkg/api/annotations_test.go
  7. 142
      pkg/api/api.go
  8. 16
      pkg/api/apikey.go
  9. 17
      pkg/api/common_test.go
  10. 31
      pkg/api/dashboard.go
  11. 8
      pkg/api/dashboard_permission.go
  12. 3
      pkg/api/dashboard_permission_test.go
  13. 9
      pkg/api/dashboard_test.go
  14. 13
      pkg/api/datasources.go
  15. 36
      pkg/api/datasources_test.go
  16. 13
      pkg/api/folder.go
  17. 7
      pkg/api/folder_permission.go
  18. 3
      pkg/api/folder_permission_test.go
  19. 6
      pkg/api/folder_test.go
  20. 12
      pkg/api/frontend_logging.go
  21. 6
      pkg/api/frontend_logging_test.go
  22. 8
      pkg/api/frontend_metrics.go
  23. 14
      pkg/api/frontendlogging/sentry.go
  24. 2
      pkg/api/ldap_debug.go
  25. 34
      pkg/api/login_oauth.go
  26. 19
      pkg/api/login_test.go
  27. 13
      pkg/api/metrics.go
  28. 31
      pkg/api/org.go
  29. 13
      pkg/api/org_invite.go
  30. 29
      pkg/api/org_users.go
  31. 4
      pkg/api/org_users_test.go
  32. 14
      pkg/api/password.go
  33. 14
      pkg/api/playlist.go
  34. 18
      pkg/api/plugins.go
  35. 20
      pkg/api/preferences.go
  36. 14
      pkg/api/quota.go
  37. 12
      pkg/api/routing/routing.go
  38. 7
      pkg/api/short_url.go
  39. 3
      pkg/api/short_url_test.go
  40. 14
      pkg/api/signup.go
  41. 20
      pkg/api/team.go
  42. 14
      pkg/api/team_members.go
  43. 8
      pkg/api/team_test.go
  44. 20
      pkg/api/user.go
  45. 8
      pkg/api/user_token.go
  46. 3
      pkg/api/user_token_test.go
  47. 20
      pkg/services/libraryelements/api.go
  48. 18
      pkg/services/libraryelements/libraryelements_create_test.go
  49. 54
      pkg/services/libraryelements/libraryelements_get_all_test.go
  50. 65
      pkg/services/libraryelements/libraryelements_patch_test.go
  51. 57
      pkg/services/libraryelements/libraryelements_permissions_test.go
  52. 13
      pkg/services/libraryelements/libraryelements_test.go
  53. 2
      pkg/services/libraryelements/models.go
  54. 6
      pkg/services/live/live.go

@ -3,6 +3,7 @@ package api
import (
"errors"
"fmt"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -11,9 +12,14 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
func (hs *HTTPServer) AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) response.Response {
func (hs *HTTPServer) AdminCreateUser(c *models.ReqContext) response.Response {
form := dtos.AdminCreateUserForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd := models.CreateUserCommand{
Login: form.Login,
Email: form.Email,
@ -56,7 +62,11 @@ func (hs *HTTPServer) AdminCreateUser(c *models.ReqContext, form dtos.AdminCreat
return response.JSON(200, result)
}
func AdminUpdateUserPassword(c *models.ReqContext, form dtos.AdminUpdateUserPasswordForm) response.Response {
func AdminUpdateUserPassword(c *models.ReqContext) response.Response {
form := dtos.AdminUpdateUserPasswordForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID := c.ParamsInt64(":id")
if len(form.Password) < 4 {
@ -87,7 +97,11 @@ func AdminUpdateUserPassword(c *models.ReqContext, form dtos.AdminUpdateUserPass
}
// PUT /api/admin/users/:id/permissions
func (hs *HTTPServer) AdminUpdateUserPermissions(c *models.ReqContext, form dtos.AdminUpdateUserPermissionsForm) response.Response {
func (hs *HTTPServer) AdminUpdateUserPermissions(c *models.ReqContext) response.Response {
form := dtos.AdminUpdateUserPermissionsForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID := c.ParamsInt64(":id")
err := updateUserPermissions(hs.SQLStore, userID, form.IsGrafanaAdmin)
@ -182,7 +196,11 @@ func (hs *HTTPServer) AdminGetUserAuthTokens(c *models.ReqContext) response.Resp
}
// POST /api/admin/users/:id/revoke-auth-token
func (hs *HTTPServer) AdminRevokeUserAuthToken(c *models.ReqContext, cmd models.RevokeAuthTokenCmd) response.Response {
func (hs *HTTPServer) AdminRevokeUserAuthToken(c *models.ReqContext) response.Response {
cmd := models.RevokeAuthTokenCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID := c.ParamsInt64(":id")
return hs.revokeUserAuthTokenInternal(c, userID, cmd)
}

@ -315,12 +315,13 @@ func putAdminScenario(t *testing.T, desc string, url string, routePattern string
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return hs.AdminUpdateUserPermissions(c, cmd)
return hs.AdminUpdateUserPermissions(c)
})
sc.m.Put(routePattern, sc.defaultHandler)
@ -370,12 +371,13 @@ func adminRevokeUserAuthTokenScenario(t *testing.T, desc string, url string, rou
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.AdminRevokeUserAuthToken(c, cmd)
return hs.AdminRevokeUserAuthToken(c)
})
sc.m.Post(routePattern, sc.defaultHandler)
@ -470,10 +472,11 @@ func adminCreateUserScenario(t *testing.T, desc string, url string, routePattern
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
return hs.AdminCreateUser(c, cmd)
return hs.AdminCreateUser(c)
})
sc.m.Post(routePattern, sc.defaultHandler)

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
"github.com/grafana/grafana/pkg/api/dtos"
@ -132,7 +133,11 @@ func GetAlerts(c *models.ReqContext) response.Response {
}
// POST /api/alerts/test
func (hs *HTTPServer) AlertTest(c *models.ReqContext, dto dtos.AlertTestCommand) response.Response {
func (hs *HTTPServer) AlertTest(c *models.ReqContext) response.Response {
dto := dtos.AlertTestCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
return response.Error(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
}
@ -276,7 +281,11 @@ func GetAlertNotificationByUID(c *models.ReqContext) response.Response {
return response.JSON(200, dtos.NewAlertNotification(query.Result))
}
func CreateAlertNotification(c *models.ReqContext, cmd models.CreateAlertNotificationCommand) response.Response {
func CreateAlertNotification(c *models.ReqContext) response.Response {
cmd := models.CreateAlertNotificationCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
if err := bus.DispatchCtx(c.Req.Context(), &cmd); err != nil {
@ -293,7 +302,11 @@ func CreateAlertNotification(c *models.ReqContext, cmd models.CreateAlertNotific
return response.JSON(200, dtos.NewAlertNotification(cmd.Result))
}
func (hs *HTTPServer) UpdateAlertNotification(c *models.ReqContext, cmd models.UpdateAlertNotificationCommand) response.Response {
func (hs *HTTPServer) UpdateAlertNotification(c *models.ReqContext) response.Response {
cmd := models.UpdateAlertNotificationCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
err := hs.fillWithSecureSettingsData(c.Req.Context(), &cmd)
@ -324,7 +337,11 @@ func (hs *HTTPServer) UpdateAlertNotification(c *models.ReqContext, cmd models.U
return response.JSON(200, dtos.NewAlertNotification(query.Result))
}
func (hs *HTTPServer) UpdateAlertNotificationByUID(c *models.ReqContext, cmd models.UpdateAlertNotificationWithUidCommand) response.Response {
func (hs *HTTPServer) UpdateAlertNotificationByUID(c *models.ReqContext) response.Response {
cmd := models.UpdateAlertNotificationWithUidCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
cmd.Uid = web.Params(c.Req)[":uid"]
@ -444,7 +461,11 @@ func DeleteAlertNotificationByUID(c *models.ReqContext) response.Response {
}
// POST /api/alert-notifications/test
func NotificationTest(c *models.ReqContext, dto dtos.NotificationTestCommand) response.Response {
func NotificationTest(c *models.ReqContext) response.Response {
dto := dtos.NotificationTestCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd := &alerting.NotificationTestCommand{
OrgID: c.OrgId,
ID: dto.ID,
@ -470,7 +491,11 @@ func NotificationTest(c *models.ReqContext, dto dtos.NotificationTestCommand) re
}
// POST /api/alerts/:alertId/pause
func PauseAlert(c *models.ReqContext, dto dtos.PauseAlertCommand) response.Response {
func PauseAlert(c *models.ReqContext) response.Response {
dto := dtos.PauseAlertCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
alertID := c.ParamsInt64(":alertId")
result := make(map[string]interface{})
result["alertId"] = alertID
@ -523,7 +548,11 @@ func PauseAlert(c *models.ReqContext, dto dtos.PauseAlertCommand) response.Respo
}
// POST /api/admin/pause-all-alerts
func PauseAllAlerts(c *models.ReqContext, dto dtos.PauseAllAlertsCommand) response.Response {
func PauseAllAlerts(c *models.ReqContext) response.Response {
dto := dtos.PauseAllAlertsCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
updateCmd := models.PauseAllAlertCommand{
Paused: dto.Paused,
}

@ -166,12 +166,13 @@ func postAlertScenario(t *testing.T, desc string, url string, routePattern strin
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return PauseAlert(c, cmd)
return PauseAlert(c)
})
sc.m.Post(routePattern, sc.defaultHandler)

@ -2,6 +2,7 @@ package api
import (
"errors"
"net/http"
"strings"
"github.com/grafana/grafana/pkg/api/dtos"
@ -10,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
func GetAnnotations(c *models.ReqContext) response.Response {
@ -51,7 +53,11 @@ func (e *CreateAnnotationError) Error() string {
return e.message
}
func PostAnnotation(c *models.ReqContext, cmd dtos.PostAnnotationsCmd) response.Response {
func PostAnnotation(c *models.ReqContext) response.Response {
cmd := dtos.PostAnnotationsCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if canSave, err := canSaveByDashboardID(c, cmd.DashboardId); err != nil || !canSave {
return dashboardGuardianResponse(err)
}
@ -98,7 +104,11 @@ func formatGraphiteAnnotation(what string, data string) string {
return text
}
func PostGraphiteAnnotation(c *models.ReqContext, cmd dtos.PostGraphiteAnnotationsCmd) response.Response {
func PostGraphiteAnnotation(c *models.ReqContext) response.Response {
cmd := dtos.PostGraphiteAnnotationsCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
repo := annotations.GetRepository()
if cmd.What == "" {
@ -149,7 +159,11 @@ func PostGraphiteAnnotation(c *models.ReqContext, cmd dtos.PostGraphiteAnnotatio
})
}
func UpdateAnnotation(c *models.ReqContext, cmd dtos.UpdateAnnotationsCmd) response.Response {
func UpdateAnnotation(c *models.ReqContext) response.Response {
cmd := dtos.UpdateAnnotationsCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
annotationID := c.ParamsInt64(":annotationId")
repo := annotations.GetRepository()
@ -175,7 +189,11 @@ func UpdateAnnotation(c *models.ReqContext, cmd dtos.UpdateAnnotationsCmd) respo
return response.Success("Annotation updated")
}
func PatchAnnotation(c *models.ReqContext, cmd dtos.PatchAnnotationsCmd) response.Response {
func PatchAnnotation(c *models.ReqContext) response.Response {
cmd := dtos.PatchAnnotationsCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
annotationID := c.ParamsInt64(":annotationId")
repo := annotations.GetRepository()
@ -223,7 +241,11 @@ func PatchAnnotation(c *models.ReqContext, cmd dtos.PatchAnnotationsCmd) respons
return response.Success("Annotation patched")
}
func DeleteAnnotations(c *models.ReqContext, cmd dtos.DeleteAnnotationsCmd) response.Response {
func DeleteAnnotations(c *models.ReqContext) response.Response {
cmd := dtos.DeleteAnnotationsCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
repo := annotations.GetRepository()
err := repo.Delete(&annotations.DeleteParams{

@ -275,12 +275,13 @@ func postAnnotationScenario(t *testing.T, desc string, url string, routePattern
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return PostAnnotation(c, cmd)
return PostAnnotation(c)
})
fakeAnnoRepo = &fakeAnnotationsRepo{}
@ -299,12 +300,13 @@ func putAnnotationScenario(t *testing.T, desc string, url string, routePattern s
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return UpdateAnnotation(c, cmd)
return UpdateAnnotation(c)
})
fakeAnnoRepo = &fakeAnnotationsRepo{}
@ -322,12 +324,13 @@ func patchAnnotationScenario(t *testing.T, desc string, url string, routePattern
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return PatchAnnotation(c, cmd)
return PatchAnnotation(c)
})
fakeAnnoRepo = &fakeAnnotationsRepo{}
@ -346,12 +349,13 @@ func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePatte
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return DeleteAnnotations(c, cmd)
return DeleteAnnotations(c)
})
fakeAnnoRepo = &fakeAnnotationsRepo{}

@ -4,13 +4,10 @@ package api
import (
"time"
"github.com/go-macaron/binding"
"github.com/grafana/grafana/pkg/api/avatar"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/frontendlogging"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
@ -35,7 +32,6 @@ func (hs *HTTPServer) registerRoutes() {
authorize := acmiddleware.Middleware(hs.AccessControl)
authorizeInOrg := acmiddleware.AuthorizeInOrgMiddleware(hs.AccessControl, hs.SQLStore)
quota := middleware.Quota(hs.QuotaService)
bind := binding.Bind
r := hs.RouteRegister
@ -116,19 +112,19 @@ func (hs *HTTPServer) registerRoutes() {
r.Get("/verify", hs.Index)
r.Get("/signup", hs.Index)
r.Get("/api/user/signup/options", routing.Wrap(GetSignUpOptions))
r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), routing.Wrap(SignUp))
r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), routing.Wrap(hs.SignUpStep2))
r.Post("/api/user/signup", quota("user"), routing.Wrap(SignUp))
r.Post("/api/user/signup/step2", routing.Wrap(hs.SignUpStep2))
// invited
r.Get("/api/user/invite/:code", routing.Wrap(GetInviteInfoByCode))
r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), routing.Wrap(hs.CompleteInvite))
r.Post("/api/user/invite/complete", routing.Wrap(hs.CompleteInvite))
// reset password
r.Get("/user/password/send-reset-email", reqNotSignedIn, hs.Index)
r.Get("/user/password/reset", hs.Index)
r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), routing.Wrap(SendResetPasswordEmail))
r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), routing.Wrap(ResetPassword))
r.Post("/api/user/password/send-reset-email", routing.Wrap(SendResetPasswordEmail))
r.Post("/api/user/password/reset", routing.Wrap(ResetPassword))
// dashboard snapshots
r.Get("/dashboard/snapshot/*", reqNoAuth, hs.Index)
@ -145,7 +141,7 @@ func (hs *HTTPServer) registerRoutes() {
// user (signed in)
apiRoute.Group("/user", func(userRoute routing.RouteRegister) {
userRoute.Get("/", routing.Wrap(GetSignedInUser))
userRoute.Put("/", bind(models.UpdateUserCommand{}), routing.Wrap(UpdateSignedInUser))
userRoute.Put("/", routing.Wrap(UpdateSignedInUser))
userRoute.Post("/using/:id", routing.Wrap(UserSetUsingOrg))
userRoute.Get("/orgs", routing.Wrap(GetSignedInUserOrgList))
userRoute.Get("/teams", routing.Wrap(GetSignedInUserTeamList))
@ -153,17 +149,17 @@ func (hs *HTTPServer) registerRoutes() {
userRoute.Post("/stars/dashboard/:id", routing.Wrap(StarDashboard))
userRoute.Delete("/stars/dashboard/:id", routing.Wrap(UnstarDashboard))
userRoute.Put("/password", bind(models.ChangeUserPasswordCommand{}), routing.Wrap(ChangeUserPassword))
userRoute.Put("/password", routing.Wrap(ChangeUserPassword))
userRoute.Get("/quotas", routing.Wrap(GetUserQuotas))
userRoute.Put("/helpflags/:id", routing.Wrap(SetHelpFlag))
// For dev purpose
userRoute.Get("/helpflags/clear", routing.Wrap(ClearHelpFlags))
userRoute.Get("/preferences", routing.Wrap(hs.GetUserPreferences))
userRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), routing.Wrap(hs.UpdateUserPreferences))
userRoute.Put("/preferences", routing.Wrap(hs.UpdateUserPreferences))
userRoute.Get("/auth-tokens", routing.Wrap(hs.GetUserAuthTokens))
userRoute.Post("/revoke-auth-token", bind(models.RevokeAuthTokenCmd{}), routing.Wrap(hs.RevokeUserAuthToken))
userRoute.Post("/revoke-auth-token", routing.Wrap(hs.RevokeUserAuthToken))
}, reqSignedInNoAnonymous)
apiRoute.Group("/users", func(usersRoute routing.RouteRegister) {
@ -175,21 +171,21 @@ func (hs *HTTPServer) registerRoutes() {
usersRoute.Get("/:id/orgs", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, userIDScope)), routing.Wrap(GetUserOrgList))
// query parameters /users/lookup?loginOrEmail=admin@example.com
usersRoute.Get("/lookup", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersRead, ac.ScopeGlobalUsersAll)), routing.Wrap(GetUserByLoginOrEmail))
usersRoute.Put("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersWrite, userIDScope)), bind(models.UpdateUserCommand{}), routing.Wrap(UpdateUser))
usersRoute.Put("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersWrite, userIDScope)), routing.Wrap(UpdateUser))
usersRoute.Post("/:id/using/:orgId", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersWrite, userIDScope)), routing.Wrap(UpdateUserActiveOrg))
})
// team (admin permission required)
apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) {
teamsRoute.Post("/", bind(models.CreateTeamCommand{}), routing.Wrap(hs.CreateTeam))
teamsRoute.Put("/:teamId", bind(models.UpdateTeamCommand{}), routing.Wrap(hs.UpdateTeam))
teamsRoute.Post("/", routing.Wrap(hs.CreateTeam))
teamsRoute.Put("/:teamId", routing.Wrap(hs.UpdateTeam))
teamsRoute.Delete("/:teamId", routing.Wrap(hs.DeleteTeamByID))
teamsRoute.Get("/:teamId/members", routing.Wrap(hs.GetTeamMembers))
teamsRoute.Post("/:teamId/members", bind(models.AddTeamMemberCommand{}), routing.Wrap(hs.AddTeamMember))
teamsRoute.Put("/:teamId/members/:userId", bind(models.UpdateTeamMemberCommand{}), routing.Wrap(hs.UpdateTeamMember))
teamsRoute.Post("/:teamId/members", routing.Wrap(hs.AddTeamMember))
teamsRoute.Put("/:teamId/members/:userId", routing.Wrap(hs.UpdateTeamMember))
teamsRoute.Delete("/:teamId/members/:userId", routing.Wrap(hs.RemoveTeamMember))
teamsRoute.Get("/:teamId/preferences", routing.Wrap(hs.GetTeamPreferences))
teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), routing.Wrap(hs.UpdateTeamPreferences))
teamsRoute.Put("/:teamId/preferences", routing.Wrap(hs.UpdateTeamPreferences))
}, reqCanAccessTeams)
// team without requirement of user to be org admin
@ -207,22 +203,22 @@ func (hs *HTTPServer) registerRoutes() {
// current org
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
userIDScope := ac.Scope("users", "id", ac.Parameter(":userId"))
orgRoute.Put("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsWrite)), bind(dtos.UpdateOrgForm{}), routing.Wrap(UpdateCurrentOrg))
orgRoute.Put("/address", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsWrite)), bind(dtos.UpdateOrgAddressForm{}), routing.Wrap(UpdateCurrentOrgAddress))
orgRoute.Put("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(UpdateCurrentOrg))
orgRoute.Put("/address", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(UpdateCurrentOrgAddress))
orgRoute.Get("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead, ac.ScopeUsersAll)), routing.Wrap(hs.GetOrgUsersForCurrentOrg))
orgRoute.Get("/users/search", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRead, ac.ScopeUsersAll)), routing.Wrap(hs.SearchOrgUsersWithPaging))
orgRoute.Post("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), quota("user"), bind(models.AddOrgUserCommand{}), routing.Wrap(hs.AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), bind(models.UpdateOrgUserCommand{}), routing.Wrap(hs.UpdateOrgUserForCurrentOrg))
orgRoute.Post("/users", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), quota("user"), routing.Wrap(hs.AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), routing.Wrap(hs.UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUserForCurrentOrg))
// invites
orgRoute.Get("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), routing.Wrap(GetPendingOrgInvites))
orgRoute.Post("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), quota("user"), bind(dtos.AddInviteForm{}), routing.Wrap(AddOrgInvite))
orgRoute.Post("/invites", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), quota("user"), routing.Wrap(AddOrgInvite))
orgRoute.Patch("/invites/:code/revoke", authorize(reqOrgAdmin, ac.EvalPermission(ac.ActionUsersCreate)), routing.Wrap(RevokeInvite))
// prefs
orgRoute.Get("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesRead)), routing.Wrap(hs.GetOrgPreferences))
orgRoute.Put("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), bind(dtos.UpdatePrefsCmd{}), routing.Wrap(hs.UpdateOrgPreferences))
orgRoute.Put("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), routing.Wrap(hs.UpdateOrgPreferences))
})
// current org without requirement of user to be org admin
@ -231,7 +227,7 @@ func (hs *HTTPServer) registerRoutes() {
})
// create new org
apiRoute.Post("/orgs", authorizeInOrg(reqSignedIn, acmiddleware.UseGlobalOrg, ac.EvalPermission(ActionOrgsCreate)), quota("org"), bind(models.CreateOrgCommand{}), routing.Wrap(hs.CreateOrg))
apiRoute.Post("/orgs", authorizeInOrg(reqSignedIn, acmiddleware.UseGlobalOrg, ac.EvalPermission(ActionOrgsCreate)), quota("org"), routing.Wrap(hs.CreateOrg))
// search all orgs
apiRoute.Get("/orgs", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseGlobalOrg, ac.EvalPermission(ActionOrgsRead)), routing.Wrap(SearchOrgs))
@ -240,15 +236,15 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Group("/orgs/:orgId", func(orgsRoute routing.RouteRegister) {
userIDScope := ac.Scope("users", "id", ac.Parameter(":userId"))
orgsRoute.Get("/", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsRead)), routing.Wrap(GetOrgByID))
orgsRoute.Put("/", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsWrite)), bind(dtos.UpdateOrgForm{}), routing.Wrap(UpdateOrg))
orgsRoute.Put("/address", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsWrite)), bind(dtos.UpdateOrgAddressForm{}), routing.Wrap(UpdateOrgAddress))
orgsRoute.Put("/", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(UpdateOrg))
orgsRoute.Put("/address", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsWrite)), routing.Wrap(UpdateOrgAddress))
orgsRoute.Delete("/", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsDelete)), routing.Wrap(DeleteOrgByID))
orgsRoute.Get("/users", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRead, ac.ScopeUsersAll)), routing.Wrap(hs.GetOrgUsers))
orgsRoute.Post("/users", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), bind(models.AddOrgUserCommand{}), routing.Wrap(hs.AddOrgUser))
orgsRoute.Patch("/users/:userId", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), bind(models.UpdateOrgUserCommand{}), routing.Wrap(hs.UpdateOrgUser))
orgsRoute.Post("/users", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersAdd, ac.ScopeUsersAll)), routing.Wrap(hs.AddOrgUser))
orgsRoute.Patch("/users/:userId", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRoleUpdate, userIDScope)), routing.Wrap(hs.UpdateOrgUser))
orgsRoute.Delete("/users/:userId", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ac.ActionOrgUsersRemove, userIDScope)), routing.Wrap(hs.RemoveOrgUser))
orgsRoute.Get("/quotas", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasRead)), routing.Wrap(hs.GetOrgQuotas))
orgsRoute.Put("/quotas/:target", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasWrite)), bind(models.UpdateOrgQuotaCmd{}), routing.Wrap(hs.UpdateOrgQuota))
orgsRoute.Put("/quotas/:target", authorizeInOrg(reqGrafanaAdmin, acmiddleware.UseOrgFromContextParams, ac.EvalPermission(ActionOrgsQuotasWrite)), routing.Wrap(hs.UpdateOrgQuota))
})
// orgs (admin routes)
@ -257,21 +253,21 @@ func (hs *HTTPServer) registerRoutes() {
// auth api keys
apiRoute.Group("/auth/keys", func(keysRoute routing.RouteRegister) {
keysRoute.Get("/", routing.Wrap(GetAPIKeys))
keysRoute.Post("/", quota("api_key"), bind(models.AddApiKeyCommand{}), routing.Wrap(hs.AddAPIKey))
keysRoute.Post("/additional", quota("api_key"), bind(models.AddApiKeyCommand{}), routing.Wrap(hs.AdditionalAPIKey))
keysRoute.Post("/", quota("api_key"), routing.Wrap(hs.AddAPIKey))
keysRoute.Post("/additional", quota("api_key"), routing.Wrap(hs.AdditionalAPIKey))
keysRoute.Delete("/:id", routing.Wrap(DeleteAPIKey))
}, reqOrgAdmin)
// Preferences
apiRoute.Group("/preferences", func(prefRoute routing.RouteRegister) {
prefRoute.Post("/set-home-dash", bind(models.SavePreferencesCommand{}), routing.Wrap(SetHomeDashboard))
prefRoute.Post("/set-home-dash", routing.Wrap(SetHomeDashboard))
})
// Data sources
apiRoute.Group("/datasources", func(datasourceRoute routing.RouteRegister) {
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead, ScopeDatasourcesAll)), routing.Wrap(hs.GetDataSources))
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesCreate)), quota("data_source"), bind(models.AddDataSourceCommand{}), routing.Wrap(AddDataSource))
datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesWrite, ScopeDatasourceID)), bind(models.UpdateDataSourceCommand{}), routing.Wrap(hs.UpdateDataSource))
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesCreate)), quota("data_source"), routing.Wrap(AddDataSource))
datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesWrite, ScopeDatasourceID)), routing.Wrap(hs.UpdateDataSource))
datasourceRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesDelete, ScopeDatasourceID)), routing.Wrap(hs.DeleteDataSourceById))
datasourceRoute.Delete("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesDelete, ScopeDatasourceUID)), routing.Wrap(hs.DeleteDataSourceByUID))
datasourceRoute.Delete("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesDelete, ScopeDatasourceName)), routing.Wrap(hs.DeleteDataSourceByName))
@ -291,13 +287,13 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Get("/plugins/errors", routing.Wrap(hs.GetPluginErrorsList))
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Post("/:pluginId/install", bind(dtos.InstallPluginCommand{}), routing.Wrap(hs.InstallPlugin))
pluginRoute.Post("/:pluginId/install", routing.Wrap(hs.InstallPlugin))
pluginRoute.Post("/:pluginId/uninstall", routing.Wrap(hs.UninstallPlugin))
}, reqGrafanaAdmin)
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Get("/:pluginId/dashboards/", routing.Wrap(hs.GetPluginDashboards))
pluginRoute.Post("/:pluginId/settings", bind(models.UpdatePluginSettingCmd{}), routing.Wrap(hs.UpdatePluginSetting))
pluginRoute.Post("/:pluginId/settings", routing.Wrap(hs.UpdatePluginSetting))
pluginRoute.Get("/:pluginId/metrics", routing.Wrap(hs.CollectPluginMetrics))
}, reqOrgAdmin)
@ -312,16 +308,16 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
folderRoute.Get("/", routing.Wrap(hs.GetFolders))
folderRoute.Get("/id/:id", routing.Wrap(hs.GetFolderByID))
folderRoute.Post("/", bind(models.CreateFolderCommand{}), routing.Wrap(hs.CreateFolder))
folderRoute.Post("/", routing.Wrap(hs.CreateFolder))
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
folderUidRoute.Get("/", routing.Wrap(hs.GetFolderByUID))
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), routing.Wrap(hs.UpdateFolder))
folderUidRoute.Put("/", routing.Wrap(hs.UpdateFolder))
folderUidRoute.Delete("/", routing.Wrap(hs.DeleteFolder))
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
folderPermissionRoute.Get("/", routing.Wrap(hs.GetFolderPermissionList))
folderPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), routing.Wrap(hs.UpdateFolderPermissions))
folderPermissionRoute.Post("/", routing.Wrap(hs.UpdateFolderPermissions))
})
})
})
@ -331,22 +327,22 @@ func (hs *HTTPServer) registerRoutes() {
dashboardRoute.Get("/uid/:uid", routing.Wrap(hs.GetDashboard))
dashboardRoute.Delete("/uid/:uid", routing.Wrap(hs.DeleteDashboardByUID))
dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), routing.Wrap(CalculateDashboardDiff))
dashboardRoute.Post("/trim", bind(models.TrimDashboardCommand{}), routing.Wrap(hs.TrimDashboard))
dashboardRoute.Post("/calculate-diff", routing.Wrap(CalculateDashboardDiff))
dashboardRoute.Post("/trim", routing.Wrap(hs.TrimDashboard))
dashboardRoute.Post("/db", bind(models.SaveDashboardCommand{}), routing.Wrap(hs.PostDashboard))
dashboardRoute.Post("/db", routing.Wrap(hs.PostDashboard))
dashboardRoute.Get("/home", routing.Wrap(hs.GetHomeDashboard))
dashboardRoute.Get("/tags", GetDashboardTags)
dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), routing.Wrap(hs.ImportDashboard))
dashboardRoute.Post("/import", routing.Wrap(hs.ImportDashboard))
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) {
dashIdRoute.Get("/versions", routing.Wrap(GetDashboardVersions))
dashIdRoute.Get("/versions/:id", routing.Wrap(GetDashboardVersion))
dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), routing.Wrap(hs.RestoreDashboardVersion))
dashIdRoute.Post("/restore", routing.Wrap(hs.RestoreDashboardVersion))
dashIdRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {
dashboardPermissionRoute.Get("/", routing.Wrap(hs.GetDashboardPermissionList))
dashboardPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), routing.Wrap(hs.UpdateDashboardPermissions))
dashboardPermissionRoute.Post("/", routing.Wrap(hs.UpdateDashboardPermissions))
})
})
})
@ -363,8 +359,8 @@ func (hs *HTTPServer) registerRoutes() {
playlistRoute.Get("/:id/items", ValidateOrgPlaylist, routing.Wrap(GetPlaylistItems))
playlistRoute.Get("/:id/dashboards", ValidateOrgPlaylist, routing.Wrap(GetPlaylistDashboards))
playlistRoute.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, routing.Wrap(DeletePlaylist))
playlistRoute.Put("/:id", reqEditorRole, bind(models.UpdatePlaylistCommand{}), ValidateOrgPlaylist, routing.Wrap(UpdatePlaylist))
playlistRoute.Post("/", reqEditorRole, bind(models.CreatePlaylistCommand{}), routing.Wrap(CreatePlaylist))
playlistRoute.Put("/:id", reqEditorRole, ValidateOrgPlaylist, routing.Wrap(UpdatePlaylist))
playlistRoute.Post("/", reqEditorRole, routing.Wrap(CreatePlaylist))
})
// Search
@ -372,14 +368,14 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Get("/search/", routing.Wrap(Search))
// metrics
apiRoute.Post("/tsdb/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), bind(dtos.MetricRequest{}), routing.Wrap(hs.QueryMetrics))
apiRoute.Post("/tsdb/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetrics))
// DataSource w/ expressions
apiRoute.Post("/ds/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), bind(dtos.MetricRequest{}), routing.Wrap(hs.QueryMetricsV2))
apiRoute.Post("/ds/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetricsV2))
apiRoute.Group("/alerts", func(alertsRoute routing.RouteRegister) {
alertsRoute.Post("/test", bind(dtos.AlertTestCommand{}), routing.Wrap(hs.AlertTest))
alertsRoute.Post("/:alertId/pause", reqEditorRole, bind(dtos.PauseAlertCommand{}), routing.Wrap(PauseAlert))
alertsRoute.Post("/test", routing.Wrap(hs.AlertTest))
alertsRoute.Post("/:alertId/pause", reqEditorRole, routing.Wrap(PauseAlert))
alertsRoute.Get("/:alertId", ValidateOrgAlert, routing.Wrap(GetAlert))
alertsRoute.Get("/", routing.Wrap(GetAlerts))
alertsRoute.Get("/states-for-dashboard", routing.Wrap(GetAlertStatesForDashboard))
@ -391,13 +387,13 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Group("/alert-notifications", func(alertNotifications routing.RouteRegister) {
alertNotifications.Get("/", routing.Wrap(GetAlertNotifications))
alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), routing.Wrap(NotificationTest))
alertNotifications.Post("/", bind(models.CreateAlertNotificationCommand{}), routing.Wrap(CreateAlertNotification))
alertNotifications.Put("/:notificationId", bind(models.UpdateAlertNotificationCommand{}), routing.Wrap(hs.UpdateAlertNotification))
alertNotifications.Post("/test", routing.Wrap(NotificationTest))
alertNotifications.Post("/", routing.Wrap(CreateAlertNotification))
alertNotifications.Put("/:notificationId", routing.Wrap(hs.UpdateAlertNotification))
alertNotifications.Get("/:notificationId", routing.Wrap(GetAlertNotificationByID))
alertNotifications.Delete("/:notificationId", routing.Wrap(DeleteAlertNotification))
alertNotifications.Get("/uid/:uid", routing.Wrap(GetAlertNotificationByUID))
alertNotifications.Put("/uid/:uid", bind(models.UpdateAlertNotificationWithUidCommand{}), routing.Wrap(hs.UpdateAlertNotificationByUID))
alertNotifications.Put("/uid/:uid", routing.Wrap(hs.UpdateAlertNotificationByUID))
alertNotifications.Delete("/uid/:uid", routing.Wrap(DeleteAlertNotificationByUID))
}, reqEditorRole)
@ -407,22 +403,22 @@ func (hs *HTTPServer) registerRoutes() {
})
apiRoute.Get("/annotations", routing.Wrap(GetAnnotations))
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), routing.Wrap(DeleteAnnotations))
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, routing.Wrap(DeleteAnnotations))
apiRoute.Group("/annotations", func(annotationsRoute routing.RouteRegister) {
annotationsRoute.Post("/", bind(dtos.PostAnnotationsCmd{}), routing.Wrap(PostAnnotation))
annotationsRoute.Post("/", routing.Wrap(PostAnnotation))
annotationsRoute.Delete("/:annotationId", routing.Wrap(DeleteAnnotationByID))
annotationsRoute.Put("/:annotationId", bind(dtos.UpdateAnnotationsCmd{}), routing.Wrap(UpdateAnnotation))
annotationsRoute.Patch("/:annotationId", bind(dtos.PatchAnnotationsCmd{}), routing.Wrap(PatchAnnotation))
annotationsRoute.Post("/graphite", reqEditorRole, bind(dtos.PostGraphiteAnnotationsCmd{}), routing.Wrap(PostGraphiteAnnotation))
annotationsRoute.Put("/:annotationId", routing.Wrap(UpdateAnnotation))
annotationsRoute.Patch("/:annotationId", routing.Wrap(PatchAnnotation))
annotationsRoute.Post("/graphite", reqEditorRole, routing.Wrap(PostGraphiteAnnotation))
annotationsRoute.Get("/tags", routing.Wrap(GetAnnotationTags))
})
apiRoute.Post("/frontend-metrics", bind(metrics.PostFrontendMetricsCommand{}), routing.Wrap(hs.PostFrontendMetrics))
apiRoute.Post("/frontend-metrics", routing.Wrap(hs.PostFrontendMetrics))
apiRoute.Group("/live", func(liveRoute routing.RouteRegister) {
// the channel path is in the name
liveRoute.Post("/publish", bind(dtos.LivePublishCmd{}), routing.Wrap(hs.Live.HandleHTTPPublish))
liveRoute.Post("/publish", routing.Wrap(hs.Live.HandleHTTPPublish))
// POST influx line protocol.
liveRoute.Post("/push/:streamId", hs.LivePushGateway.Handle)
@ -450,14 +446,14 @@ func (hs *HTTPServer) registerRoutes() {
})
// short urls
apiRoute.Post("/short-urls", bind(dtos.CreateShortURLCmd{}), routing.Wrap(hs.createShortURL))
apiRoute.Post("/short-urls", routing.Wrap(hs.createShortURL))
}, reqSignedIn)
// admin api
r.Group("/api/admin", func(adminRoute routing.RouteRegister) {
adminRoute.Get("/settings", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionSettingsRead)), routing.Wrap(hs.AdminGetSettings))
adminRoute.Get("/stats", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionServerStatsRead)), routing.Wrap(AdminGetStats))
adminRoute.Post("/pause-all-alerts", reqGrafanaAdmin, bind(dtos.PauseAllAlertsCommand{}), routing.Wrap(PauseAllAlerts))
adminRoute.Post("/pause-all-alerts", reqGrafanaAdmin, routing.Wrap(PauseAllAlerts))
adminRoute.Post("/provisioning/dashboards/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDashboards)), routing.Wrap(hs.AdminProvisioningReloadDashboards))
adminRoute.Post("/provisioning/plugins/reload", authorize(reqGrafanaAdmin, ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersPlugins)), routing.Wrap(hs.AdminProvisioningReloadPlugins))
@ -474,18 +470,18 @@ func (hs *HTTPServer) registerRoutes() {
r.Group("/api/admin/users", func(adminUserRoute routing.RouteRegister) {
userIDScope := ac.Scope("global", "users", "id", ac.Parameter(":id"))
adminUserRoute.Post("/", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersCreate)), bind(dtos.AdminCreateUserForm{}), routing.Wrap(hs.AdminCreateUser))
adminUserRoute.Put("/:id/password", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersPasswordUpdate, userIDScope)), bind(dtos.AdminUpdateUserPasswordForm{}), routing.Wrap(AdminUpdateUserPassword))
adminUserRoute.Put("/:id/permissions", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersPermissionsUpdate, userIDScope)), bind(dtos.AdminUpdateUserPermissionsForm{}), routing.Wrap(hs.AdminUpdateUserPermissions))
adminUserRoute.Post("/", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersCreate)), routing.Wrap(hs.AdminCreateUser))
adminUserRoute.Put("/:id/password", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersPasswordUpdate, userIDScope)), routing.Wrap(AdminUpdateUserPassword))
adminUserRoute.Put("/:id/permissions", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersPermissionsUpdate, userIDScope)), routing.Wrap(hs.AdminUpdateUserPermissions))
adminUserRoute.Delete("/:id", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersDelete, userIDScope)), routing.Wrap(AdminDeleteUser))
adminUserRoute.Post("/:id/disable", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersDisable, userIDScope)), routing.Wrap(hs.AdminDisableUser))
adminUserRoute.Post("/:id/enable", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersEnable, userIDScope)), routing.Wrap(AdminEnableUser))
adminUserRoute.Get("/:id/quotas", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersQuotasList, userIDScope)), routing.Wrap(GetUserQuotas))
adminUserRoute.Put("/:id/quotas/:target", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersQuotasUpdate, userIDScope)), bind(models.UpdateUserQuotaCmd{}), routing.Wrap(UpdateUserQuota))
adminUserRoute.Put("/:id/quotas/:target", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersQuotasUpdate, userIDScope)), routing.Wrap(UpdateUserQuota))
adminUserRoute.Post("/:id/logout", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersLogout, userIDScope)), routing.Wrap(hs.AdminLogoutUser))
adminUserRoute.Get("/:id/auth-tokens", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersAuthTokenList, userIDScope)), routing.Wrap(hs.AdminGetUserAuthTokens))
adminUserRoute.Post("/:id/revoke-auth-token", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersAuthTokenUpdate, userIDScope)), bind(models.RevokeAuthTokenCmd{}), routing.Wrap(hs.AdminRevokeUserAuthToken))
adminUserRoute.Post("/:id/revoke-auth-token", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionUsersAuthTokenUpdate, userIDScope)), routing.Wrap(hs.AdminRevokeUserAuthToken))
})
// rendering
@ -499,7 +495,7 @@ func (hs *HTTPServer) registerRoutes() {
r.Get("/avatar/:hash", avatarCacheServer.Handler)
// Snapshots
r.Post("/api/snapshots/", reqSnapshotPublicModeOrSignedIn, bind(models.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot)
r.Post("/api/snapshots/", reqSnapshotPublicModeOrSignedIn, CreateDashboardSnapshot)
r.Get("/api/snapshot/shared-options/", reqSignedIn, GetSharingOptions)
r.Get("/api/snapshots/:key", routing.Wrap(GetDashboardSnapshot))
r.Get("/api/snapshots-delete/:deleteKey", reqSnapshotPublicModeOrSignedIn, routing.Wrap(DeleteDashboardSnapshotByDeleteKey))
@ -508,5 +504,5 @@ func (hs *HTTPServer) registerRoutes() {
// Frontend logs
sourceMapStore := frontendlogging.NewSourceMapStore(hs.Cfg, hs.pluginStaticRouteResolver, frontendlogging.ReadSourceMapFromFS)
r.Post("/log", middleware.RateLimit(hs.Cfg.Sentry.EndpointRPS, hs.Cfg.Sentry.EndpointBurst, time.Now),
bind(frontendlogging.FrontendSentryEvent{}), routing.Wrap(NewFrontendLogMessageHandler(sourceMapStore)))
routing.Wrap(NewFrontendLogMessageHandler(sourceMapStore)))
}

@ -2,6 +2,7 @@ package api
import (
"errors"
"net/http"
"time"
"github.com/grafana/grafana/pkg/api/dtos"
@ -9,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/web"
)
// GetAPIKeys returns a list of API keys
@ -58,7 +60,11 @@ func DeleteAPIKey(c *models.ReqContext) response.Response {
}
// AddAPIKey adds an API key
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyCommand) response.Response {
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext) response.Response {
cmd := models.AddApiKeyCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !cmd.Role.IsValid() {
return response.Error(400, "Invalid role specified", nil)
}
@ -129,7 +135,11 @@ func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyComman
}
// AddAPIKey adds an additional API key to a service account
func (hs *HTTPServer) AdditionalAPIKey(c *models.ReqContext, cmd models.AddApiKeyCommand) response.Response {
func (hs *HTTPServer) AdditionalAPIKey(c *models.ReqContext) response.Response {
cmd := models.AddApiKeyCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !hs.Cfg.FeatureToggles["service-accounts"] {
return response.Error(500, "Requires services-accounts feature", errors.New("feature missing"))
}
@ -137,5 +147,5 @@ func (hs *HTTPServer) AdditionalAPIKey(c *models.ReqContext, cmd models.AddApiKe
return response.Error(500, "Can't create service account while adding additional API key", nil)
}
return hs.AddAPIKey(c, cmd)
return hs.AddAPIKey(c)
}

@ -1,7 +1,9 @@
package api
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
@ -52,10 +54,6 @@ func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url
return sc.handlerFunc(sc.context)
}
if sc.handlerFuncCtx != nil {
return sc.handlerFuncCtx(context.Background(), sc.context)
}
return nil
})
@ -81,10 +79,6 @@ func anonymousUserScenario(t *testing.T, desc string, method string, url string,
return sc.handlerFunc(sc.context)
}
if sc.handlerFuncCtx != nil {
return sc.handlerFuncCtx(context.Background(), sc.context)
}
return nil
})
@ -154,7 +148,6 @@ type scenarioContext struct {
context *models.ReqContext
resp *httptest.ResponseRecorder
handlerFunc handlerFunc
handlerFuncCtx handlerFuncCtx
defaultHandler web.Handler
req *http.Request
url string
@ -167,7 +160,6 @@ func (sc *scenarioContext) exec() {
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *models.ReqContext) response.Response
type handlerFuncCtx func(ctx context.Context, c *models.ReqContext) response.Response
func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHandler {
t.Helper()
@ -376,3 +368,8 @@ func callAPI(server *web.Mux, method, path string, body io.Reader, t *testing.T)
server.ServeHTTP(recorder, req)
return recorder
}
func mockRequestBody(v interface{}) io.ReadCloser {
b, _ := json.Marshal(v)
return io.NopCloser(bytes.NewReader(b))
}

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"strconv"
@ -49,7 +50,11 @@ func dashboardGuardianResponse(err error) response.Response {
return response.Error(403, "Access denied to this dashboard", nil)
}
func (hs *HTTPServer) TrimDashboard(c *models.ReqContext, cmd models.TrimDashboardCommand) response.Response {
func (hs *HTTPServer) TrimDashboard(c *models.ReqContext) response.Response {
cmd := models.TrimDashboardCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
var err error
dash := cmd.Dashboard
meta := cmd.Meta
@ -277,7 +282,15 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
})
}
func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboardCommand) response.Response {
func (hs *HTTPServer) PostDashboard(c *models.ReqContext) response.Response {
cmd := models.SaveDashboardCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.postDashboard(c, cmd)
}
func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboardCommand) response.Response {
ctx := c.Req.Context()
var err error
cmd.OrgId = c.OrgId
@ -585,7 +598,11 @@ func GetDashboardVersion(c *models.ReqContext) response.Response {
}
// POST /api/dashboards/calculate-diff performs diffs on two dashboards
func CalculateDashboardDiff(c *models.ReqContext, apiOptions dtos.CalculateDiffOptions) response.Response {
func CalculateDashboardDiff(c *models.ReqContext) response.Response {
apiOptions := dtos.CalculateDiffOptions{}
if err := web.Bind(c.Req, &apiOptions); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
guardianBase := guardian.New(c.Req.Context(), apiOptions.Base.DashboardId, c.OrgId, c.SignedInUser)
if canSave, err := guardianBase.CanSave(); err != nil || !canSave {
return dashboardGuardianResponse(err)
@ -629,7 +646,11 @@ func CalculateDashboardDiff(c *models.ReqContext, apiOptions dtos.CalculateDiffO
}
// RestoreDashboardVersion restores a dashboard to the given version.
func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) response.Response {
func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext) response.Response {
apiCmd := dtos.RestoreDashboardVersionCommand{}
if err := web.Bind(c.Req, &apiCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
dash, rsp := getDashboardHelper(c.Req.Context(), c.OrgId, c.ParamsInt64(":dashboardId"), "")
if rsp != nil {
return rsp
@ -657,7 +678,7 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext, apiCmd dtos.
saveCmd.Message = fmt.Sprintf("Restored from version %d", version.Version)
saveCmd.FolderId = dash.FolderId
return hs.PostDashboard(c, saveCmd)
return hs.postDashboard(c, saveCmd)
}
func GetDashboardTags(c *models.ReqContext) {

@ -2,12 +2,14 @@ package api
import (
"errors"
"net/http"
"time"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/web"
)
func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) response.Response {
@ -50,7 +52,11 @@ func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) response.
return response.JSON(200, filteredAcls)
}
func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) response.Response {
func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext) response.Response {
apiCmd := dtos.UpdateDashboardAclCommand{}
if err := web.Bind(c.Req, &apiCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if err := validatePermissionsUpdate(apiCmd); err != nil {
return response.Error(400, err.Error(), err)
}

@ -433,11 +433,12 @@ func updateDashboardPermissionScenario(t *testing.T, ctx updatePermissionContext
sc := setupScenarioContext(t, ctx.url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(ctx.cmd)
sc.context = c
sc.context.OrgId = testOrgID
sc.context.UserId = testUserID
return hs.UpdateDashboardPermissions(c, ctx.cmd)
return hs.UpdateDashboardPermissions(c)
})
sc.m.Post(ctx.routePattern, sc.defaultHandler)

@ -1122,10 +1122,11 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId}
return hs.PostDashboard(c, cmd)
return hs.PostDashboard(c)
})
origNewDashboardService := dashboards.NewService
@ -1154,6 +1155,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{
OrgId: testOrgID,
@ -1161,7 +1163,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
}
sc.context.OrgRole = role
return CalculateDashboardDiff(c, cmd)
return CalculateDashboardDiff(c)
})
sc.m.Post(routePattern, sc.defaultHandler)
@ -1188,6 +1190,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{
OrgId: testOrgID,
@ -1195,7 +1198,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
}
sc.context.OrgRole = models.ROLE_ADMIN
return hs.RestoreDashboardVersion(c, cmd)
return hs.RestoreDashboardVersion(c)
})
origProvisioningService := dashboards.NewProvisioningService

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
"github.com/grafana/grafana-plugin-sdk-go/backend"
@ -211,7 +212,11 @@ func validateURL(tp string, u string) response.Response {
return nil
}
func AddDataSource(c *models.ReqContext, cmd models.AddDataSourceCommand) response.Response {
func AddDataSource(c *models.ReqContext) response.Response {
cmd := models.AddDataSourceCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
datasourcesLogger.Debug("Received command to add data source", "url", cmd.Url)
cmd.OrgId = c.OrgId
if resp := validateURL(cmd.Type, cmd.Url); resp != nil {
@ -235,7 +240,11 @@ func AddDataSource(c *models.ReqContext, cmd models.AddDataSourceCommand) respon
})
}
func (hs *HTTPServer) UpdateDataSource(c *models.ReqContext, cmd models.UpdateDataSourceCommand) response.Response {
func (hs *HTTPServer) UpdateDataSource(c *models.ReqContext) response.Response {
cmd := models.UpdateDataSourceCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
datasourcesLogger.Debug("Received command to update data source", "url", cmd.Url)
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":id")

@ -80,10 +80,13 @@ func TestAddDataSource_InvalidURL(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources")
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: "Test",
Url: "invalid:url",
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
Name: "Test",
Url: "invalid:url",
Access: "direct",
Type: "test",
})
return AddDataSource(c)
}))
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
@ -110,10 +113,13 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources")
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: name,
Url: url,
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
Name: name,
Url: url,
Access: "direct",
Type: "test",
})
return AddDataSource(c)
}))
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
@ -128,10 +134,13 @@ func TestUpdateDataSource_InvalidURL(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources/1234")
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: "Test",
Url: "invalid:url",
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
Name: "Test",
Url: "invalid:url",
Access: "direct",
Type: "test",
})
return AddDataSource(c)
}))
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
@ -158,10 +167,13 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources/1234")
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: name,
Url: url,
c.Req.Body = mockRequestBody(models.AddDataSourceCommand{
Name: name,
Url: url,
Access: "direct",
Type: "test",
})
return AddDataSource(c)
}))
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/http"
"github.com/grafana/grafana/pkg/api/apierrors"
"github.com/grafana/grafana/pkg/api/dtos"
@ -59,7 +60,11 @@ func (hs *HTTPServer) GetFolderByID(c *models.ReqContext) response.Response {
return response.JSON(200, toFolderDto(c.Req.Context(), g, folder))
}
func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolderCommand) response.Response {
func (hs *HTTPServer) CreateFolder(c *models.ReqContext) response.Response {
cmd := models.CreateFolderCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
folder, err := s.CreateFolder(c.Req.Context(), cmd.Title, cmd.Uid)
if err != nil {
@ -77,7 +82,11 @@ func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolder
return response.JSON(200, toFolderDto(c.Req.Context(), g, folder))
}
func (hs *HTTPServer) UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response.Response {
func (hs *HTTPServer) UpdateFolder(c *models.ReqContext) response.Response {
cmd := models.UpdateFolderCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser, hs.SQLStore)
err := s.UpdateFolder(c.Req.Context(), web.Params(c.Req)[":uid"], &cmd)
if err != nil {

@ -2,6 +2,7 @@ package api
import (
"errors"
"net/http"
"time"
"github.com/grafana/grafana/pkg/api/apierrors"
@ -58,7 +59,11 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Res
return response.JSON(200, filteredAcls)
}
func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) response.Response {
func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Response {
apiCmd := dtos.UpdateDashboardAclCommand{}
if err := web.Bind(c.Req, &apiCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if err := validatePermissionsUpdate(apiCmd); err != nil {
return response.Error(400, err.Error(), err)
}

@ -402,11 +402,12 @@ func updateFolderPermissionScenario(t *testing.T, ctx updatePermissionContext, h
sc := setupScenarioContext(t, ctx.url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(ctx.cmd)
sc.context = c
sc.context.OrgId = testOrgID
sc.context.UserId = testUserID
return hs.UpdateFolderPermissions(c, ctx.cmd)
return hs.UpdateFolderPermissions(c)
})
sc.m.Post(ctx.routePattern, sc.defaultHandler)

@ -148,10 +148,11 @@ func createFolderScenario(t *testing.T, desc string, url string, routePattern st
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
return hs.CreateFolder(c, cmd)
return hs.CreateFolder(c)
})
origNewFolderService := dashboards.NewFolderService
@ -182,10 +183,11 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
return hs.UpdateFolder(c, cmd)
return hs.UpdateFolder(c)
})
origNewFolderService := dashboards.NewFolderService

@ -1,19 +1,27 @@
package api
import (
"net/http"
"github.com/getsentry/sentry-go"
"github.com/grafana/grafana/pkg/api/frontendlogging"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/web"
)
var frontendLogger = log.New("frontend")
type frontendLogMessageHandler func(c *models.ReqContext, event frontendlogging.FrontendSentryEvent) response.Response
type frontendLogMessageHandler func(c *models.ReqContext) response.Response
func NewFrontendLogMessageHandler(store *frontendlogging.SourceMapStore) frontendLogMessageHandler {
return func(c *models.ReqContext, event frontendlogging.FrontendSentryEvent) response.Response {
return func(c *models.ReqContext) response.Response {
event := frontendlogging.FrontendSentryEvent{}
if err := web.Bind(c.Req, &event); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
var msg = "unknown"
if len(event.Message) > 0 {

@ -3,7 +3,6 @@ package api
import (
"errors"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
@ -84,9 +83,10 @@ func logSentryEventScenario(t *testing.T, desc string, event frontendlogging.Fro
loggingHandler := NewFrontendLogMessageHandler(sourceMapStore)
handler := routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) response.Response {
handler := routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
return loggingHandler(c, event)
c.Req.Body = mockRequestBody(event)
return loggingHandler(c)
})
sc.m.Post(sc.url, handler)

@ -1,14 +1,20 @@
package api
import (
"net/http"
"strings"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/web"
)
func (hs *HTTPServer) PostFrontendMetrics(c *models.ReqContext, cmd metrics.PostFrontendMetricsCommand) response.Response {
func (hs *HTTPServer) PostFrontendMetrics(c *models.ReqContext) response.Response {
cmd := metrics.PostFrontendMetricsCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
for _, event := range cmd.Events {
name := strings.Replace(event.Name, "-", "_", -1)
if recorder, ok := metrics.FrontendMetrics[name]; ok {

@ -1,6 +1,7 @@
package frontendlogging
import (
"encoding/json"
"fmt"
"strings"
@ -93,3 +94,16 @@ func (event *FrontendSentryEvent) ToLogContext(store *SourceMapStore) log15.Ctx
return ctx
}
func (event *FrontendSentryEvent) MarshalJSON() ([]byte, error) {
eventJSON, err := json.Marshal(event.Event)
if err != nil {
return nil, err
}
exceptionJSON, err := json.Marshal(map[string]interface{}{"exception": event.Exception})
if err != nil {
return nil, err
}
exceptionJSON[0] = ','
return append(eventJSON[:len(eventJSON)-1], exceptionJSON...), nil
}

@ -100,7 +100,7 @@ func (user *LDAPUserDTO) FetchOrgs(ctx context.Context) error {
}
// ReloadLDAPCfg reloads the LDAP configuration
func (hs *HTTPServer) ReloadLDAPCfg() response.Response {
func (hs *HTTPServer) ReloadLDAPCfg(c *models.ReqContext) response.Response {
if !ldap.IsEnabled() {
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}

@ -11,6 +11,7 @@ import (
"net/http"
"net/url"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
@ -67,7 +68,7 @@ func genPKCECode() (string, string, error) {
return string(ascii), pkce, nil
}
func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) response.Response {
loginInfo := models.LoginInfo{
AuthModule: "oauth",
}
@ -79,7 +80,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
HttpStatus: http.StatusNotFound,
PublicMessage: "OAuth not enabled",
})
return
return nil
}
connect, err := hs.SocialService.GetConnector(name)
@ -88,7 +89,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
HttpStatus: http.StatusNotFound,
PublicMessage: fmt.Sprintf("No OAuth with name %s configured", name),
})
return
return nil
}
errorParam := ctx.Query("error")
@ -96,7 +97,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
errorDesc := ctx.Query("error_description")
oauthLogger.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
return
return nil
}
code := ctx.Query("code")
@ -128,7 +129,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
HttpStatus: http.StatusInternalServerError,
PublicMessage: "An internal error occurred",
})
return
return nil
}
hashedState := hashStatecode(state, provider.ClientSecret)
@ -138,7 +139,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
}
ctx.Redirect(connect.AuthCodeURL(state, opts...))
return
return nil
}
cookieState := ctx.GetCookie(OauthStateCookieName)
@ -151,7 +152,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
HttpStatus: http.StatusInternalServerError,
PublicMessage: "login.OAuthLogin(missing saved state)",
})
return
return nil
}
queryState := hashStatecode(ctx.Query("state"), provider.ClientSecret)
@ -161,7 +162,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
HttpStatus: http.StatusInternalServerError,
PublicMessage: "login.OAuthLogin(state mismatch)",
})
return
return nil
}
oauthClient, err := hs.SocialService.GetOAuthHttpClient(name)
@ -171,7 +172,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
HttpStatus: http.StatusInternalServerError,
PublicMessage: "login.OAuthLogin(" + err.Error() + ")",
})
return
return nil
}
oauthCtx := context.WithValue(context.Background(), oauth2.HTTPClient, oauthClient)
@ -193,7 +194,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
PublicMessage: "login.OAuthLogin(NewTransportWithCode)",
Err: err,
})
return
return nil
}
// token.TokenType was defaulting to "bearer", which is out of spec, so we explicitly set to "Bearer"
token.TokenType = "Bearer"
@ -216,7 +217,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
Err: err,
})
}
return
return nil
}
oauthLogger.Debug("OAuthLogin got user info", "userInfo", userInfo)
@ -224,26 +225,26 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
// validate that we got at least an email address
if userInfo.Email == "" {
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrNoEmail)
return
return nil
}
// validate that the email is allowed to login to grafana
if !connect.IsEmailAllowed(userInfo.Email) {
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrEmailNotAllowed)
return
return nil
}
loginInfo.ExternalUser = *buildExternalUserInfo(token, userInfo, name)
loginInfo.User, err = syncUser(ctx, &loginInfo.ExternalUser, connect)
if err != nil {
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
return
return nil
}
// login
if err := hs.loginUserWithUser(loginInfo.User, ctx); err != nil {
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
return
return nil
}
loginInfo.HTTPStatus = http.StatusOK
@ -254,12 +255,13 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
if err := hs.ValidateRedirectTo(redirectTo); err == nil {
cookies.DeleteCookie(ctx.Resp, "redirect_to", hs.CookieOptionsFromCfg)
ctx.Redirect(redirectTo)
return
return nil
}
ctx.Logger.Debug("Ignored invalid redirect_to cookie value", "redirect_to", redirectTo)
}
ctx.Redirect(setting.AppSubUrl + "/")
return nil
}
// buildExternalUserInfo returns a ExternalUserInfo struct from OAuth user profile

@ -118,8 +118,9 @@ func TestLoginErrorCookieAPIEndpoint(t *testing.T) {
SecretsService: secretsService,
}
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
hs.LoginView(c)
return response.Empty(http.StatusOK)
})
cfg.LoginCookieName = "grafana_session"
@ -166,12 +167,13 @@ func TestLoginViewRedirect(t *testing.T) {
}
hs.Cfg.CookieSecure = true
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.IsSignedIn = true
c.SignedInUser = &models.SignedInUser{
UserId: 10,
}
hs.LoginView(c)
return response.Empty(http.StatusOK)
})
redirectCases := []redirectCase{
@ -340,7 +342,7 @@ func TestLoginPostRedirect(t *testing.T) {
}
hs.Cfg.CookieSecure = true
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) response.Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Header.Set("Content-Type", "application/json")
c.Req.Body = io.NopCloser(bytes.NewBufferString(`{"user":"admin","password":"admin"}`))
return hs.LoginPost(c)
@ -504,8 +506,9 @@ func TestLoginOAuthRedirect(t *testing.T) {
SocialService: mock,
}
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
hs.LoginView(c)
return response.Empty(http.StatusOK)
})
setting.OAuthAutoLogin = true
@ -529,9 +532,10 @@ func TestLoginInternal(t *testing.T) {
log: log.New("test"),
}
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.URL.RawQuery = "disableAutoLogin=true"
hs.LoginView(c)
return response.Empty(http.StatusOK)
})
setting.OAuthAutoLogin = true
@ -580,12 +584,13 @@ func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioConte
SocialService: &mockSocialService{},
}
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.IsSignedIn = true
c.SignedInUser = &models.SignedInUser{
UserId: 10,
}
hs.LoginView(c)
return response.Empty(http.StatusOK)
})
sc.cfg.AuthProxyEnabled = true
@ -616,7 +621,7 @@ func TestLoginPostRunLokingHook(t *testing.T) {
HooksService: hookService,
}
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) response.Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Header.Set("Content-Type", "application/json")
c.Req.Body = io.NopCloser(bytes.NewBufferString(`{"user":"admin","password":"admin"}`))
x := hs.LoginPost(c)

@ -16,11 +16,16 @@ import (
"github.com/grafana/grafana/pkg/plugins/adapters"
"github.com/grafana/grafana/pkg/tsdb/grafanads"
"github.com/grafana/grafana/pkg/tsdb/legacydata"
"github.com/grafana/grafana/pkg/web"
)
// QueryMetricsV2 returns query metrics.
// POST /api/ds/query DataSource query w/ expressions
func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricRequest) response.Response {
func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext) response.Response {
reqDTO := dtos.MetricRequest{}
if err := web.Bind(c.Req, &reqDTO); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if len(reqDTO.Queries) == 0 {
return response.Error(http.StatusBadRequest, "No queries found in query", nil)
}
@ -162,7 +167,11 @@ func (hs *HTTPServer) handleGetDataSourceError(err error, datasourceRef interfac
// QueryMetrics returns query metrics
// POST /api/tsdb/query
func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricRequest) response.Response {
func (hs *HTTPServer) QueryMetrics(c *models.ReqContext) response.Response {
reqDto := dtos.MetricRequest{}
if err := web.Bind(c.Req, &reqDto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if len(reqDto.Queries) == 0 {
return response.Error(http.StatusBadRequest, "No queries found in query", nil)
}

@ -3,6 +3,7 @@ package api
import (
"context"
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -78,7 +79,11 @@ func getOrgHelper(ctx context.Context, orgID int64) response.Response {
}
// POST /api/orgs
func (hs *HTTPServer) CreateOrg(c *models.ReqContext, cmd models.CreateOrgCommand) response.Response {
func (hs *HTTPServer) CreateOrg(c *models.ReqContext) response.Response {
cmd := models.CreateOrgCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
acEnabled := hs.Cfg.FeatureToggles["accesscontrol"]
if !acEnabled && !(setting.AllowUserOrgCreate || c.IsGrafanaAdmin) {
return response.Error(403, "Access denied", nil)
@ -101,12 +106,20 @@ func (hs *HTTPServer) CreateOrg(c *models.ReqContext, cmd models.CreateOrgComman
}
// PUT /api/org
func UpdateCurrentOrg(c *models.ReqContext, form dtos.UpdateOrgForm) response.Response {
func UpdateCurrentOrg(c *models.ReqContext) response.Response {
form := dtos.UpdateOrgForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return updateOrgHelper(c.Req.Context(), form, c.OrgId)
}
// PUT /api/orgs/:orgId
func UpdateOrg(c *models.ReqContext, form dtos.UpdateOrgForm) response.Response {
func UpdateOrg(c *models.ReqContext) response.Response {
form := dtos.UpdateOrgForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return updateOrgHelper(c.Req.Context(), form, c.ParamsInt64(":orgId"))
}
@ -123,12 +136,20 @@ func updateOrgHelper(ctx context.Context, form dtos.UpdateOrgForm, orgID int64)
}
// PUT /api/org/address
func UpdateCurrentOrgAddress(c *models.ReqContext, form dtos.UpdateOrgAddressForm) response.Response {
func UpdateCurrentOrgAddress(c *models.ReqContext) response.Response {
form := dtos.UpdateOrgAddressForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return updateOrgAddressHelper(c.Req.Context(), form, c.OrgId)
}
// PUT /api/orgs/:orgId/address
func UpdateOrgAddress(c *models.ReqContext, form dtos.UpdateOrgAddressForm) response.Response {
func UpdateOrgAddress(c *models.ReqContext) response.Response {
form := dtos.UpdateOrgAddressForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return updateOrgAddressHelper(c.Req.Context(), form, c.ParamsInt64(":orgId"))
}

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -30,7 +31,11 @@ func GetPendingOrgInvites(c *models.ReqContext) response.Response {
return response.JSON(200, query.Result)
}
func AddOrgInvite(c *models.ReqContext, inviteDto dtos.AddInviteForm) response.Response {
func AddOrgInvite(c *models.ReqContext) response.Response {
inviteDto := dtos.AddInviteForm{}
if err := web.Bind(c.Req, &inviteDto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !inviteDto.Role.IsValid() {
return response.Error(400, "Invalid role specified", nil)
}
@ -165,7 +170,11 @@ func GetInviteInfoByCode(c *models.ReqContext) response.Response {
})
}
func (hs *HTTPServer) CompleteInvite(c *models.ReqContext, completeInvite dtos.CompleteInviteForm) response.Response {
func (hs *HTTPServer) CompleteInvite(c *models.ReqContext) response.Response {
completeInvite := dtos.CompleteInviteForm{}
if err := web.Bind(c.Req, &completeInvite); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
query := models.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
if err := bus.DispatchCtx(c.Req.Context(), &query); err != nil {

@ -3,21 +3,31 @@ package api
import (
"context"
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
// POST /api/org/users
func (hs *HTTPServer) AddOrgUserToCurrentOrg(c *models.ReqContext, cmd models.AddOrgUserCommand) response.Response {
func (hs *HTTPServer) AddOrgUserToCurrentOrg(c *models.ReqContext) response.Response {
cmd := models.AddOrgUserCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
return hs.addOrgUserHelper(c.Req.Context(), cmd)
}
// POST /api/orgs/:orgId/users
func (hs *HTTPServer) AddOrgUser(c *models.ReqContext, cmd models.AddOrgUserCommand) response.Response {
func (hs *HTTPServer) AddOrgUser(c *models.ReqContext) response.Response {
cmd := models.AddOrgUserCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.ParamsInt64(":orgId")
return hs.addOrgUserHelper(c.Req.Context(), cmd)
}
@ -128,7 +138,8 @@ func (hs *HTTPServer) getOrgUsersHelper(ctx context.Context, query *models.GetOr
// SearchOrgUsersWithPaging is an HTTP handler to search for org users with paging.
// GET /api/org/users/search
func (hs *HTTPServer) SearchOrgUsersWithPaging(ctx context.Context, c *models.ReqContext) response.Response {
func (hs *HTTPServer) SearchOrgUsersWithPaging(c *models.ReqContext) response.Response {
ctx := c.Req.Context()
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
@ -168,14 +179,22 @@ func (hs *HTTPServer) SearchOrgUsersWithPaging(ctx context.Context, c *models.Re
}
// PATCH /api/org/users/:userId
func (hs *HTTPServer) UpdateOrgUserForCurrentOrg(c *models.ReqContext, cmd models.UpdateOrgUserCommand) response.Response {
func (hs *HTTPServer) UpdateOrgUserForCurrentOrg(c *models.ReqContext) response.Response {
cmd := models.UpdateOrgUserCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
cmd.UserId = c.ParamsInt64(":userId")
return hs.updateOrgUserHelper(c.Req.Context(), cmd)
}
// PATCH /api/orgs/:orgId/users/:userId
func (hs *HTTPServer) UpdateOrgUser(c *models.ReqContext, cmd models.UpdateOrgUserCommand) response.Response {
func (hs *HTTPServer) UpdateOrgUser(c *models.ReqContext) response.Response {
cmd := models.UpdateOrgUserCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.ParamsInt64(":orgId")
cmd.UserId = c.ParamsInt64(":userId")
return hs.updateOrgUserHelper(c.Req.Context(), cmd)

@ -57,7 +57,7 @@ func TestOrgUsersAPIEndpoint_userLoggedIn(t *testing.T) {
loggedInUserScenario(t, "When calling GET on", "api/org/users/search", func(sc *scenarioContext) {
setUpGetOrgUsersDB(t, sqlStore)
sc.handlerFuncCtx = hs.SearchOrgUsersWithPaging
sc.handlerFunc = hs.SearchOrgUsersWithPaging
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
require.Equal(t, http.StatusOK, sc.resp.Code)
@ -75,7 +75,7 @@ func TestOrgUsersAPIEndpoint_userLoggedIn(t *testing.T) {
loggedInUserScenario(t, "When calling GET with page and limit query parameters on", "api/org/users/search", func(sc *scenarioContext) {
setUpGetOrgUsersDB(t, sqlStore)
sc.handlerFuncCtx = hs.SearchOrgUsersWithPaging
sc.handlerFunc = hs.SearchOrgUsersWithPaging
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "2", "page": "2"}).exec()
require.Equal(t, http.StatusOK, sc.resp.Code)

@ -2,6 +2,7 @@ package api
import (
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -9,9 +10,14 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
func SendResetPasswordEmail(c *models.ReqContext, form dtos.SendResetPasswordEmailForm) response.Response {
func SendResetPasswordEmail(c *models.ReqContext) response.Response {
form := dtos.SendResetPasswordEmailForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if setting.LDAPEnabled || setting.AuthProxyEnabled {
return response.Error(401, "Not allowed to reset password when LDAP or Auth Proxy is enabled", nil)
}
@ -34,7 +40,11 @@ func SendResetPasswordEmail(c *models.ReqContext, form dtos.SendResetPasswordEma
return response.Success("Email sent")
}
func ResetPassword(c *models.ReqContext, form dtos.ResetUserPasswordForm) response.Response {
func ResetPassword(c *models.ReqContext) response.Response {
form := dtos.ResetUserPasswordForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
query := models.ValidateResetPasswordCodeQuery{Code: form.Code}
if err := bus.Dispatch(&query); err != nil {

@ -2,10 +2,12 @@ package api
import (
"context"
"net/http"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/web"
)
func ValidateOrgPlaylist(c *models.ReqContext) {
@ -138,7 +140,11 @@ func DeletePlaylist(c *models.ReqContext) response.Response {
return response.JSON(200, "")
}
func CreatePlaylist(c *models.ReqContext, cmd models.CreatePlaylistCommand) response.Response {
func CreatePlaylist(c *models.ReqContext) response.Response {
cmd := models.CreatePlaylistCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
if err := bus.DispatchCtx(c.Req.Context(), &cmd); err != nil {
@ -148,7 +154,11 @@ func CreatePlaylist(c *models.ReqContext, cmd models.CreatePlaylistCommand) resp
return response.JSON(200, cmd.Result)
}
func UpdatePlaylist(c *models.ReqContext, cmd models.UpdatePlaylistCommand) response.Response {
func UpdatePlaylist(c *models.ReqContext) response.Response {
cmd := models.UpdatePlaylistCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":id")

@ -149,7 +149,11 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon
return response.JSON(200, dto)
}
func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext, cmd models.UpdatePluginSettingCmd) response.Response {
func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext) response.Response {
cmd := models.UpdatePluginSettingCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
pluginID := web.Params(c.Req)[":pluginId"]
if _, exists := hs.pluginStore.Plugin(c.Req.Context(), pluginID); !exists {
@ -208,7 +212,11 @@ func (hs *HTTPServer) GetPluginMarkdown(c *models.ReqContext) response.Response
return resp
}
func (hs *HTTPServer) ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDashboardCommand) response.Response {
func (hs *HTTPServer) ImportDashboard(c *models.ReqContext) response.Response {
apiCmd := dtos.ImportDashboardCommand{}
if err := web.Bind(c.Req, &apiCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
var err error
if apiCmd.PluginId == "" && apiCmd.Dashboard == nil {
return response.Error(422, "Dashboard must be set", nil)
@ -387,7 +395,11 @@ func (hs *HTTPServer) GetPluginErrorsList(_ *models.ReqContext) response.Respons
return response.JSON(200, hs.pluginErrorResolver.PluginErrors())
}
func (hs *HTTPServer) InstallPlugin(c *models.ReqContext, dto dtos.InstallPluginCommand) response.Response {
func (hs *HTTPServer) InstallPlugin(c *models.ReqContext) response.Response {
dto := dtos.InstallPluginCommand{}
if err := web.Bind(c.Req, &dto); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
pluginID := web.Params(c.Req)[":pluginId"]
err := hs.pluginStore.Add(c.Req.Context(), pluginID, dto.Version, plugins.AddOpts{})

@ -2,11 +2,13 @@ package api
import (
"context"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/web"
)
const (
@ -16,7 +18,11 @@ const (
)
// POST /api/preferences/set-home-dash
func SetHomeDashboard(c *models.ReqContext, cmd models.SavePreferencesCommand) response.Response {
func SetHomeDashboard(c *models.ReqContext) response.Response {
cmd := models.SavePreferencesCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.UserId = c.UserId
cmd.OrgId = c.OrgId
@ -50,7 +56,11 @@ func (hs *HTTPServer) getPreferencesFor(ctx context.Context, orgID, userID, team
}
// PUT /api/user/preferences
func (hs *HTTPServer) UpdateUserPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) response.Response {
func (hs *HTTPServer) UpdateUserPreferences(c *models.ReqContext) response.Response {
dtoCmd := dtos.UpdatePrefsCmd{}
if err := web.Bind(c.Req, &dtoCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.updatePreferencesFor(c.Req.Context(), c.OrgId, c.UserId, 0, &dtoCmd)
}
@ -81,6 +91,10 @@ func (hs *HTTPServer) GetOrgPreferences(c *models.ReqContext) response.Response
}
// PUT /api/org/preferences
func (hs *HTTPServer) UpdateOrgPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) response.Response {
func (hs *HTTPServer) UpdateOrgPreferences(c *models.ReqContext) response.Response {
dtoCmd := dtos.UpdatePrefsCmd{}
if err := web.Bind(c.Req, &dtoCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.updatePreferencesFor(c.Req.Context(), c.OrgId, 0, 0, &dtoCmd)
}

@ -1,6 +1,8 @@
package api
import (
"net/http"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
@ -29,7 +31,11 @@ func (hs *HTTPServer) getOrgQuotasHelper(c *models.ReqContext, orgID int64) resp
return response.JSON(200, query.Result)
}
func (hs *HTTPServer) UpdateOrgQuota(c *models.ReqContext, cmd models.UpdateOrgQuotaCmd) response.Response {
func (hs *HTTPServer) UpdateOrgQuota(c *models.ReqContext) response.Response {
cmd := models.UpdateOrgQuotaCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !hs.Cfg.Quota.Enabled {
return response.Error(404, "Quotas not enabled", nil)
}
@ -59,7 +65,11 @@ func GetUserQuotas(c *models.ReqContext) response.Response {
return response.JSON(200, query.Result)
}
func UpdateUserQuota(c *models.ReqContext, cmd models.UpdateUserQuotaCmd) response.Response {
func UpdateUserQuota(c *models.ReqContext) response.Response {
cmd := models.UpdateUserQuotaCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !setting.Quota.Enabled {
return response.Error(404, "Quotas not enabled", nil)
}

@ -12,16 +12,10 @@ var (
}
)
func Wrap(action interface{}) web.Handler {
func Wrap(handler func(c *models.ReqContext) response.Response) web.Handler {
return func(c *models.ReqContext) {
var res response.Response
val, err := c.Invoke(action)
if err == nil && val != nil && len(val) > 0 {
res = val[0].Interface().(response.Response)
} else {
res = ServerError(err)
if res := handler(c); res != nil {
res.WriteTo(c)
}
res.WriteTo(c)
}
}

@ -3,6 +3,7 @@ package api
import (
"errors"
"fmt"
"net/http"
"path"
"strings"
@ -15,7 +16,11 @@ import (
)
// createShortURL handles requests to create short URLs.
func (hs *HTTPServer) createShortURL(c *models.ReqContext, cmd dtos.CreateShortURLCmd) response.Response {
func (hs *HTTPServer) createShortURL(c *models.ReqContext) response.Response {
cmd := dtos.CreateShortURLCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
hs.log.Debug("Received request to create short URL", "path", cmd.Path)
cmd.Path = strings.TrimSpace(cmd.Path)

@ -64,10 +64,11 @@ func createShortURLScenario(t *testing.T, desc string, url string, routePattern
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
return hs.createShortURL(c, cmd)
return hs.createShortURL(c)
})
sc.m.Post(routePattern, sc.defaultHandler)

@ -3,6 +3,7 @@ package api
import (
"context"
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -12,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
// GET /api/user/signup/options
@ -23,7 +25,11 @@ func GetSignUpOptions(c *models.ReqContext) response.Response {
}
// POST /api/user/signup
func SignUp(c *models.ReqContext, form dtos.SignUpForm) response.Response {
func SignUp(c *models.ReqContext) response.Response {
form := dtos.SignUpForm{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !setting.AllowUserSignUp {
return response.Error(401, "User signup is disabled", nil)
}
@ -61,7 +67,11 @@ func SignUp(c *models.ReqContext, form dtos.SignUpForm) response.Response {
return response.JSON(200, util.DynMap{"status": "SignUpCreated"})
}
func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2Form) response.Response {
func (hs *HTTPServer) SignUpStep2(c *models.ReqContext) response.Response {
form := dtos.SignUpStep2Form{}
if err := web.Bind(c.Req, &form); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if !setting.AllowUserSignUp {
return response.Error(401, "User signup is disabled", nil)
}

@ -2,6 +2,7 @@ package api
import (
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -10,10 +11,15 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/teamguardian"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
// POST /api/teams
func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamCommand) response.Response {
func (hs *HTTPServer) CreateTeam(c *models.ReqContext) response.Response {
cmd := models.CreateTeamCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if c.OrgRole == models.ROLE_VIEWER {
return response.Error(403, "Not allowed to create team.", nil)
}
@ -47,7 +53,11 @@ func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamComm
}
// PUT /api/teams/:teamId
func (hs *HTTPServer) UpdateTeam(c *models.ReqContext, cmd models.UpdateTeamCommand) response.Response {
func (hs *HTTPServer) UpdateTeam(c *models.ReqContext) response.Response {
cmd := models.UpdateTeamCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":teamId")
@ -159,7 +169,11 @@ func (hs *HTTPServer) GetTeamPreferences(c *models.ReqContext) response.Response
}
// PUT /api/teams/:teamId/preferences
func (hs *HTTPServer) UpdateTeamPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) response.Response {
func (hs *HTTPServer) UpdateTeamPreferences(c *models.ReqContext) response.Response {
dtoCmd := dtos.UpdatePrefsCmd{}
if err := web.Bind(c.Req, &dtoCmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
teamId := c.ParamsInt64(":teamId")
orgId := c.OrgId

@ -2,6 +2,7 @@ package api
import (
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -10,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/teamguardian"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
// GET /api/teams/:teamId/members
@ -41,7 +43,11 @@ func (hs *HTTPServer) GetTeamMembers(c *models.ReqContext) response.Response {
}
// POST /api/teams/:teamId/members
func (hs *HTTPServer) AddTeamMember(c *models.ReqContext, cmd models.AddTeamMemberCommand) response.Response {
func (hs *HTTPServer) AddTeamMember(c *models.ReqContext) response.Response {
cmd := models.AddTeamMemberCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.OrgId = c.OrgId
cmd.TeamId = c.ParamsInt64(":teamId")
@ -68,7 +74,11 @@ func (hs *HTTPServer) AddTeamMember(c *models.ReqContext, cmd models.AddTeamMemb
}
// PUT /:teamId/members/:userId
func (hs *HTTPServer) UpdateTeamMember(c *models.ReqContext, cmd models.UpdateTeamMemberCommand) response.Response {
func (hs *HTTPServer) UpdateTeamMember(c *models.ReqContext) response.Response {
cmd := models.UpdateTeamMemberCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
teamId := c.ParamsInt64(":teamId")
orgId := c.OrgId

@ -130,8 +130,8 @@ func TestTeamAPIEndpoint(t *testing.T) {
Logger: stub,
}
c.OrgRole = models.ROLE_EDITOR
cmd := models.CreateTeamCommand{Name: teamName}
hs.CreateTeam(c, cmd)
c.Req.Body = mockRequestBody(models.CreateTeamCommand{Name: teamName})
hs.CreateTeam(c)
assert.Equal(t, createTeamCalled, 1)
assert.Equal(t, addTeamMemberCalled, 0)
assert.True(t, stub.warnCalled)
@ -146,9 +146,9 @@ func TestTeamAPIEndpoint(t *testing.T) {
Logger: stub,
}
c.OrgRole = models.ROLE_EDITOR
cmd := models.CreateTeamCommand{Name: teamName}
c.Req.Body = mockRequestBody(models.CreateTeamCommand{Name: teamName})
createTeamCalled, addTeamMemberCalled = 0, 0
hs.CreateTeam(c, cmd)
hs.CreateTeam(c)
assert.Equal(t, createTeamCalled, 1)
assert.Equal(t, addTeamMemberCalled, 1)
assert.False(t, stub.warnCalled)

@ -3,6 +3,7 @@ package api
import (
"context"
"errors"
"net/http"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
@ -10,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
)
// GET /api/user (current authenticated user)
@ -70,7 +72,11 @@ func GetUserByLoginOrEmail(c *models.ReqContext) response.Response {
}
// POST /api/user
func UpdateSignedInUser(c *models.ReqContext, cmd models.UpdateUserCommand) response.Response {
func UpdateSignedInUser(c *models.ReqContext) response.Response {
cmd := models.UpdateUserCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if setting.AuthProxyEnabled {
if setting.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
return response.Error(400, "Not allowed to change email when auth proxy is using email property", nil)
@ -84,7 +90,11 @@ func UpdateSignedInUser(c *models.ReqContext, cmd models.UpdateUserCommand) resp
}
// POST /api/users/:id
func UpdateUser(c *models.ReqContext, cmd models.UpdateUserCommand) response.Response {
func UpdateUser(c *models.ReqContext) response.Response {
cmd := models.UpdateUserCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
cmd.UserId = c.ParamsInt64(":id")
return handleUpdateUser(c.Req.Context(), cmd)
}
@ -217,7 +227,11 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *models.ReqContext) {
c.Redirect(hs.Cfg.AppSubURL + "/")
}
func ChangeUserPassword(c *models.ReqContext, cmd models.ChangeUserPasswordCommand) response.Response {
func ChangeUserPassword(c *models.ReqContext) response.Response {
cmd := models.ChangeUserPasswordCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
if setting.LDAPEnabled || setting.AuthProxyEnabled {
return response.Error(400, "Not allowed to change password when LDAP or Auth Proxy is enabled", nil)
}

@ -3,6 +3,7 @@ package api
import (
"context"
"errors"
"net/http"
"time"
"github.com/grafana/grafana/pkg/api/dtos"
@ -10,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
"github.com/ua-parser/uap-go/uaparser"
)
@ -19,7 +21,11 @@ func (hs *HTTPServer) GetUserAuthTokens(c *models.ReqContext) response.Response
}
// POST /api/user/revoke-auth-token
func (hs *HTTPServer) RevokeUserAuthToken(c *models.ReqContext, cmd models.RevokeAuthTokenCmd) response.Response {
func (hs *HTTPServer) RevokeUserAuthToken(c *models.ReqContext) response.Response {
cmd := models.RevokeAuthTokenCmd{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
return hs.revokeUserAuthTokenInternal(c, c.UserId, cmd)
}

@ -184,12 +184,13 @@ func revokeUserAuthTokenScenario(t *testing.T, desc string, url string, routePat
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
sc.context = c
sc.context.UserId = userId
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.RevokeUserAuthToken(c, cmd)
return hs.RevokeUserAuthToken(c)
})
sc.m.Post(routePattern, sc.defaultHandler)

@ -2,8 +2,8 @@ package libraryelements
import (
"errors"
"net/http"
"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"
@ -13,18 +13,23 @@ import (
func (l *LibraryElementService) registerAPIEndpoints() {
l.RouteRegister.Group("/api/library-elements", func(entities routing.RouteRegister) {
entities.Post("/", middleware.ReqSignedIn, binding.Bind(CreateLibraryElementCommand{}), routing.Wrap(l.createHandler))
entities.Post("/", middleware.ReqSignedIn, routing.Wrap(l.createHandler))
entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(l.deleteHandler))
entities.Get("/", middleware.ReqSignedIn, routing.Wrap(l.getAllHandler))
entities.Get("/:uid", middleware.ReqSignedIn, routing.Wrap(l.getHandler))
entities.Get("/:uid/connections/", middleware.ReqSignedIn, routing.Wrap(l.getConnectionsHandler))
entities.Get("/name/:name", middleware.ReqSignedIn, routing.Wrap(l.getByNameHandler))
entities.Patch("/:uid", middleware.ReqSignedIn, binding.Bind(patchLibraryElementCommand{}), routing.Wrap(l.patchHandler))
entities.Patch("/:uid", middleware.ReqSignedIn, routing.Wrap(l.patchHandler))
})
}
// createHandler handles POST /api/library-elements.
func (l *LibraryElementService) createHandler(c *models.ReqContext, cmd CreateLibraryElementCommand) response.Response {
func (l *LibraryElementService) createHandler(c *models.ReqContext) response.Response {
cmd := CreateLibraryElementCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
element, err := l.createLibraryElement(c.Req.Context(), c.SignedInUser, cmd)
if err != nil {
return toLibraryElementError(err, "Failed to create library element")
@ -77,7 +82,12 @@ func (l *LibraryElementService) getAllHandler(c *models.ReqContext) response.Res
}
// patchHandler handles PATCH /api/library-elements/:uid
func (l *LibraryElementService) patchHandler(c *models.ReqContext, cmd patchLibraryElementCommand) response.Response {
func (l *LibraryElementService) patchHandler(c *models.ReqContext) response.Response {
cmd := patchLibraryElementCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
element, err := l.patchLibraryElement(c.Req.Context(), c.SignedInUser, cmd, web.Params(c.Req)[":uid"])
if err != nil {
return toLibraryElementError(err, "Failed to update library element")

@ -14,7 +14,8 @@ func TestCreateLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to create a library panel that already exists, it should fail",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -64,7 +65,8 @@ func TestCreateLibraryElement(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Nonexistent UID")
command.UID = util.GenerateShortUID()
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
var expected = libraryElementResult{
Result: libraryElement{
@ -110,7 +112,8 @@ func TestCreateLibraryElement(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Existing UID")
command.UID = sc.initialResult.Result.UID
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -118,7 +121,8 @@ func TestCreateLibraryElement(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Invalid UID")
command.UID = "Testing an invalid UID"
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -126,14 +130,16 @@ func TestCreateLibraryElement(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Invalid UID")
command.UID = "j6T00KRZzj6T00KRZzj6T00KRZzj6T00KRZzj6T00K"
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
testScenario(t, "When an admin tries to create a library panel where name and panel title differ, it should not update panel title",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(1, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
var expected = libraryElementResult{
Result: libraryElement{

@ -37,7 +37,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all panel elements and both panels and variables exist, it should only return panels",
func(t *testing.T, sc scenarioContext) {
command := getCreateVariableCommand(sc.folder.Id, "query0")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -102,7 +103,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all variable elements and both panels and variables exist, it should only return panels",
func(t *testing.T, sc scenarioContext) {
command := getCreateVariableCommand(sc.folder.Id, "query0")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -166,7 +168,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist, it should succeed",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
resp = sc.service.getAllHandler(sc.reqContext)
@ -262,7 +265,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and sort desc is set, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -369,7 +373,8 @@ func TestGetAllLibraryElements(t *testing.T) {
"description": "Gauge description"
}
`))
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
command = getCreateCommandWithModel(sc.folder.Id, "BarGauge - Library Panel", models.PanelElement, []byte(`
@ -381,7 +386,8 @@ func TestGetAllLibraryElements(t *testing.T) {
"description": "BarGauge description"
}
`))
resp = sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp = sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -488,7 +494,8 @@ func TestGetAllLibraryElements(t *testing.T) {
"description": "Gauge description"
}
`))
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -517,7 +524,8 @@ func TestGetAllLibraryElements(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
command := getCreatePanelCommand(newFolder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
folderFilter := strconv.FormatInt(newFolder.Id, 10)
@ -583,7 +591,8 @@ func TestGetAllLibraryElements(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
command := getCreatePanelCommand(newFolder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
folderFilter := "2020,2021"
@ -612,7 +621,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilter is set to General folder, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
folderFilter := "0"
@ -712,7 +722,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and excludeUID is set, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -776,7 +787,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -840,7 +852,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 2, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -913,7 +926,8 @@ func TestGetAllLibraryElements(t *testing.T) {
"description": "Some other d e s c r i p t i o n"
}
`))
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -987,7 +1001,8 @@ func TestGetAllLibraryElements(t *testing.T) {
"description": "A Library Panel"
}
`))
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -1086,7 +1101,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 1 and searchString is panel2, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -1152,7 +1168,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString is panel, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()
@ -1182,7 +1199,8 @@ func TestGetAllLibraryElements(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and perPage is 1 and page is 3 and searchString does not exist, it should succeed and the result should be correct",
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel2")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
err := sc.reqContext.Req.ParseForm()

@ -14,9 +14,10 @@ import (
func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to patch a library panel that does not exist, it should fail",
func(t *testing.T, sc scenarioContext) {
cmd := patchLibraryElementCommand{Kind: int64(models.PanelElement)}
cmd := patchLibraryElementCommand{Kind: int64(models.PanelElement), Version: 1}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": "unknown"})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 404, resp.Status())
})
@ -39,7 +40,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result = validateAndUnMarshalResponse(t, resp)
var expected = libraryElementResult{
@ -91,7 +93,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.FolderID = newFolder.Id
@ -113,7 +116,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Name = "New Name"
sc.initialResult.Result.Meta.CreatedBy.Name = userInDbName
@ -135,7 +139,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.UID = cmd.UID
sc.initialResult.Result.Meta.CreatedBy.Name = userInDbName
@ -157,7 +162,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -170,7 +176,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -178,7 +185,8 @@ func TestPatchLibraryElement(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Existing UID")
command.UID = util.GenerateShortUID()
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
cmd := patchLibraryElementCommand{
FolderID: -1,
@ -187,7 +195,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -200,7 +209,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Type = "graph"
sc.initialResult.Result.Description = "New description"
@ -228,7 +238,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Type = "text"
sc.initialResult.Result.Description = "New description"
@ -254,7 +265,8 @@ func TestPatchLibraryElement(t *testing.T) {
Version: 1,
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Type = "graph"
sc.initialResult.Result.Description = "A description"
@ -276,7 +288,8 @@ func TestPatchLibraryElement(t *testing.T) {
cmd := patchLibraryElementCommand{FolderID: -1, Version: 1, Kind: int64(models.PanelElement)}
sc.reqContext.UserId = 2
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Meta.UpdatedBy.ID = int64(2)
sc.initialResult.Result.Meta.CreatedBy.Name = userInDbName
@ -291,7 +304,8 @@ func TestPatchLibraryElement(t *testing.T) {
scenarioWithPanel(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 := getCreatePanelCommand(sc.folder.Id, "Another Panel")
resp := sc.service.createHandler(sc.reqContext, command)
sc.ctx.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
cmd := patchLibraryElementCommand{
Name: "Text - Library Panel",
@ -299,7 +313,8 @@ func TestPatchLibraryElement(t *testing.T) {
Kind: int64(models.PanelElement),
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -307,7 +322,8 @@ func TestPatchLibraryElement(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
newFolder := createFolderWithACL(t, sc.sqlStore, "NewFolder", sc.user, []folderACLItem{})
command := getCreatePanelCommand(newFolder.Id, "Text - Library Panel")
resp := sc.service.createHandler(sc.reqContext, command)
sc.ctx.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
var result = validateAndUnMarshalResponse(t, resp)
cmd := patchLibraryElementCommand{
FolderID: 1,
@ -315,7 +331,8 @@ func TestPatchLibraryElement(t *testing.T) {
Kind: int64(models.PanelElement),
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, 400, resp.Status())
})
@ -328,7 +345,8 @@ func TestPatchLibraryElement(t *testing.T) {
}
sc.reqContext.OrgId = 2
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 404, resp.Status())
})
@ -340,9 +358,11 @@ func TestPatchLibraryElement(t *testing.T) {
Kind: int64(models.PanelElement),
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, 412, resp.Status())
})
@ -354,7 +374,8 @@ func TestPatchLibraryElement(t *testing.T) {
Kind: int64(models.VariableElement),
}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
resp := sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp := sc.service.patchHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result = validateAndUnMarshalResponse(t, resp)
sc.initialResult.Result.Type = "text"

@ -71,7 +71,8 @@ func TestLibraryElementPermissions(t *testing.T) {
sc.reqContext.SignedInUser.OrgRole = testCase.role
command := getCreatePanelCommand(folder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, testCase.status, resp.Status())
})
@ -79,14 +80,16 @@ func TestLibraryElementPermissions(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
fromFolder := createFolderWithACL(t, sc.sqlStore, "Everyone", sc.user, everyonePermissions)
command := getCreatePanelCommand(fromFolder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(models.PanelElement)}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, testCase.status, resp.Status())
})
@ -94,14 +97,16 @@ func TestLibraryElementPermissions(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
fromFolder := createFolderWithACL(t, sc.sqlStore, "Everyone", sc.user, testCase.items)
command := getCreatePanelCommand(fromFolder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
toFolder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: toFolder.Id, Version: 1, Kind: int64(models.PanelElement)}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, testCase.status, resp.Status())
})
@ -109,7 +114,8 @@ func TestLibraryElementPermissions(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, testCase.items)
cmd := getCreatePanelCommand(folder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role
@ -134,7 +140,8 @@ func TestLibraryElementPermissions(t *testing.T) {
sc.reqContext.SignedInUser.OrgRole = testCase.role
command := getCreatePanelCommand(0, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, testCase.status, resp.Status())
})
@ -142,13 +149,15 @@ func TestLibraryElementPermissions(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
command := getCreatePanelCommand(folder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: 0, Version: 1, Kind: int64(models.PanelElement)}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, testCase.status, resp.Status())
})
@ -156,20 +165,23 @@ func TestLibraryElementPermissions(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
command := getCreatePanelCommand(0, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: folder.Id, Version: 1, Kind: int64(models.PanelElement)}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.ctx.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
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 := getCreatePanelCommand(0, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role
@ -193,7 +205,8 @@ func TestLibraryElementPermissions(t *testing.T) {
sc.reqContext.SignedInUser.OrgRole = testCase.role
command := getCreatePanelCommand(-100, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 404, resp.Status())
})
@ -201,13 +214,15 @@ func TestLibraryElementPermissions(t *testing.T) {
func(t *testing.T, sc scenarioContext) {
folder := createFolderWithACL(t, sc.sqlStore, "Folder", sc.user, everyonePermissions)
command := getCreatePanelCommand(folder.Id, "Library Panel Name")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
sc.reqContext.SignedInUser.OrgRole = testCase.role
cmd := patchLibraryElementCommand{FolderID: -100, Version: 1, Kind: int64(models.PanelElement)}
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
resp = sc.service.patchHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp = sc.service.patchHandler(sc.reqContext)
require.Equal(t, 404, resp.Status())
})
}
@ -228,7 +243,8 @@ func TestLibraryElementPermissions(t *testing.T) {
for i, folderCase := range folderCases {
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
cmd := getCreatePanelCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
resp := sc.service.createHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
result.Result.Meta.CreatedBy.Name = userInDbName
result.Result.Meta.CreatedBy.AvatarURL = userInDbAvatar
@ -250,7 +266,8 @@ func TestLibraryElementPermissions(t *testing.T) {
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 := getCreatePanelCommand(0, "Library Panel in General Folder")
resp := sc.service.createHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
result.Result.Meta.CreatedBy.Name = userInDbName
result.Result.Meta.CreatedBy.AvatarURL = userInDbAvatar
@ -289,7 +306,8 @@ func TestLibraryElementPermissions(t *testing.T) {
for i, folderCase := range folderCases {
folder := createFolderWithACL(t, sc.sqlStore, fmt.Sprintf("Folder%v", i), sc.user, folderCase)
cmd := getCreatePanelCommand(folder.Id, fmt.Sprintf("Library Panel in Folder%v", i))
resp := sc.service.createHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
result.Result.Meta.CreatedBy.Name = userInDbName
result.Result.Meta.CreatedBy.AvatarURL = userInDbAvatar
@ -340,7 +358,8 @@ func TestLibraryElementPermissions(t *testing.T) {
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 := getCreatePanelCommand(0, "Library Panel in General Folder")
resp := sc.service.createHandler(sc.reqContext, cmd)
sc.reqContext.Req.Body = mockRequestBody(cmd)
resp := sc.service.createHandler(sc.reqContext)
result := validateAndUnMarshalResponse(t, resp)
result.Result.Meta.CreatedBy.Name = userInDbName
result.Result.Meta.CreatedBy.AvatarURL = userInDbAvatar

@ -1,8 +1,10 @@
package libraryelements
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
@ -68,7 +70,8 @@ func TestDeleteLibraryPanelsInFolder(t *testing.T) {
scenarioWithPanel(t, "When an admin tries to delete a folder that contains disconnected elements, it should delete all disconnected elements too",
func(t *testing.T, sc scenarioContext) {
command := getCreateVariableCommand(sc.folder.Id, "query0")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
resp = sc.service.getAllHandler(sc.reqContext)
@ -266,7 +269,8 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
command := getCreatePanelCommand(sc.folder.Id, "Text - Library Panel")
resp := sc.service.createHandler(sc.reqContext, command)
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
sc.initialResult = validateAndUnMarshalResponse(t, resp)
fn(t, sc)
@ -333,3 +337,8 @@ func getCompareOptions() []cmp.Option {
}),
}
}
func mockRequestBody(v interface{}) io.ReadCloser {
b, _ := json.Marshal(v)
return io.NopCloser(bytes.NewReader(b))
}

@ -170,7 +170,7 @@ type CreateLibraryElementCommand struct {
type patchLibraryElementCommand struct {
FolderID int64 `json:"folderId" binding:"Default(-1)"`
Name string `json:"name"`
Model json.RawMessage `json:"model"`
Model json.RawMessage `json:"model,omitempty"`
Kind int64 `json:"kind" binding:"Required"`
Version int64 `json:"version" binding:"Required"`
UID string `json:"uid"`

@ -893,7 +893,11 @@ func (g *GrafanaLive) ClientCount(orgID int64, channel string) (int, error) {
return len(p.Presence), nil
}
func (g *GrafanaLive) HandleHTTPPublish(ctx *models.ReqContext, cmd dtos.LivePublishCmd) response.Response {
func (g *GrafanaLive) HandleHTTPPublish(ctx *models.ReqContext) response.Response {
cmd := dtos.LivePublishCmd{}
if err := web.Bind(ctx.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
addr, err := live.ParseChannel(cmd.Channel)
if err != nil {
return response.Error(http.StatusBadRequest, "invalid channel ID", nil)

Loading…
Cancel
Save