Chore: Moves common and response into separate packages (#30298)

* Chore: moves common and response into separate packages

* Chore: moves common and response into separate packages

* Update pkg/api/utils/common.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Chore: changes after PR comments

* Chore: move wrap to routing package

* Chore: move functions in common to response package

* Chore: move functions in common to response package

* Chore: formats imports

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Hugo Häggmark
2021-01-15 14:43:20 +01:00
committed by GitHub
parent a94c256516
commit 3d41267fc4
51 changed files with 1321 additions and 1248 deletions

View File

@@ -4,12 +4,13 @@ import (
"regexp"
"strings"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
func AdminGetSettings(c *models.ReqContext) Response {
func AdminGetSettings(c *models.ReqContext) response.Response {
settings := make(map[string]interface{})
for _, section := range setting.Raw.Sections() {
@@ -35,15 +36,15 @@ func AdminGetSettings(c *models.ReqContext) Response {
}
}
return JSON(200, settings)
return response.JSON(200, settings)
}
func AdminGetStats(c *models.ReqContext) Response {
func AdminGetStats(c *models.ReqContext) response.Response {
statsQuery := models.GetAdminStatsQuery{}
if err := bus.Dispatch(&statsQuery); err != nil {
return Error(500, "Failed to get admin stats from database", err)
return response.Error(500, "Failed to get admin stats from database", err)
}
return JSON(200, statsQuery.Result)
return response.JSON(200, statsQuery.Result)
}

View File

@@ -4,37 +4,38 @@ import (
"context"
"errors"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
)
func (hs *HTTPServer) AdminProvisioningReloadDashboards(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminProvisioningReloadDashboards(c *models.ReqContext) response.Response {
err := hs.ProvisioningService.ProvisionDashboards()
if err != nil && !errors.Is(err, context.Canceled) {
return Error(500, "", err)
return response.Error(500, "", err)
}
return Success("Dashboards config reloaded")
return response.Success("Dashboards config reloaded")
}
func (hs *HTTPServer) AdminProvisioningReloadDatasources(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminProvisioningReloadDatasources(c *models.ReqContext) response.Response {
err := hs.ProvisioningService.ProvisionDatasources()
if err != nil {
return Error(500, "", err)
return response.Error(500, "", err)
}
return Success("Datasources config reloaded")
return response.Success("Datasources config reloaded")
}
func (hs *HTTPServer) AdminProvisioningReloadPlugins(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminProvisioningReloadPlugins(c *models.ReqContext) response.Response {
err := hs.ProvisioningService.ProvisionPlugins()
if err != nil {
return Error(500, "Failed to reload plugins config", err)
return response.Error(500, "Failed to reload plugins config", err)
}
return Success("Plugins config reloaded")
return response.Success("Plugins config reloaded")
}
func (hs *HTTPServer) AdminProvisioningReloadNotifications(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminProvisioningReloadNotifications(c *models.ReqContext) response.Response {
err := hs.ProvisioningService.ProvisionNotifications()
if err != nil {
return Error(500, "", err)
return response.Error(500, "", err)
}
return Success("Notifications config reloaded")
return response.Success("Notifications config reloaded")
}

View File

@@ -5,13 +5,14 @@ import (
"fmt"
"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/infra/metrics"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
func AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) Response {
func AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) response.Response {
cmd := models.CreateUserCommand{
Login: form.Login,
Email: form.Email,
@@ -23,24 +24,24 @@ func AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) Respon
if len(cmd.Login) == 0 {
cmd.Login = cmd.Email
if len(cmd.Login) == 0 {
return Error(400, "Validation error, need specify either username or email", nil)
return response.Error(400, "Validation error, need specify either username or email", nil)
}
}
if len(cmd.Password) < 4 {
return Error(400, "Password is missing or too short", nil)
return response.Error(400, "Password is missing or too short", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrOrgNotFound) {
return Error(400, err.Error(), nil)
return response.Error(400, err.Error(), nil)
}
if errors.Is(err, models.ErrUserAlreadyExists) {
return Error(412, fmt.Sprintf("User with email '%s' or username '%s' already exists", form.Email, form.Login), err)
return response.Error(412, fmt.Sprintf("User with email '%s' or username '%s' already exists", form.Email, form.Login), err)
}
return Error(500, "failed to create user", err)
return response.Error(500, "failed to create user", err)
}
metrics.MApiAdminUserCreate.Inc()
@@ -52,25 +53,25 @@ func AdminCreateUser(c *models.ReqContext, form dtos.AdminCreateUserForm) Respon
Id: user.Id,
}
return JSON(200, result)
return response.JSON(200, result)
}
func AdminUpdateUserPassword(c *models.ReqContext, form dtos.AdminUpdateUserPasswordForm) Response {
func AdminUpdateUserPassword(c *models.ReqContext, form dtos.AdminUpdateUserPasswordForm) response.Response {
userID := c.ParamsInt64(":id")
if len(form.Password) < 4 {
return Error(400, "New password too short", nil)
return response.Error(400, "New password too short", nil)
}
userQuery := models.GetUserByIdQuery{Id: userID}
if err := bus.Dispatch(&userQuery); err != nil {
return Error(500, "Could not read user from database", err)
return response.Error(500, "Could not read user from database", err)
}
passwordHashed, err := util.EncodePassword(form.Password, userQuery.Result.Salt)
if err != nil {
return Error(500, "Could not encode password", err)
return response.Error(500, "Could not encode password", err)
}
cmd := models.ChangeUserPasswordCommand{
@@ -79,14 +80,14 @@ func AdminUpdateUserPassword(c *models.ReqContext, form dtos.AdminUpdateUserPass
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update user password", err)
return response.Error(500, "Failed to update user password", err)
}
return Success("User password updated")
return response.Success("User password updated")
}
// PUT /api/admin/users/:id/permissions
func AdminUpdateUserPermissions(c *models.ReqContext, form dtos.AdminUpdateUserPermissionsForm) Response {
func AdminUpdateUserPermissions(c *models.ReqContext, form dtos.AdminUpdateUserPermissionsForm) response.Response {
userID := c.ParamsInt64(":id")
cmd := models.UpdateUserPermissionsCommand{
@@ -96,96 +97,96 @@ func AdminUpdateUserPermissions(c *models.ReqContext, form dtos.AdminUpdateUserP
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrLastGrafanaAdmin) {
return Error(400, models.ErrLastGrafanaAdmin.Error(), nil)
return response.Error(400, models.ErrLastGrafanaAdmin.Error(), nil)
}
return Error(500, "Failed to update user permissions", err)
return response.Error(500, "Failed to update user permissions", err)
}
return Success("User permissions updated")
return response.Success("User permissions updated")
}
func AdminDeleteUser(c *models.ReqContext) Response {
func AdminDeleteUser(c *models.ReqContext) response.Response {
userID := c.ParamsInt64(":id")
cmd := models.DeleteUserCommand{UserId: userID}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to delete user", err)
return response.Error(500, "Failed to delete user", err)
}
return Success("User deleted")
return response.Success("User deleted")
}
// POST /api/admin/users/:id/disable
func (hs *HTTPServer) AdminDisableUser(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminDisableUser(c *models.ReqContext) response.Response {
userID := c.ParamsInt64(":id")
// External users shouldn't be disabled from API
authInfoQuery := &models.GetAuthInfoQuery{UserId: userID}
if err := bus.Dispatch(authInfoQuery); !errors.Is(err, models.ErrUserNotFound) {
return Error(500, "Could not disable external user", nil)
return response.Error(500, "Could not disable external user", nil)
}
disableCmd := models.DisableUserCommand{UserId: userID, IsDisabled: true}
if err := bus.Dispatch(&disableCmd); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to disable user", err)
return response.Error(500, "Failed to disable user", err)
}
err := hs.AuthTokenService.RevokeAllUserTokens(c.Req.Context(), userID)
if err != nil {
return Error(500, "Failed to disable user", err)
return response.Error(500, "Failed to disable user", err)
}
return Success("User disabled")
return response.Success("User disabled")
}
// POST /api/admin/users/:id/enable
func AdminEnableUser(c *models.ReqContext) Response {
func AdminEnableUser(c *models.ReqContext) response.Response {
userID := c.ParamsInt64(":id")
// External users shouldn't be disabled from API
authInfoQuery := &models.GetAuthInfoQuery{UserId: userID}
if err := bus.Dispatch(authInfoQuery); !errors.Is(err, models.ErrUserNotFound) {
return Error(500, "Could not enable external user", nil)
return response.Error(500, "Could not enable external user", nil)
}
disableCmd := models.DisableUserCommand{UserId: userID, IsDisabled: false}
if err := bus.Dispatch(&disableCmd); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to enable user", err)
return response.Error(500, "Failed to enable user", err)
}
return Success("User enabled")
return response.Success("User enabled")
}
// POST /api/admin/users/:id/logout
func (hs *HTTPServer) AdminLogoutUser(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminLogoutUser(c *models.ReqContext) response.Response {
userID := c.ParamsInt64(":id")
if c.UserId == userID {
return Error(400, "You cannot logout yourself", nil)
return response.Error(400, "You cannot logout yourself", nil)
}
return hs.logoutUserFromAllDevicesInternal(c.Req.Context(), userID)
}
// GET /api/admin/users/:id/auth-tokens
func (hs *HTTPServer) AdminGetUserAuthTokens(c *models.ReqContext) Response {
func (hs *HTTPServer) AdminGetUserAuthTokens(c *models.ReqContext) response.Response {
userID := c.ParamsInt64(":id")
return hs.getUserAuthTokensInternal(c, userID)
}
// POST /api/admin/users/:id/revoke-auth-token
func (hs *HTTPServer) AdminRevokeUserAuthToken(c *models.ReqContext, cmd models.RevokeAuthTokenCmd) Response {
func (hs *HTTPServer) AdminRevokeUserAuthToken(c *models.ReqContext, cmd models.RevokeAuthTokenCmd) response.Response {
userID := c.ParamsInt64(":id")
return hs.revokeUserAuthTokenInternal(c, userID, cmd)
}

View File

@@ -5,6 +5,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
@@ -355,7 +357,7 @@ func putAdminScenario(t *testing.T, desc string, url string, routePattern string
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -380,7 +382,7 @@ func adminLogoutUserScenario(t *testing.T, desc string, url string, routePattern
}
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
t.Log("Route handler invoked", "url", c.Req.URL)
sc.context = c
@@ -410,7 +412,7 @@ func adminRevokeUserAuthTokenScenario(t *testing.T, desc string, url string, rou
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -438,7 +440,7 @@ func adminGetUserAuthTokensScenario(t *testing.T, desc string, url string, route
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -465,7 +467,7 @@ func adminDisableUserScenario(t *testing.T, desc string, action string, url stri
}
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
@@ -487,7 +489,7 @@ func adminDeleteUserScenario(t *testing.T, desc string, url string, routePattern
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
@@ -505,7 +507,7 @@ func adminCreateUserScenario(t *testing.T, desc string, url string, routePattern
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID

View File

@@ -6,6 +6,7 @@ import (
"strconv"
"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/services/alerting"
@@ -29,11 +30,11 @@ func ValidateOrgAlert(c *models.ReqContext) {
}
}
func GetAlertStatesForDashboard(c *models.ReqContext) Response {
func GetAlertStatesForDashboard(c *models.ReqContext) response.Response {
dashboardID := c.QueryInt64("dashboardId")
if dashboardID == 0 {
return Error(400, "Missing query parameter dashboardId", nil)
return response.Error(400, "Missing query parameter dashboardId", nil)
}
query := models.GetAlertStatesForDashboardQuery{
@@ -42,14 +43,14 @@ func GetAlertStatesForDashboard(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to fetch alert states", err)
return response.Error(500, "Failed to fetch alert states", err)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
// GET /api/alerts
func GetAlerts(c *models.ReqContext) Response {
func GetAlerts(c *models.ReqContext) response.Response {
dashboardQuery := c.Query("dashboardQuery")
dashboardTags := c.QueryStrings("dashboardTag")
stringDashboardIDs := c.QueryStrings("dashboardId")
@@ -86,7 +87,7 @@ func GetAlerts(c *models.ReqContext) Response {
err := bus.Dispatch(&searchQuery)
if err != nil {
return Error(500, "List alerts failed", err)
return response.Error(500, "List alerts failed", err)
}
for _, d := range searchQuery.Result {
@@ -97,7 +98,7 @@ func GetAlerts(c *models.ReqContext) Response {
// if we didn't find any dashboards, return empty result
if len(dashboardIDs) == 0 {
return JSON(200, []*models.AlertListItemDTO{})
return response.JSON(200, []*models.AlertListItemDTO{})
}
}
@@ -116,20 +117,20 @@ func GetAlerts(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "List alerts failed", err)
return response.Error(500, "List alerts failed", err)
}
for _, alert := range query.Result {
alert.Url = models.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
// POST /api/alerts/test
func AlertTest(c *models.ReqContext, dto dtos.AlertTestCommand) Response {
func AlertTest(c *models.ReqContext, dto dtos.AlertTestCommand) response.Response {
if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
return Error(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
return response.Error(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
}
backendCmd := alerting.AlertTestCommand{
@@ -142,12 +143,12 @@ func AlertTest(c *models.ReqContext, dto dtos.AlertTestCommand) Response {
if err := bus.Dispatch(&backendCmd); err != nil {
var validationErr alerting.ValidationError
if errors.As(err, &validationErr) {
return Error(422, validationErr.Error(), nil)
return response.Error(422, validationErr.Error(), nil)
}
if errors.Is(err, models.ErrDataSourceAccessDenied) {
return Error(403, "Access denied to datasource", err)
return response.Error(403, "Access denied to datasource", err)
}
return Error(500, "Failed to test rule", err)
return response.Error(500, "Failed to test rule", err)
}
res := backendCmd.Result
@@ -170,29 +171,29 @@ func AlertTest(c *models.ReqContext, dto dtos.AlertTestCommand) Response {
dtoRes.TimeMs = fmt.Sprintf("%1.3fms", res.GetDurationMs())
return JSON(200, dtoRes)
return response.JSON(200, dtoRes)
}
// GET /api/alerts/:id
func GetAlert(c *models.ReqContext) Response {
func GetAlert(c *models.ReqContext) response.Response {
id := c.ParamsInt64(":alertId")
query := models.GetAlertByIdQuery{Id: id}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "List alerts failed", err)
return response.Error(500, "List alerts failed", err)
}
return JSON(200, &query.Result)
return response.JSON(200, &query.Result)
}
func GetAlertNotifiers(c *models.ReqContext) Response {
return JSON(200, alerting.GetNotifiers())
func GetAlertNotifiers(c *models.ReqContext) response.Response {
return response.JSON(200, alerting.GetNotifiers())
}
func GetAlertNotificationLookup(c *models.ReqContext) Response {
func GetAlertNotificationLookup(c *models.ReqContext) response.Response {
alertNotifications, err := getAlertNotificationsInternal(c)
if err != nil {
return Error(500, "Failed to get alert notifications", err)
return response.Error(500, "Failed to get alert notifications", err)
}
result := make([]*dtos.AlertNotificationLookup, 0)
@@ -201,13 +202,13 @@ func GetAlertNotificationLookup(c *models.ReqContext) Response {
result = append(result, dtos.NewAlertNotificationLookup(notification))
}
return JSON(200, result)
return response.JSON(200, result)
}
func GetAlertNotifications(c *models.ReqContext) Response {
func GetAlertNotifications(c *models.ReqContext) response.Response {
alertNotifications, err := getAlertNotificationsInternal(c)
if err != nil {
return Error(500, "Failed to get alert notifications", err)
return response.Error(500, "Failed to get alert notifications", err)
}
result := make([]*dtos.AlertNotification, 0)
@@ -216,7 +217,7 @@ func GetAlertNotifications(c *models.ReqContext) Response {
result = append(result, dtos.NewAlertNotification(notification))
}
return JSON(200, result)
return response.JSON(200, result)
}
func getAlertNotificationsInternal(c *models.ReqContext) ([]*models.AlertNotification, error) {
@@ -229,74 +230,74 @@ func getAlertNotificationsInternal(c *models.ReqContext) ([]*models.AlertNotific
return query.Result, nil
}
func GetAlertNotificationByID(c *models.ReqContext) Response {
func GetAlertNotificationByID(c *models.ReqContext) response.Response {
query := &models.GetAlertNotificationsQuery{
OrgId: c.OrgId,
Id: c.ParamsInt64("notificationId"),
}
if query.Id == 0 {
return Error(404, "Alert notification not found", nil)
return response.Error(404, "Alert notification not found", nil)
}
if err := bus.Dispatch(query); err != nil {
return Error(500, "Failed to get alert notifications", err)
return response.Error(500, "Failed to get alert notifications", err)
}
if query.Result == nil {
return Error(404, "Alert notification not found", nil)
return response.Error(404, "Alert notification not found", nil)
}
return JSON(200, dtos.NewAlertNotification(query.Result))
return response.JSON(200, dtos.NewAlertNotification(query.Result))
}
func GetAlertNotificationByUID(c *models.ReqContext) Response {
func GetAlertNotificationByUID(c *models.ReqContext) response.Response {
query := &models.GetAlertNotificationsWithUidQuery{
OrgId: c.OrgId,
Uid: c.Params("uid"),
}
if query.Uid == "" {
return Error(404, "Alert notification not found", nil)
return response.Error(404, "Alert notification not found", nil)
}
if err := bus.Dispatch(query); err != nil {
return Error(500, "Failed to get alert notifications", err)
return response.Error(500, "Failed to get alert notifications", err)
}
if query.Result == nil {
return Error(404, "Alert notification not found", nil)
return response.Error(404, "Alert notification not found", nil)
}
return JSON(200, dtos.NewAlertNotification(query.Result))
return response.JSON(200, dtos.NewAlertNotification(query.Result))
}
func CreateAlertNotification(c *models.ReqContext, cmd models.CreateAlertNotificationCommand) Response {
func CreateAlertNotification(c *models.ReqContext, cmd models.CreateAlertNotificationCommand) response.Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrAlertNotificationWithSameNameExists) || errors.Is(err, models.ErrAlertNotificationWithSameUIDExists) {
return Error(409, "Failed to create alert notification", err)
return response.Error(409, "Failed to create alert notification", err)
}
return Error(500, "Failed to create alert notification", err)
return response.Error(500, "Failed to create alert notification", err)
}
return JSON(200, dtos.NewAlertNotification(cmd.Result))
return response.JSON(200, dtos.NewAlertNotification(cmd.Result))
}
func UpdateAlertNotification(c *models.ReqContext, cmd models.UpdateAlertNotificationCommand) Response {
func UpdateAlertNotification(c *models.ReqContext, cmd models.UpdateAlertNotificationCommand) response.Response {
cmd.OrgId = c.OrgId
err := fillWithSecureSettingsData(&cmd)
if err != nil {
return Error(500, "Failed to update alert notification", err)
return response.Error(500, "Failed to update alert notification", err)
}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrAlertNotificationNotFound) {
return Error(404, err.Error(), err)
return response.Error(404, err.Error(), err)
}
return Error(500, "Failed to update alert notification", err)
return response.Error(500, "Failed to update alert notification", err)
}
query := models.GetAlertNotificationsQuery{
@@ -305,26 +306,26 @@ func UpdateAlertNotification(c *models.ReqContext, cmd models.UpdateAlertNotific
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get alert notification", err)
return response.Error(500, "Failed to get alert notification", err)
}
return JSON(200, dtos.NewAlertNotification(query.Result))
return response.JSON(200, dtos.NewAlertNotification(query.Result))
}
func UpdateAlertNotificationByUID(c *models.ReqContext, cmd models.UpdateAlertNotificationWithUidCommand) Response {
func UpdateAlertNotificationByUID(c *models.ReqContext, cmd models.UpdateAlertNotificationWithUidCommand) response.Response {
cmd.OrgId = c.OrgId
cmd.Uid = c.Params("uid")
err := fillWithSecureSettingsDataByUID(&cmd)
if err != nil {
return Error(500, "Failed to update alert notification", err)
return response.Error(500, "Failed to update alert notification", err)
}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrAlertNotificationNotFound) {
return Error(404, err.Error(), nil)
return response.Error(404, err.Error(), nil)
}
return Error(500, "Failed to update alert notification", err)
return response.Error(500, "Failed to update alert notification", err)
}
query := models.GetAlertNotificationsWithUidQuery{
@@ -333,10 +334,10 @@ func UpdateAlertNotificationByUID(c *models.ReqContext, cmd models.UpdateAlertNo
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get alert notification", err)
return response.Error(500, "Failed to get alert notification", err)
}
return JSON(200, dtos.NewAlertNotification(query.Result))
return response.JSON(200, dtos.NewAlertNotification(query.Result))
}
func fillWithSecureSettingsData(cmd *models.UpdateAlertNotificationCommand) error {
@@ -387,7 +388,7 @@ func fillWithSecureSettingsDataByUID(cmd *models.UpdateAlertNotificationWithUidC
return nil
}
func DeleteAlertNotification(c *models.ReqContext) Response {
func DeleteAlertNotification(c *models.ReqContext) response.Response {
cmd := models.DeleteAlertNotificationCommand{
OrgId: c.OrgId,
Id: c.ParamsInt64("notificationId"),
@@ -395,15 +396,15 @@ func DeleteAlertNotification(c *models.ReqContext) Response {
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrAlertNotificationNotFound) {
return Error(404, err.Error(), nil)
return response.Error(404, err.Error(), nil)
}
return Error(500, "Failed to delete alert notification", err)
return response.Error(500, "Failed to delete alert notification", err)
}
return Success("Notification deleted")
return response.Success("Notification deleted")
}
func DeleteAlertNotificationByUID(c *models.ReqContext) Response {
func DeleteAlertNotificationByUID(c *models.ReqContext) response.Response {
cmd := models.DeleteAlertNotificationWithUidCommand{
OrgId: c.OrgId,
Uid: c.Params("uid"),
@@ -411,19 +412,19 @@ func DeleteAlertNotificationByUID(c *models.ReqContext) Response {
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrAlertNotificationNotFound) {
return Error(404, err.Error(), nil)
return response.Error(404, err.Error(), nil)
}
return Error(500, "Failed to delete alert notification", err)
return response.Error(500, "Failed to delete alert notification", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Notification deleted",
"id": cmd.DeletedAlertNotificationId,
})
}
// POST /api/alert-notifications/test
func NotificationTest(c *models.ReqContext, dto dtos.NotificationTestCommand) Response {
func NotificationTest(c *models.ReqContext, dto dtos.NotificationTestCommand) response.Response {
cmd := &alerting.NotificationTestCommand{
OrgID: c.OrgId,
ID: dto.ID,
@@ -435,43 +436,43 @@ func NotificationTest(c *models.ReqContext, dto dtos.NotificationTestCommand) Re
if err := bus.DispatchCtx(c.Req.Context(), cmd); err != nil {
if errors.Is(err, models.ErrSmtpNotEnabled) {
return Error(412, err.Error(), err)
return response.Error(412, err.Error(), err)
}
return Error(500, "Failed to send alert notifications", err)
return response.Error(500, "Failed to send alert notifications", err)
}
return Success("Test notification sent")
return response.Success("Test notification sent")
}
// POST /api/alerts/:alertId/pause
func PauseAlert(c *models.ReqContext, dto dtos.PauseAlertCommand) Response {
func PauseAlert(c *models.ReqContext, dto dtos.PauseAlertCommand) response.Response {
alertID := c.ParamsInt64("alertId")
result := make(map[string]interface{})
result["alertId"] = alertID
query := models.GetAlertByIdQuery{Id: alertID}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Get Alert failed", err)
return response.Error(500, "Get Alert failed", err)
}
guardian := guardian.New(query.Result.DashboardId, c.OrgId, c.SignedInUser)
if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
if err != nil {
return Error(500, "Error while checking permissions for Alert", err)
return response.Error(500, "Error while checking permissions for Alert", err)
}
return Error(403, "Access denied to this dashboard and alert", nil)
return response.Error(403, "Access denied to this dashboard and alert", nil)
}
// Alert state validation
if query.Result.State != models.AlertStatePaused && !dto.Paused {
result["state"] = "un-paused"
result["message"] = "Alert is already un-paused"
return JSON(200, result)
return response.JSON(200, result)
} else if query.Result.State == models.AlertStatePaused && dto.Paused {
result["state"] = models.AlertStatePaused
result["message"] = "Alert is already paused"
return JSON(200, result)
return response.JSON(200, result)
}
cmd := models.PauseAlertCommand{
@@ -481,43 +482,43 @@ func PauseAlert(c *models.ReqContext, dto dtos.PauseAlertCommand) Response {
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "", err)
return response.Error(500, "", err)
}
var response models.AlertStateType = models.AlertStateUnknown
var resp models.AlertStateType = models.AlertStateUnknown
pausedState := "un-paused"
if cmd.Paused {
response = models.AlertStatePaused
resp = models.AlertStatePaused
pausedState = "paused"
}
result["state"] = response
result["state"] = resp
result["message"] = "Alert " + pausedState
return JSON(200, result)
return response.JSON(200, result)
}
// POST /api/admin/pause-all-alerts
func PauseAllAlerts(c *models.ReqContext, dto dtos.PauseAllAlertsCommand) Response {
func PauseAllAlerts(c *models.ReqContext, dto dtos.PauseAllAlertsCommand) response.Response {
updateCmd := models.PauseAllAlertCommand{
Paused: dto.Paused,
}
if err := bus.Dispatch(&updateCmd); err != nil {
return Error(500, "Failed to pause alerts", err)
return response.Error(500, "Failed to pause alerts", err)
}
var response models.AlertStateType = models.AlertStatePending
var resp models.AlertStateType = models.AlertStatePending
pausedState := "un paused"
if updateCmd.Paused {
response = models.AlertStatePaused
resp = models.AlertStatePaused
pausedState = "paused"
}
result := map[string]interface{}{
"state": response,
"state": resp,
"message": "alerts " + pausedState,
"alertsAffected": updateCmd.ResultCount,
}
return JSON(200, result)
return response.JSON(200, result)
}

View File

@@ -4,7 +4,10 @@ import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/routing"
"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/services/search"
@@ -162,7 +165,7 @@ func postAlertScenario(t *testing.T, desc string, url string, routePattern strin
defer bus.ClearBusHandlers()
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID

View File

@@ -4,13 +4,14 @@ import (
"strings"
"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/annotations"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
)
func GetAnnotations(c *models.ReqContext) Response {
func GetAnnotations(c *models.ReqContext) response.Response {
query := &annotations.ItemQuery{
From: c.QueryInt64("from"),
To: c.QueryInt64("to"),
@@ -29,7 +30,7 @@ func GetAnnotations(c *models.ReqContext) Response {
items, err := repo.Find(query)
if err != nil {
return Error(500, "Failed to get annotations", err)
return response.Error(500, "Failed to get annotations", err)
}
for _, item := range items {
@@ -38,7 +39,7 @@ func GetAnnotations(c *models.ReqContext) Response {
}
}
return JSON(200, items)
return response.JSON(200, items)
}
type CreateAnnotationError struct {
@@ -49,7 +50,7 @@ func (e *CreateAnnotationError) Error() string {
return e.message
}
func PostAnnotation(c *models.ReqContext, cmd dtos.PostAnnotationsCmd) Response {
func PostAnnotation(c *models.ReqContext, cmd dtos.PostAnnotationsCmd) response.Response {
if canSave, err := canSaveByDashboardID(c, cmd.DashboardId); err != nil || !canSave {
return dashboardGuardianResponse(err)
}
@@ -58,7 +59,7 @@ func PostAnnotation(c *models.ReqContext, cmd dtos.PostAnnotationsCmd) Response
if cmd.Text == "" {
err := &CreateAnnotationError{"text field should not be empty"}
return Error(500, "Failed to save annotation", err)
return response.Error(500, "Failed to save annotation", err)
}
item := annotations.Item{
@@ -74,12 +75,12 @@ func PostAnnotation(c *models.ReqContext, cmd dtos.PostAnnotationsCmd) Response
}
if err := repo.Save(&item); err != nil {
return Error(500, "Failed to save annotation", err)
return response.Error(500, "Failed to save annotation", err)
}
startID := item.Id
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Annotation added",
"id": startID,
})
@@ -93,12 +94,12 @@ func formatGraphiteAnnotation(what string, data string) string {
return text
}
func PostGraphiteAnnotation(c *models.ReqContext, cmd dtos.PostGraphiteAnnotationsCmd) Response {
func PostGraphiteAnnotation(c *models.ReqContext, cmd dtos.PostGraphiteAnnotationsCmd) response.Response {
repo := annotations.GetRepository()
if cmd.What == "" {
err := &CreateAnnotationError{"what field should not be empty"}
return Error(500, "Failed to save Graphite annotation", err)
return response.Error(500, "Failed to save Graphite annotation", err)
}
text := formatGraphiteAnnotation(cmd.What, cmd.Data)
@@ -118,12 +119,12 @@ func PostGraphiteAnnotation(c *models.ReqContext, cmd dtos.PostGraphiteAnnotatio
tagsArray = append(tagsArray, tagStr)
} else {
err := &CreateAnnotationError{"tag should be a string"}
return Error(500, "Failed to save Graphite annotation", err)
return response.Error(500, "Failed to save Graphite annotation", err)
}
}
default:
err := &CreateAnnotationError{"unsupported tags format"}
return Error(500, "Failed to save Graphite annotation", err)
return response.Error(500, "Failed to save Graphite annotation", err)
}
item := annotations.Item{
@@ -135,16 +136,16 @@ func PostGraphiteAnnotation(c *models.ReqContext, cmd dtos.PostGraphiteAnnotatio
}
if err := repo.Save(&item); err != nil {
return Error(500, "Failed to save Graphite annotation", err)
return response.Error(500, "Failed to save Graphite annotation", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Graphite annotation added",
"id": item.Id,
})
}
func UpdateAnnotation(c *models.ReqContext, cmd dtos.UpdateAnnotationsCmd) Response {
func UpdateAnnotation(c *models.ReqContext, cmd dtos.UpdateAnnotationsCmd) response.Response {
annotationID := c.ParamsInt64(":annotationId")
repo := annotations.GetRepository()
@@ -164,13 +165,13 @@ func UpdateAnnotation(c *models.ReqContext, cmd dtos.UpdateAnnotationsCmd) Respo
}
if err := repo.Update(&item); err != nil {
return Error(500, "Failed to update annotation", err)
return response.Error(500, "Failed to update annotation", err)
}
return Success("Annotation updated")
return response.Success("Annotation updated")
}
func PatchAnnotation(c *models.ReqContext, cmd dtos.PatchAnnotationsCmd) Response {
func PatchAnnotation(c *models.ReqContext, cmd dtos.PatchAnnotationsCmd) response.Response {
annotationID := c.ParamsInt64(":annotationId")
repo := annotations.GetRepository()
@@ -182,7 +183,7 @@ func PatchAnnotation(c *models.ReqContext, cmd dtos.PatchAnnotationsCmd) Respons
items, err := repo.Find(&annotations.ItemQuery{AnnotationId: annotationID, OrgId: c.OrgId})
if err != nil || len(items) == 0 {
return Error(404, "Could not find annotation to update", err)
return response.Error(404, "Could not find annotation to update", err)
}
existing := annotations.Item{
@@ -212,13 +213,13 @@ func PatchAnnotation(c *models.ReqContext, cmd dtos.PatchAnnotationsCmd) Respons
}
if err := repo.Update(&existing); err != nil {
return Error(500, "Failed to update annotation", err)
return response.Error(500, "Failed to update annotation", err)
}
return Success("Annotation patched")
return response.Success("Annotation patched")
}
func DeleteAnnotations(c *models.ReqContext, cmd dtos.DeleteAnnotationsCmd) Response {
func DeleteAnnotations(c *models.ReqContext, cmd dtos.DeleteAnnotationsCmd) response.Response {
repo := annotations.GetRepository()
err := repo.Delete(&annotations.DeleteParams{
@@ -229,13 +230,13 @@ func DeleteAnnotations(c *models.ReqContext, cmd dtos.DeleteAnnotationsCmd) Resp
})
if err != nil {
return Error(500, "Failed to delete annotations", err)
return response.Error(500, "Failed to delete annotations", err)
}
return Success("Annotations deleted")
return response.Success("Annotations deleted")
}
func DeleteAnnotationByID(c *models.ReqContext) Response {
func DeleteAnnotationByID(c *models.ReqContext) response.Response {
repo := annotations.GetRepository()
annotationID := c.ParamsInt64(":annotationId")
@@ -248,10 +249,10 @@ func DeleteAnnotationByID(c *models.ReqContext) Response {
Id: annotationID,
})
if err != nil {
return Error(500, "Failed to delete annotation", err)
return response.Error(500, "Failed to delete annotation", err)
}
return Success("Annotation deleted")
return response.Success("Annotation deleted")
}
func canSaveByDashboardID(c *models.ReqContext, dashboardID int64) (bool, error) {
@@ -269,10 +270,10 @@ func canSaveByDashboardID(c *models.ReqContext, dashboardID int64) (bool, error)
return true, nil
}
func canSave(c *models.ReqContext, repo annotations.Repository, annotationID int64) Response {
func canSave(c *models.ReqContext, repo annotations.Repository, annotationID int64) response.Response {
items, err := repo.Find(&annotations.ItemQuery{AnnotationId: annotationID, OrgId: c.OrgId})
if err != nil || len(items) == 0 {
return Error(500, "Could not find annotation to update", err)
return response.Error(500, "Could not find annotation to update", err)
}
dashboardID := items[0].DashboardId

View File

@@ -5,6 +5,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/annotations"
@@ -266,7 +268,7 @@ func postAnnotationScenario(t *testing.T, desc string, url string, routePattern
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -290,7 +292,7 @@ func putAnnotationScenario(t *testing.T, desc string, url string, routePattern s
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -313,7 +315,7 @@ func patchAnnotationScenario(t *testing.T, desc string, url string, routePattern
defer bus.ClearBusHandlers()
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -337,7 +339,7 @@ func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePatte
defer bus.ClearBusHandlers()
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID

View File

@@ -33,7 +33,7 @@ func (hs *HTTPServer) registerRoutes() {
// not logged in views
r.Get("/logout", hs.Logout)
r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), Wrap(hs.LoginPost))
r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), routing.Wrap(hs.LoginPost))
r.Get("/login/:name", quota("session"), hs.OAuthLogin)
r.Get("/login", hs.LoginView)
r.Get("/invite/:code", hs.Index)
@@ -100,180 +100,180 @@ func (hs *HTTPServer) registerRoutes() {
// sign up
r.Get("/verify", hs.Index)
r.Get("/signup", hs.Index)
r.Get("/api/user/signup/options", Wrap(GetSignUpOptions))
r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), Wrap(SignUp))
r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), Wrap(hs.SignUpStep2))
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))
// invited
r.Get("/api/user/invite/:code", Wrap(GetInviteInfoByCode))
r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), Wrap(hs.CompleteInvite))
r.Get("/api/user/invite/:code", routing.Wrap(GetInviteInfoByCode))
r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), routing.Wrap(hs.CompleteInvite))
// reset password
r.Get("/user/password/send-reset-email", hs.Index)
r.Get("/user/password/reset", hs.Index)
r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), Wrap(SendResetPasswordEmail))
r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), Wrap(ResetPassword))
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))
// dashboard snapshots
r.Get("/dashboard/snapshot/*", hs.Index)
r.Get("/dashboard/snapshots/", reqSignedIn, hs.Index)
// api renew session based on cookie
r.Get("/api/login/ping", quota("session"), Wrap(hs.LoginAPIPing))
r.Get("/api/login/ping", quota("session"), routing.Wrap(hs.LoginAPIPing))
// authed api
r.Group("/api", func(apiRoute routing.RouteRegister) {
// user (signed in)
apiRoute.Group("/user", func(userRoute routing.RouteRegister) {
userRoute.Get("/", Wrap(GetSignedInUser))
userRoute.Put("/", bind(models.UpdateUserCommand{}), Wrap(UpdateSignedInUser))
userRoute.Post("/using/:id", Wrap(UserSetUsingOrg))
userRoute.Get("/orgs", Wrap(GetSignedInUserOrgList))
userRoute.Get("/teams", Wrap(GetSignedInUserTeamList))
userRoute.Get("/", routing.Wrap(GetSignedInUser))
userRoute.Put("/", bind(models.UpdateUserCommand{}), routing.Wrap(UpdateSignedInUser))
userRoute.Post("/using/:id", routing.Wrap(UserSetUsingOrg))
userRoute.Get("/orgs", routing.Wrap(GetSignedInUserOrgList))
userRoute.Get("/teams", routing.Wrap(GetSignedInUserTeamList))
userRoute.Post("/stars/dashboard/:id", Wrap(StarDashboard))
userRoute.Delete("/stars/dashboard/:id", Wrap(UnstarDashboard))
userRoute.Post("/stars/dashboard/:id", routing.Wrap(StarDashboard))
userRoute.Delete("/stars/dashboard/:id", routing.Wrap(UnstarDashboard))
userRoute.Put("/password", bind(models.ChangeUserPasswordCommand{}), Wrap(ChangeUserPassword))
userRoute.Get("/quotas", Wrap(GetUserQuotas))
userRoute.Put("/helpflags/:id", Wrap(SetHelpFlag))
userRoute.Put("/password", bind(models.ChangeUserPasswordCommand{}), routing.Wrap(ChangeUserPassword))
userRoute.Get("/quotas", routing.Wrap(GetUserQuotas))
userRoute.Put("/helpflags/:id", routing.Wrap(SetHelpFlag))
// For dev purpose
userRoute.Get("/helpflags/clear", Wrap(ClearHelpFlags))
userRoute.Get("/helpflags/clear", routing.Wrap(ClearHelpFlags))
userRoute.Get("/preferences", Wrap(GetUserPreferences))
userRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateUserPreferences))
userRoute.Get("/preferences", routing.Wrap(GetUserPreferences))
userRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), routing.Wrap(UpdateUserPreferences))
userRoute.Get("/auth-tokens", Wrap(hs.GetUserAuthTokens))
userRoute.Post("/revoke-auth-token", bind(models.RevokeAuthTokenCmd{}), Wrap(hs.RevokeUserAuthToken))
userRoute.Get("/auth-tokens", routing.Wrap(hs.GetUserAuthTokens))
userRoute.Post("/revoke-auth-token", bind(models.RevokeAuthTokenCmd{}), routing.Wrap(hs.RevokeUserAuthToken))
})
// users (admin permission required)
apiRoute.Group("/users", func(usersRoute routing.RouteRegister) {
usersRoute.Get("/", Wrap(SearchUsers))
usersRoute.Get("/search", Wrap(SearchUsersWithPaging))
usersRoute.Get("/:id", Wrap(GetUserByID))
usersRoute.Get("/:id/teams", Wrap(GetUserTeams))
usersRoute.Get("/:id/orgs", Wrap(GetUserOrgList))
usersRoute.Get("/", routing.Wrap(SearchUsers))
usersRoute.Get("/search", routing.Wrap(SearchUsersWithPaging))
usersRoute.Get("/:id", routing.Wrap(GetUserByID))
usersRoute.Get("/:id/teams", routing.Wrap(GetUserTeams))
usersRoute.Get("/:id/orgs", routing.Wrap(GetUserOrgList))
// query parameters /users/lookup?loginOrEmail=admin@example.com
usersRoute.Get("/lookup", Wrap(GetUserByLoginOrEmail))
usersRoute.Put("/:id", bind(models.UpdateUserCommand{}), Wrap(UpdateUser))
usersRoute.Post("/:id/using/:orgId", Wrap(UpdateUserActiveOrg))
usersRoute.Get("/lookup", routing.Wrap(GetUserByLoginOrEmail))
usersRoute.Put("/:id", bind(models.UpdateUserCommand{}), routing.Wrap(UpdateUser))
usersRoute.Post("/:id/using/:orgId", routing.Wrap(UpdateUserActiveOrg))
}, reqGrafanaAdmin)
// team (admin permission required)
apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) {
teamsRoute.Post("/", bind(models.CreateTeamCommand{}), Wrap(hs.CreateTeam))
teamsRoute.Put("/:teamId", bind(models.UpdateTeamCommand{}), Wrap(hs.UpdateTeam))
teamsRoute.Delete("/:teamId", Wrap(hs.DeleteTeamByID))
teamsRoute.Get("/:teamId/members", Wrap(hs.GetTeamMembers))
teamsRoute.Post("/:teamId/members", bind(models.AddTeamMemberCommand{}), Wrap(hs.AddTeamMember))
teamsRoute.Put("/:teamId/members/:userId", bind(models.UpdateTeamMemberCommand{}), Wrap(hs.UpdateTeamMember))
teamsRoute.Delete("/:teamId/members/:userId", Wrap(hs.RemoveTeamMember))
teamsRoute.Get("/:teamId/preferences", Wrap(hs.GetTeamPreferences))
teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(hs.UpdateTeamPreferences))
teamsRoute.Post("/", bind(models.CreateTeamCommand{}), routing.Wrap(hs.CreateTeam))
teamsRoute.Put("/:teamId", bind(models.UpdateTeamCommand{}), 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.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))
}, reqCanAccessTeams)
// team without requirement of user to be org admin
apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) {
teamsRoute.Get("/:teamId", Wrap(hs.GetTeamByID))
teamsRoute.Get("/search", Wrap(hs.SearchTeams))
teamsRoute.Get("/:teamId", routing.Wrap(hs.GetTeamByID))
teamsRoute.Get("/search", routing.Wrap(hs.SearchTeams))
})
// org information available to all users.
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/", Wrap(GetOrgCurrent))
orgRoute.Get("/quotas", Wrap(GetOrgQuotas))
orgRoute.Get("/", routing.Wrap(GetOrgCurrent))
orgRoute.Get("/quotas", routing.Wrap(GetOrgQuotas))
})
// current org
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), Wrap(UpdateOrgCurrent))
orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), Wrap(UpdateOrgAddressCurrent))
orgRoute.Get("/users", Wrap(hs.GetOrgUsersForCurrentOrg))
orgRoute.Post("/users", quota("user"), bind(models.AddOrgUserCommand{}), Wrap(AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", bind(models.UpdateOrgUserCommand{}), Wrap(UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", Wrap(RemoveOrgUserForCurrentOrg))
orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), routing.Wrap(UpdateOrgCurrent))
orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), routing.Wrap(UpdateOrgAddressCurrent))
orgRoute.Get("/users", routing.Wrap(hs.GetOrgUsersForCurrentOrg))
orgRoute.Post("/users", quota("user"), bind(models.AddOrgUserCommand{}), routing.Wrap(AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", bind(models.UpdateOrgUserCommand{}), routing.Wrap(UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", routing.Wrap(RemoveOrgUserForCurrentOrg))
// invites
orgRoute.Get("/invites", Wrap(GetPendingOrgInvites))
orgRoute.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), Wrap(AddOrgInvite))
orgRoute.Patch("/invites/:code/revoke", Wrap(RevokeInvite))
orgRoute.Get("/invites", routing.Wrap(GetPendingOrgInvites))
orgRoute.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), routing.Wrap(AddOrgInvite))
orgRoute.Patch("/invites/:code/revoke", routing.Wrap(RevokeInvite))
// prefs
orgRoute.Get("/preferences", Wrap(GetOrgPreferences))
orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateOrgPreferences))
orgRoute.Get("/preferences", routing.Wrap(GetOrgPreferences))
orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), routing.Wrap(UpdateOrgPreferences))
}, reqOrgAdmin)
// current org without requirement of user to be org admin
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/users/lookup", Wrap(hs.GetOrgUsersForCurrentOrgLookup))
orgRoute.Get("/users/lookup", routing.Wrap(hs.GetOrgUsersForCurrentOrgLookup))
})
// create new org
apiRoute.Post("/orgs", quota("org"), bind(models.CreateOrgCommand{}), Wrap(CreateOrg))
apiRoute.Post("/orgs", quota("org"), bind(models.CreateOrgCommand{}), routing.Wrap(CreateOrg))
// search all orgs
apiRoute.Get("/orgs", reqGrafanaAdmin, Wrap(SearchOrgs))
apiRoute.Get("/orgs", reqGrafanaAdmin, routing.Wrap(SearchOrgs))
// orgs (admin routes)
apiRoute.Group("/orgs/:orgId", func(orgsRoute routing.RouteRegister) {
orgsRoute.Get("/", Wrap(GetOrgByID))
orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), Wrap(UpdateOrg))
orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), Wrap(UpdateOrgAddress))
orgsRoute.Delete("/", Wrap(DeleteOrgByID))
orgsRoute.Get("/users", Wrap(hs.GetOrgUsers))
orgsRoute.Post("/users", bind(models.AddOrgUserCommand{}), Wrap(AddOrgUser))
orgsRoute.Patch("/users/:userId", bind(models.UpdateOrgUserCommand{}), Wrap(UpdateOrgUser))
orgsRoute.Delete("/users/:userId", Wrap(RemoveOrgUser))
orgsRoute.Get("/quotas", Wrap(GetOrgQuotas))
orgsRoute.Put("/quotas/:target", bind(models.UpdateOrgQuotaCmd{}), Wrap(UpdateOrgQuota))
orgsRoute.Get("/", routing.Wrap(GetOrgByID))
orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), routing.Wrap(UpdateOrg))
orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), routing.Wrap(UpdateOrgAddress))
orgsRoute.Delete("/", routing.Wrap(DeleteOrgByID))
orgsRoute.Get("/users", routing.Wrap(hs.GetOrgUsers))
orgsRoute.Post("/users", bind(models.AddOrgUserCommand{}), routing.Wrap(AddOrgUser))
orgsRoute.Patch("/users/:userId", bind(models.UpdateOrgUserCommand{}), routing.Wrap(UpdateOrgUser))
orgsRoute.Delete("/users/:userId", routing.Wrap(RemoveOrgUser))
orgsRoute.Get("/quotas", routing.Wrap(GetOrgQuotas))
orgsRoute.Put("/quotas/:target", bind(models.UpdateOrgQuotaCmd{}), routing.Wrap(UpdateOrgQuota))
}, reqGrafanaAdmin)
// orgs (admin routes)
apiRoute.Group("/orgs/name/:name", func(orgsRoute routing.RouteRegister) {
orgsRoute.Get("/", Wrap(hs.GetOrgByName))
orgsRoute.Get("/", routing.Wrap(hs.GetOrgByName))
}, reqGrafanaAdmin)
// auth api keys
apiRoute.Group("/auth/keys", func(keysRoute routing.RouteRegister) {
keysRoute.Get("/", Wrap(GetAPIKeys))
keysRoute.Post("/", quota("api_key"), bind(models.AddApiKeyCommand{}), Wrap(hs.AddAPIKey))
keysRoute.Delete("/:id", Wrap(DeleteAPIKey))
keysRoute.Get("/", routing.Wrap(GetAPIKeys))
keysRoute.Post("/", quota("api_key"), bind(models.AddApiKeyCommand{}), routing.Wrap(hs.AddAPIKey))
keysRoute.Delete("/:id", routing.Wrap(DeleteAPIKey))
}, reqOrgAdmin)
// Preferences
apiRoute.Group("/preferences", func(prefRoute routing.RouteRegister) {
prefRoute.Post("/set-home-dash", bind(models.SavePreferencesCommand{}), Wrap(SetHomeDashboard))
prefRoute.Post("/set-home-dash", bind(models.SavePreferencesCommand{}), routing.Wrap(SetHomeDashboard))
})
// Data sources
apiRoute.Group("/datasources", func(datasourceRoute routing.RouteRegister) {
datasourceRoute.Get("/", Wrap(hs.GetDataSources))
datasourceRoute.Post("/", quota("data_source"), bind(models.AddDataSourceCommand{}), Wrap(AddDataSource))
datasourceRoute.Put("/:id", bind(models.UpdateDataSourceCommand{}), Wrap(UpdateDataSource))
datasourceRoute.Delete("/:id", Wrap(DeleteDataSourceById))
datasourceRoute.Delete("/uid/:uid", Wrap(DeleteDataSourceByUID))
datasourceRoute.Delete("/name/:name", Wrap(DeleteDataSourceByName))
datasourceRoute.Get("/:id", Wrap(GetDataSourceById))
datasourceRoute.Get("/uid/:uid", Wrap(GetDataSourceByUID))
datasourceRoute.Get("/name/:name", Wrap(GetDataSourceByName))
datasourceRoute.Get("/", routing.Wrap(hs.GetDataSources))
datasourceRoute.Post("/", quota("data_source"), bind(models.AddDataSourceCommand{}), routing.Wrap(AddDataSource))
datasourceRoute.Put("/:id", bind(models.UpdateDataSourceCommand{}), routing.Wrap(UpdateDataSource))
datasourceRoute.Delete("/:id", routing.Wrap(DeleteDataSourceById))
datasourceRoute.Delete("/uid/:uid", routing.Wrap(DeleteDataSourceByUID))
datasourceRoute.Delete("/name/:name", routing.Wrap(DeleteDataSourceByName))
datasourceRoute.Get("/:id", routing.Wrap(GetDataSourceById))
datasourceRoute.Get("/uid/:uid", routing.Wrap(GetDataSourceByUID))
datasourceRoute.Get("/name/:name", routing.Wrap(GetDataSourceByName))
}, reqOrgAdmin)
apiRoute.Get("/datasources/id/:name", Wrap(GetDataSourceIdByName), reqSignedIn)
apiRoute.Get("/datasources/id/:name", routing.Wrap(GetDataSourceIdByName), reqSignedIn)
apiRoute.Get("/plugins", Wrap(hs.GetPluginList))
apiRoute.Get("/plugins/:pluginId/settings", Wrap(GetPluginSettingByID))
apiRoute.Get("/plugins/:pluginId/markdown/:name", Wrap(GetPluginMarkdown))
apiRoute.Get("/plugins/:pluginId/health", Wrap(hs.CheckHealth))
apiRoute.Get("/plugins", routing.Wrap(hs.GetPluginList))
apiRoute.Get("/plugins/:pluginId/settings", routing.Wrap(GetPluginSettingByID))
apiRoute.Get("/plugins/:pluginId/markdown/:name", routing.Wrap(GetPluginMarkdown))
apiRoute.Get("/plugins/:pluginId/health", routing.Wrap(hs.CheckHealth))
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
apiRoute.Any("/plugins/:pluginId/resources/*", hs.CallResource)
apiRoute.Any("/plugins/errors", Wrap(hs.GetPluginErrorsList))
apiRoute.Any("/plugins/errors", routing.Wrap(hs.GetPluginErrorsList))
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Get("/:pluginId/dashboards/", Wrap(GetPluginDashboards))
pluginRoute.Post("/:pluginId/settings", bind(models.UpdatePluginSettingCmd{}), Wrap(UpdatePluginSetting))
pluginRoute.Get("/:pluginId/metrics", Wrap(hs.CollectPluginMetrics))
pluginRoute.Get("/:pluginId/dashboards/", routing.Wrap(GetPluginDashboards))
pluginRoute.Post("/:pluginId/settings", bind(models.UpdatePluginSettingCmd{}), routing.Wrap(UpdatePluginSetting))
pluginRoute.Get("/:pluginId/metrics", routing.Wrap(hs.CollectPluginMetrics))
}, reqOrgAdmin)
apiRoute.Get("/frontend/settings/", hs.GetFrontendSettings)
@@ -281,153 +281,153 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Any("/datasources/proxy/:id", reqSignedIn, hs.ProxyDataSourceRequest)
apiRoute.Any("/datasources/:id/resources", hs.CallDatasourceResource)
apiRoute.Any("/datasources/:id/resources/*", hs.CallDatasourceResource)
apiRoute.Any("/datasources/:id/health", Wrap(hs.CheckDatasourceHealth))
apiRoute.Any("/datasources/:id/health", routing.Wrap(hs.CheckDatasourceHealth))
// Folders
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
folderRoute.Get("/", Wrap(GetFolders))
folderRoute.Get("/id/:id", Wrap(GetFolderByID))
folderRoute.Post("/", bind(models.CreateFolderCommand{}), Wrap(hs.CreateFolder))
folderRoute.Get("/", routing.Wrap(GetFolders))
folderRoute.Get("/id/:id", routing.Wrap(GetFolderByID))
folderRoute.Post("/", bind(models.CreateFolderCommand{}), routing.Wrap(hs.CreateFolder))
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
folderUidRoute.Get("/", Wrap(GetFolderByUID))
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), Wrap(UpdateFolder))
folderUidRoute.Delete("/", Wrap(DeleteFolder))
folderUidRoute.Get("/", routing.Wrap(GetFolderByUID))
folderUidRoute.Put("/", bind(models.UpdateFolderCommand{}), routing.Wrap(UpdateFolder))
folderUidRoute.Delete("/", routing.Wrap(DeleteFolder))
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
folderPermissionRoute.Get("/", Wrap(hs.GetFolderPermissionList))
folderPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), Wrap(hs.UpdateFolderPermissions))
folderPermissionRoute.Get("/", routing.Wrap(hs.GetFolderPermissionList))
folderPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), routing.Wrap(hs.UpdateFolderPermissions))
})
})
})
// Dashboard
apiRoute.Group("/dashboards", func(dashboardRoute routing.RouteRegister) {
dashboardRoute.Get("/uid/:uid", Wrap(hs.GetDashboard))
dashboardRoute.Delete("/uid/:uid", Wrap(DeleteDashboardByUID))
dashboardRoute.Get("/uid/:uid", routing.Wrap(hs.GetDashboard))
dashboardRoute.Delete("/uid/:uid", routing.Wrap(DeleteDashboardByUID))
dashboardRoute.Get("/db/:slug", Wrap(hs.GetDashboard))
dashboardRoute.Delete("/db/:slug", Wrap(DeleteDashboardBySlug))
dashboardRoute.Get("/db/:slug", routing.Wrap(hs.GetDashboard))
dashboardRoute.Delete("/db/:slug", routing.Wrap(DeleteDashboardBySlug))
dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), Wrap(CalculateDashboardDiff))
dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), routing.Wrap(CalculateDashboardDiff))
dashboardRoute.Post("/db", bind(models.SaveDashboardCommand{}), Wrap(hs.PostDashboard))
dashboardRoute.Get("/home", Wrap(hs.GetHomeDashboard))
dashboardRoute.Post("/db", bind(models.SaveDashboardCommand{}), routing.Wrap(hs.PostDashboard))
dashboardRoute.Get("/home", routing.Wrap(hs.GetHomeDashboard))
dashboardRoute.Get("/tags", GetDashboardTags)
dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), Wrap(ImportDashboard))
dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), routing.Wrap(ImportDashboard))
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) {
dashIdRoute.Get("/versions", Wrap(GetDashboardVersions))
dashIdRoute.Get("/versions/:id", Wrap(GetDashboardVersion))
dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), Wrap(hs.RestoreDashboardVersion))
dashIdRoute.Get("/versions", routing.Wrap(GetDashboardVersions))
dashIdRoute.Get("/versions/:id", routing.Wrap(GetDashboardVersion))
dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), routing.Wrap(hs.RestoreDashboardVersion))
dashIdRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) {
dashboardPermissionRoute.Get("/", Wrap(hs.GetDashboardPermissionList))
dashboardPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), Wrap(hs.UpdateDashboardPermissions))
dashboardPermissionRoute.Get("/", routing.Wrap(hs.GetDashboardPermissionList))
dashboardPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), routing.Wrap(hs.UpdateDashboardPermissions))
})
})
})
// Dashboard snapshots
apiRoute.Group("/dashboard/snapshots", func(dashboardRoute routing.RouteRegister) {
dashboardRoute.Get("/", Wrap(SearchDashboardSnapshots))
dashboardRoute.Get("/", routing.Wrap(SearchDashboardSnapshots))
})
// Playlist
apiRoute.Group("/playlists", func(playlistRoute routing.RouteRegister) {
playlistRoute.Get("/", Wrap(SearchPlaylists))
playlistRoute.Get("/:id", ValidateOrgPlaylist, Wrap(GetPlaylist))
playlistRoute.Get("/:id/items", ValidateOrgPlaylist, Wrap(GetPlaylistItems))
playlistRoute.Get("/:id/dashboards", ValidateOrgPlaylist, Wrap(GetPlaylistDashboards))
playlistRoute.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, Wrap(DeletePlaylist))
playlistRoute.Put("/:id", reqEditorRole, bind(models.UpdatePlaylistCommand{}), ValidateOrgPlaylist, Wrap(UpdatePlaylist))
playlistRoute.Post("/", reqEditorRole, bind(models.CreatePlaylistCommand{}), Wrap(CreatePlaylist))
playlistRoute.Get("/", routing.Wrap(SearchPlaylists))
playlistRoute.Get("/:id", ValidateOrgPlaylist, routing.Wrap(GetPlaylist))
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))
})
// Search
apiRoute.Get("/search/sorting", Wrap(hs.ListSortOptions))
apiRoute.Get("/search/", Wrap(Search))
apiRoute.Get("/search/sorting", routing.Wrap(hs.ListSortOptions))
apiRoute.Get("/search/", routing.Wrap(Search))
// metrics
apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), Wrap(hs.QueryMetrics))
apiRoute.Get("/tsdb/testdata/scenarios", Wrap(GetTestDataScenarios))
apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, Wrap(GenerateSQLTestData))
apiRoute.Get("/tsdb/testdata/random-walk", Wrap(GetTestDataRandomWalk))
apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), routing.Wrap(hs.QueryMetrics))
apiRoute.Get("/tsdb/testdata/scenarios", routing.Wrap(GetTestDataScenarios))
apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, routing.Wrap(GenerateSQLTestData))
apiRoute.Get("/tsdb/testdata/random-walk", routing.Wrap(GetTestDataRandomWalk))
// DataSource w/ expressions
apiRoute.Post("/ds/query", bind(dtos.MetricRequest{}), Wrap(hs.QueryMetricsV2))
apiRoute.Post("/ds/query", bind(dtos.MetricRequest{}), routing.Wrap(hs.QueryMetricsV2))
apiRoute.Group("/alerts", func(alertsRoute routing.RouteRegister) {
alertsRoute.Post("/test", bind(dtos.AlertTestCommand{}), Wrap(AlertTest))
alertsRoute.Post("/:alertId/pause", reqEditorRole, bind(dtos.PauseAlertCommand{}), Wrap(PauseAlert))
alertsRoute.Get("/:alertId", ValidateOrgAlert, Wrap(GetAlert))
alertsRoute.Get("/", Wrap(GetAlerts))
alertsRoute.Get("/states-for-dashboard", Wrap(GetAlertStatesForDashboard))
alertsRoute.Post("/test", bind(dtos.AlertTestCommand{}), routing.Wrap(AlertTest))
alertsRoute.Post("/:alertId/pause", reqEditorRole, bind(dtos.PauseAlertCommand{}), routing.Wrap(PauseAlert))
alertsRoute.Get("/:alertId", ValidateOrgAlert, routing.Wrap(GetAlert))
alertsRoute.Get("/", routing.Wrap(GetAlerts))
alertsRoute.Get("/states-for-dashboard", routing.Wrap(GetAlertStatesForDashboard))
})
apiRoute.Get("/alert-notifiers", reqEditorRole, Wrap(GetAlertNotifiers))
apiRoute.Get("/alert-notifiers", reqEditorRole, routing.Wrap(GetAlertNotifiers))
apiRoute.Group("/alert-notifications", func(alertNotifications routing.RouteRegister) {
alertNotifications.Get("/", Wrap(GetAlertNotifications))
alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), Wrap(NotificationTest))
alertNotifications.Post("/", bind(models.CreateAlertNotificationCommand{}), Wrap(CreateAlertNotification))
alertNotifications.Put("/:notificationId", bind(models.UpdateAlertNotificationCommand{}), Wrap(UpdateAlertNotification))
alertNotifications.Get("/:notificationId", Wrap(GetAlertNotificationByID))
alertNotifications.Delete("/:notificationId", Wrap(DeleteAlertNotification))
alertNotifications.Get("/uid/:uid", Wrap(GetAlertNotificationByUID))
alertNotifications.Put("/uid/:uid", bind(models.UpdateAlertNotificationWithUidCommand{}), Wrap(UpdateAlertNotificationByUID))
alertNotifications.Delete("/uid/:uid", Wrap(DeleteAlertNotificationByUID))
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(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(UpdateAlertNotificationByUID))
alertNotifications.Delete("/uid/:uid", routing.Wrap(DeleteAlertNotificationByUID))
}, reqEditorRole)
// alert notifications without requirement of user to be org editor
apiRoute.Group("/alert-notifications", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/lookup", Wrap(GetAlertNotificationLookup))
orgRoute.Get("/lookup", routing.Wrap(GetAlertNotificationLookup))
})
apiRoute.Get("/annotations", Wrap(GetAnnotations))
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), Wrap(DeleteAnnotations))
apiRoute.Get("/annotations", routing.Wrap(GetAnnotations))
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), routing.Wrap(DeleteAnnotations))
apiRoute.Group("/annotations", func(annotationsRoute routing.RouteRegister) {
annotationsRoute.Post("/", bind(dtos.PostAnnotationsCmd{}), Wrap(PostAnnotation))
annotationsRoute.Delete("/:annotationId", Wrap(DeleteAnnotationByID))
annotationsRoute.Put("/:annotationId", bind(dtos.UpdateAnnotationsCmd{}), Wrap(UpdateAnnotation))
annotationsRoute.Patch("/:annotationId", bind(dtos.PatchAnnotationsCmd{}), Wrap(PatchAnnotation))
annotationsRoute.Post("/graphite", reqEditorRole, bind(dtos.PostGraphiteAnnotationsCmd{}), Wrap(PostGraphiteAnnotation))
annotationsRoute.Post("/", bind(dtos.PostAnnotationsCmd{}), 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))
})
// error test
r.Get("/metrics/error", Wrap(GenerateError))
r.Get("/metrics/error", routing.Wrap(GenerateError))
// short urls
apiRoute.Post("/short-urls", bind(dtos.CreateShortURLCmd{}), Wrap(hs.createShortURL))
apiRoute.Post("/short-urls", bind(dtos.CreateShortURLCmd{}), routing.Wrap(hs.createShortURL))
}, reqSignedIn)
// admin api
r.Group("/api/admin", func(adminRoute routing.RouteRegister) {
adminRoute.Get("/settings", Wrap(AdminGetSettings))
adminRoute.Post("/users", bind(dtos.AdminCreateUserForm{}), Wrap(AdminCreateUser))
adminRoute.Put("/users/:id/password", bind(dtos.AdminUpdateUserPasswordForm{}), Wrap(AdminUpdateUserPassword))
adminRoute.Put("/users/:id/permissions", bind(dtos.AdminUpdateUserPermissionsForm{}), Wrap(AdminUpdateUserPermissions))
adminRoute.Delete("/users/:id", Wrap(AdminDeleteUser))
adminRoute.Post("/users/:id/disable", Wrap(hs.AdminDisableUser))
adminRoute.Post("/users/:id/enable", Wrap(AdminEnableUser))
adminRoute.Get("/users/:id/quotas", Wrap(GetUserQuotas))
adminRoute.Put("/users/:id/quotas/:target", bind(models.UpdateUserQuotaCmd{}), Wrap(UpdateUserQuota))
adminRoute.Get("/stats", Wrap(AdminGetStats))
adminRoute.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), Wrap(PauseAllAlerts))
adminRoute.Get("/settings", routing.Wrap(AdminGetSettings))
adminRoute.Post("/users", bind(dtos.AdminCreateUserForm{}), routing.Wrap(AdminCreateUser))
adminRoute.Put("/users/:id/password", bind(dtos.AdminUpdateUserPasswordForm{}), routing.Wrap(AdminUpdateUserPassword))
adminRoute.Put("/users/:id/permissions", bind(dtos.AdminUpdateUserPermissionsForm{}), routing.Wrap(AdminUpdateUserPermissions))
adminRoute.Delete("/users/:id", routing.Wrap(AdminDeleteUser))
adminRoute.Post("/users/:id/disable", routing.Wrap(hs.AdminDisableUser))
adminRoute.Post("/users/:id/enable", routing.Wrap(AdminEnableUser))
adminRoute.Get("/users/:id/quotas", routing.Wrap(GetUserQuotas))
adminRoute.Put("/users/:id/quotas/:target", bind(models.UpdateUserQuotaCmd{}), routing.Wrap(UpdateUserQuota))
adminRoute.Get("/stats", routing.Wrap(AdminGetStats))
adminRoute.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), routing.Wrap(PauseAllAlerts))
adminRoute.Post("/users/:id/logout", Wrap(hs.AdminLogoutUser))
adminRoute.Get("/users/:id/auth-tokens", Wrap(hs.AdminGetUserAuthTokens))
adminRoute.Post("/users/:id/revoke-auth-token", bind(models.RevokeAuthTokenCmd{}), Wrap(hs.AdminRevokeUserAuthToken))
adminRoute.Post("/users/:id/logout", routing.Wrap(hs.AdminLogoutUser))
adminRoute.Get("/users/:id/auth-tokens", routing.Wrap(hs.AdminGetUserAuthTokens))
adminRoute.Post("/users/:id/revoke-auth-token", bind(models.RevokeAuthTokenCmd{}), routing.Wrap(hs.AdminRevokeUserAuthToken))
adminRoute.Post("/provisioning/dashboards/reload", Wrap(hs.AdminProvisioningReloadDashboards))
adminRoute.Post("/provisioning/plugins/reload", Wrap(hs.AdminProvisioningReloadPlugins))
adminRoute.Post("/provisioning/datasources/reload", Wrap(hs.AdminProvisioningReloadDatasources))
adminRoute.Post("/provisioning/notifications/reload", Wrap(hs.AdminProvisioningReloadNotifications))
adminRoute.Post("/ldap/reload", Wrap(hs.ReloadLDAPCfg))
adminRoute.Post("/ldap/sync/:id", Wrap(hs.PostSyncUserWithLDAP))
adminRoute.Get("/ldap/:username", Wrap(hs.GetUserFromLDAP))
adminRoute.Get("/ldap/status", Wrap(hs.GetLDAPStatus))
adminRoute.Post("/provisioning/dashboards/reload", routing.Wrap(hs.AdminProvisioningReloadDashboards))
adminRoute.Post("/provisioning/plugins/reload", routing.Wrap(hs.AdminProvisioningReloadPlugins))
adminRoute.Post("/provisioning/datasources/reload", routing.Wrap(hs.AdminProvisioningReloadDatasources))
adminRoute.Post("/provisioning/notifications/reload", routing.Wrap(hs.AdminProvisioningReloadNotifications))
adminRoute.Post("/ldap/reload", routing.Wrap(hs.ReloadLDAPCfg))
adminRoute.Post("/ldap/sync/:id", routing.Wrap(hs.PostSyncUserWithLDAP))
adminRoute.Get("/ldap/:username", routing.Wrap(hs.GetUserFromLDAP))
adminRoute.Get("/ldap/status", routing.Wrap(hs.GetLDAPStatus))
}, reqGrafanaAdmin)
// rendering
@@ -443,10 +443,10 @@ func (hs *HTTPServer) registerRoutes() {
// Snapshots
r.Post("/api/snapshots/", reqSnapshotPublicModeOrSignedIn, bind(models.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot)
r.Get("/api/snapshot/shared-options/", reqSignedIn, GetSharingOptions)
r.Get("/api/snapshots/:key", Wrap(GetDashboardSnapshot))
r.Get("/api/snapshots-delete/:deleteKey", reqSnapshotPublicModeOrSignedIn, Wrap(DeleteDashboardSnapshotByDeleteKey))
r.Delete("/api/snapshots/:key", reqEditorRole, Wrap(DeleteDashboardSnapshot))
r.Get("/api/snapshots/:key", routing.Wrap(GetDashboardSnapshot))
r.Get("/api/snapshots-delete/:deleteKey", reqSnapshotPublicModeOrSignedIn, routing.Wrap(DeleteDashboardSnapshotByDeleteKey))
r.Delete("/api/snapshots/:key", reqEditorRole, routing.Wrap(DeleteDashboardSnapshot))
// Frontend logs
r.Post("/log", middleware.RateLimit(hs.Cfg.Sentry.EndpointRPS, hs.Cfg.Sentry.EndpointBurst, time.Now), bind(frontendSentryEvent{}), Wrap(hs.logFrontendMessage))
r.Post("/log", middleware.RateLimit(hs.Cfg.Sentry.EndpointRPS, hs.Cfg.Sentry.EndpointBurst, time.Now), bind(frontendSentryEvent{}), routing.Wrap(hs.logFrontendMessage))
}

View File

@@ -5,16 +5,17 @@ import (
"time"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
"github.com/grafana/grafana/pkg/models"
)
func GetAPIKeys(c *models.ReqContext) Response {
func GetAPIKeys(c *models.ReqContext) response.Response {
query := models.GetApiKeysQuery{OrgId: c.OrgId, IncludeExpired: c.QueryBool("includeExpired")}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to list api keys", err)
return response.Error(500, "Failed to list api keys", err)
}
result := make([]*models.ApiKeyDTO, len(query.Result))
@@ -32,52 +33,52 @@ func GetAPIKeys(c *models.ReqContext) Response {
}
}
return JSON(200, result)
return response.JSON(200, result)
}
func DeleteAPIKey(c *models.ReqContext) Response {
func DeleteAPIKey(c *models.ReqContext) response.Response {
id := c.ParamsInt64(":id")
cmd := &models.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
err := bus.Dispatch(cmd)
if err != nil {
return Error(500, "Failed to delete API key", err)
return response.Error(500, "Failed to delete API key", err)
}
return Success("API key deleted")
return response.Success("API key deleted")
}
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyCommand) Response {
func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyCommand) response.Response {
if !cmd.Role.IsValid() {
return Error(400, "Invalid role specified", nil)
return response.Error(400, "Invalid role specified", nil)
}
if hs.Cfg.ApiKeyMaxSecondsToLive != -1 {
if cmd.SecondsToLive == 0 {
return Error(400, "Number of seconds before expiration should be set", nil)
return response.Error(400, "Number of seconds before expiration should be set", nil)
}
if cmd.SecondsToLive > hs.Cfg.ApiKeyMaxSecondsToLive {
return Error(400, "Number of seconds before expiration is greater than the global limit", nil)
return response.Error(400, "Number of seconds before expiration is greater than the global limit", nil)
}
}
cmd.OrgId = c.OrgId
newKeyInfo, err := apikeygen.New(cmd.OrgId, cmd.Name)
if err != nil {
return Error(500, "Generating API key failed", err)
return response.Error(500, "Generating API key failed", err)
}
cmd.Key = newKeyInfo.HashedKey
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrInvalidApiKeyExpiration) {
return Error(400, err.Error(), nil)
return response.Error(400, err.Error(), nil)
}
if errors.Is(err, models.ErrDuplicateApiKey) {
return Error(409, err.Error(), nil)
return response.Error(409, err.Error(), nil)
}
return Error(500, "Failed to add API Key", err)
return response.Error(500, "Failed to add API Key", err)
}
result := &dtos.NewApiKeyResult{
@@ -86,5 +87,5 @@ func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyComman
Key: newKeyInfo.ClientSecret,
}
return JSON(200, result)
return response.JSON(200, result)
}

View File

@@ -1,109 +0,0 @@
package api
import (
"encoding/json"
"net/http"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/macaron.v1"
)
var (
ServerError = func(err error) Response {
return Error(500, "Server error", err)
}
)
func Wrap(action interface{}) macaron.Handler {
return func(c *models.ReqContext) {
var res Response
val, err := c.Invoke(action)
if err == nil && val != nil && len(val) > 0 {
res = val[0].Interface().(Response)
} else {
res = ServerError(err)
}
res.WriteTo(c)
}
}
// JSON creates a JSON response.
func JSON(status int, body interface{}) *NormalResponse {
return Respond(status, body).Header("Content-Type", "application/json")
}
// jsonStreaming creates a streaming JSON response.
func jsonStreaming(status int, body interface{}) streamingResponse {
header := make(http.Header)
header.Set("Content-Type", "application/json")
return streamingResponse{
status: status,
body: body,
header: header,
}
}
// Success create a successful response
func Success(message string) *NormalResponse {
resp := make(map[string]interface{})
resp["message"] = message
return JSON(200, resp)
}
// Error creates an error response.
func Error(status int, message string, err error) *NormalResponse {
data := make(map[string]interface{})
switch status {
case 404:
data["message"] = "Not Found"
case 500:
data["message"] = "Internal Server Error"
}
if message != "" {
data["message"] = message
}
if err != nil {
if setting.Env != setting.Prod {
data["error"] = err.Error()
}
}
resp := JSON(status, data)
if err != nil {
resp.errMessage = message
resp.err = err
}
return resp
}
// Respond creates a response.
func Respond(status int, body interface{}) *NormalResponse {
var b []byte
switch t := body.(type) {
case []byte:
b = t
case string:
b = []byte(t)
default:
var err error
if b, err = json.Marshal(body); err != nil {
return Error(500, "body json marshal", err)
}
}
return &NormalResponse{
body: b,
status: status,
header: make(http.Header),
}
}
func Redirect(location string) *RedirectResponse {
return &RedirectResponse{location: location}
}

View File

@@ -7,6 +7,8 @@ import (
"path/filepath"
"testing"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/remotecache"
@@ -30,7 +32,7 @@ func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -59,7 +61,7 @@ func anonymousUserScenario(t *testing.T, desc string, method string, url string,
defer bus.ClearBusHandlers()
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
if sc.handlerFunc != nil {
return sc.handlerFunc(sc.context)
@@ -146,7 +148,7 @@ func (sc *scenarioContext) exec() {
}
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *models.ReqContext) Response
type handlerFunc func(c *models.ReqContext) response.Response
func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHandler {
t.Helper()

View File

@@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/dashdiffs"
"github.com/grafana/grafana/pkg/components/simplejson"
@@ -38,15 +39,15 @@ func isDashboardStarredByUser(c *models.ReqContext, dashID int64) (bool, error)
return query.Result, nil
}
func dashboardGuardianResponse(err error) Response {
func dashboardGuardianResponse(err error) response.Response {
if err != nil {
return Error(500, "Error while checking dashboard permissions", err)
return response.Error(500, "Error while checking dashboard permissions", err)
}
return Error(403, "Access denied to this dashboard", nil)
return response.Error(403, "Access denied to this dashboard", nil)
}
func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
slug := c.Params(":slug")
uid := c.Params(":uid")
dash, rsp := getDashboardHelper(c.OrgId, slug, 0, uid)
@@ -64,7 +65,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
}
}
if isEmptyData {
return Error(500, "Error while loading dashboard, dashboard data is invalid", nil)
return response.Error(500, "Error while loading dashboard, dashboard data is invalid", nil)
}
}
@@ -79,7 +80,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
isStarred, err := isDashboardStarredByUser(c, dash.Id)
if err != nil {
return Error(500, "Error while checking if dashboard was starred by user", err)
return response.Error(500, "Error while checking if dashboard was starred by user", err)
}
// Finding creator and last updater of the dashboard
@@ -115,7 +116,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
if dash.FolderId > 0 {
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Dashboard folder could not be read", err)
return response.Error(500, "Dashboard folder could not be read", err)
}
meta.FolderTitle = query.Result.Title
meta.FolderUrl = query.Result.GetUrl()
@@ -123,7 +124,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
provisioningData, err := dashboards.NewProvisioningService().GetProvisionedDashboardDataByDashboardID(dash.Id)
if err != nil {
return Error(500, "Error while checking if dashboard is provisioned", err)
return response.Error(500, "Error while checking if dashboard is provisioned", err)
}
if provisioningData != nil {
@@ -152,7 +153,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
}
c.TimeRequest(metrics.MApiDashboardGet)
return JSON(200, dto)
return response.JSON(200, dto)
}
func getUserLogin(userID int64) string {
@@ -164,7 +165,7 @@ func getUserLogin(userID int64) string {
return query.Result.Login
}
func getDashboardHelper(orgID int64, slug string, id int64, uid string) (*models.Dashboard, Response) {
func getDashboardHelper(orgID int64, slug string, id int64, uid string) (*models.Dashboard, response.Response) {
var query models.GetDashboardQuery
if len(uid) > 0 {
@@ -174,31 +175,31 @@ func getDashboardHelper(orgID int64, slug string, id int64, uid string) (*models
}
if err := bus.Dispatch(&query); err != nil {
return nil, Error(404, "Dashboard not found", err)
return nil, response.Error(404, "Dashboard not found", err)
}
return query.Result, nil
}
func DeleteDashboardBySlug(c *models.ReqContext) Response {
func DeleteDashboardBySlug(c *models.ReqContext) response.Response {
query := models.GetDashboardsBySlugQuery{OrgId: c.OrgId, Slug: c.Params(":slug")}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to retrieve dashboards by slug", err)
return response.Error(500, "Failed to retrieve dashboards by slug", err)
}
if len(query.Result) > 1 {
return JSON(412, util.DynMap{"status": "multiple-slugs-exists", "message": models.ErrDashboardsWithSameSlugExists.Error()})
return response.JSON(412, util.DynMap{"status": "multiple-slugs-exists", "message": models.ErrDashboardsWithSameSlugExists.Error()})
}
return deleteDashboard(c)
}
func DeleteDashboardByUID(c *models.ReqContext) Response {
func DeleteDashboardByUID(c *models.ReqContext) response.Response {
return deleteDashboard(c)
}
func deleteDashboard(c *models.ReqContext) Response {
func deleteDashboard(c *models.ReqContext) response.Response {
dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
if rsp != nil {
return rsp
@@ -214,21 +215,21 @@ func deleteDashboard(c *models.ReqContext) Response {
var dashboardErr models.DashboardErr
if ok := errors.As(err, &dashboardErr); ok {
if errors.Is(err, models.ErrDashboardCannotDeleteProvisionedDashboard) {
return Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
}
}
return Error(500, "Failed to delete dashboard", err)
return response.Error(500, "Failed to delete dashboard", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"title": dash.Title,
"message": fmt.Sprintf("Dashboard %s deleted", dash.Title),
"id": dash.Id,
})
}
func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboardCommand) Response {
func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboardCommand) response.Response {
cmd.OrgId = c.OrgId
cmd.UserId = c.UserId
@@ -238,16 +239,16 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
if newDashboard {
limitReached, err := hs.QuotaService.QuotaReached(c, "dashboard")
if err != nil {
return Error(500, "failed to get quota", err)
return response.Error(500, "failed to get quota", err)
}
if limitReached {
return Error(403, "Quota reached", nil)
return response.Error(403, "Quota reached", nil)
}
}
provisioningData, err := dashboards.NewProvisioningService().GetProvisionedDashboardDataByDashboardID(dash.Id)
if err != nil {
return Error(500, "Error while checking if dashboard is provisioned", err)
return response.Error(500, "Error while checking if dashboard is provisioned", err)
}
allowUiUpdate := true
@@ -288,7 +289,7 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
}
c.TimeRequest(metrics.MApiDashboardSave)
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"status": "success",
"slug": dashboard.Slug,
"version": dashboard.Version,
@@ -298,25 +299,25 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext, cmd models.SaveDashboa
})
}
func dashboardSaveErrorToApiResponse(err error) Response {
func dashboardSaveErrorToApiResponse(err error) response.Response {
var dashboardErr models.DashboardErr
if ok := errors.As(err, &dashboardErr); ok {
if body := dashboardErr.Body(); body != nil {
return JSON(dashboardErr.StatusCode, body)
return response.JSON(dashboardErr.StatusCode, body)
}
if errors.Is(dashboardErr, models.ErrDashboardUpdateAccessDenied) {
return Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
}
return Error(dashboardErr.StatusCode, dashboardErr.Error(), nil)
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), nil)
}
if errors.Is(err, models.ErrFolderNotFound) {
return Error(400, err.Error(), nil)
return response.Error(400, err.Error(), nil)
}
var validationErr alerting.ValidationError
if ok := errors.As(err, &validationErr); ok {
return Error(422, validationErr.Error(), nil)
return response.Error(422, validationErr.Error(), nil)
}
var pluginErr models.UpdatePluginDashboardError
@@ -326,17 +327,17 @@ func dashboardSaveErrorToApiResponse(err error) Response {
if pluginDef, exist := plugins.Plugins[pluginErr.PluginId]; exist {
message = fmt.Sprintf("The dashboard belongs to plugin %s.", pluginDef.Name)
}
return JSON(412, util.DynMap{"status": "plugin-dashboard", "message": message})
return response.JSON(412, util.DynMap{"status": "plugin-dashboard", "message": message})
}
return Error(500, "Failed to save dashboard", err)
return response.Error(500, "Failed to save dashboard", err)
}
// GetHomeDashboard returns the home dashboard.
func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) Response {
func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) response.Response {
prefsQuery := models.GetPreferencesWithDefaultsQuery{User: c.SignedInUser}
if err := hs.Bus.Dispatch(&prefsQuery); err != nil {
return Error(500, "Failed to get preferences", err)
return response.Error(500, "Failed to get preferences", err)
}
if prefsQuery.Result.HomeDashboardId != 0 {
@@ -345,7 +346,7 @@ func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) Response {
if err == nil {
url := models.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
return JSON(200, &dashRedirect)
return response.JSON(200, &dashRedirect)
}
hs.log.Warn("Failed to get slug from database", "err", err)
}
@@ -360,7 +361,7 @@ func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) Response {
// nolint:gosec
file, err := os.Open(filePath)
if err != nil {
return Error(500, "Failed to load home dashboard", err)
return response.Error(500, "Failed to load home dashboard", err)
}
defer func() {
if err := file.Close(); err != nil {
@@ -375,12 +376,12 @@ func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) Response {
jsonParser := json.NewDecoder(file)
if err := jsonParser.Decode(&dash.Dashboard); err != nil {
return Error(500, "Failed to load home dashboard", err)
return response.Error(500, "Failed to load home dashboard", err)
}
hs.addGettingStartedPanelToHomeDashboard(c, dash.Dashboard)
return JSON(200, &dash)
return response.JSON(200, &dash)
}
func (hs *HTTPServer) addGettingStartedPanelToHomeDashboard(c *models.ReqContext, dash *simplejson.Json) {
@@ -410,7 +411,7 @@ func (hs *HTTPServer) addGettingStartedPanelToHomeDashboard(c *models.ReqContext
}
// GetDashboardVersions returns all dashboard versions as JSON
func GetDashboardVersions(c *models.ReqContext) Response {
func GetDashboardVersions(c *models.ReqContext) response.Response {
dashID := c.ParamsInt64(":dashboardId")
guardian := guardian.New(dashID, c.OrgId, c.SignedInUser)
@@ -426,7 +427,7 @@ func GetDashboardVersions(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&query); err != nil {
return Error(404, fmt.Sprintf("No versions found for dashboardId %d", dashID), err)
return response.Error(404, fmt.Sprintf("No versions found for dashboardId %d", dashID), err)
}
for _, version := range query.Result {
@@ -445,11 +446,11 @@ func GetDashboardVersions(c *models.ReqContext) Response {
}
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
// GetDashboardVersion returns the dashboard version with the given ID.
func GetDashboardVersion(c *models.ReqContext) Response {
func GetDashboardVersion(c *models.ReqContext) response.Response {
dashID := c.ParamsInt64(":dashboardId")
guardian := guardian.New(dashID, c.OrgId, c.SignedInUser)
@@ -464,7 +465,7 @@ func GetDashboardVersion(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashID), err)
return response.Error(500, fmt.Sprintf("Dashboard version %d not found for dashboardId %d", query.Version, dashID), err)
}
creator := anonString
@@ -484,11 +485,11 @@ func GetDashboardVersion(c *models.ReqContext) Response {
CreatedBy: creator,
}
return JSON(200, dashVersionMeta)
return response.JSON(200, dashVersionMeta)
}
// POST /api/dashboards/calculate-diff performs diffs on two dashboards
func CalculateDashboardDiff(c *models.ReqContext, apiOptions dtos.CalculateDiffOptions) Response {
func CalculateDashboardDiff(c *models.ReqContext, apiOptions dtos.CalculateDiffOptions) response.Response {
guardianBase := guardian.New(apiOptions.Base.DashboardId, c.OrgId, c.SignedInUser)
if canSave, err := guardianBase.CanSave(); err != nil || !canSave {
return dashboardGuardianResponse(err)
@@ -519,20 +520,20 @@ func CalculateDashboardDiff(c *models.ReqContext, apiOptions dtos.CalculateDiffO
result, err := dashdiffs.CalculateDiff(&options)
if err != nil {
if errors.Is(err, models.ErrDashboardVersionNotFound) {
return Error(404, "Dashboard version not found", err)
return response.Error(404, "Dashboard version not found", err)
}
return Error(500, "Unable to compute diff", err)
return response.Error(500, "Unable to compute diff", err)
}
if options.DiffType == dashdiffs.DiffDelta {
return Respond(200, result.Delta).Header("Content-Type", "application/json")
return response.Respond(200, result.Delta).Header("Content-Type", "application/json")
}
return Respond(200, result.Delta).Header("Content-Type", "text/html")
return response.Respond(200, result.Delta).Header("Content-Type", "text/html")
}
// RestoreDashboardVersion restores a dashboard to the given version.
func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) Response {
func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) response.Response {
dash, rsp := getDashboardHelper(c.OrgId, "", c.ParamsInt64(":dashboardId"), "")
if rsp != nil {
return rsp
@@ -545,7 +546,7 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext, apiCmd dtos.
versionQuery := models.GetDashboardVersionQuery{DashboardId: dash.Id, Version: apiCmd.Version, OrgId: c.OrgId}
if err := bus.Dispatch(&versionQuery); err != nil {
return Error(404, "Dashboard version not found", nil)
return response.Error(404, "Dashboard version not found", nil)
}
version := versionQuery.Result

View File

@@ -5,12 +5,13 @@ import (
"time"
"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/services/guardian"
)
func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) Response {
func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) response.Response {
dashID := c.ParamsInt64(":dashboardId")
_, rsp := getDashboardHelper(c.OrgId, "", dashID, "")
@@ -26,7 +27,7 @@ func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) Response
acl, err := g.GetAcl()
if err != nil {
return Error(500, "Failed to get dashboard permissions", err)
return response.Error(500, "Failed to get dashboard permissions", err)
}
filteredAcls := make([]*models.DashboardAclInfoDTO, 0, len(acl))
@@ -47,12 +48,12 @@ func (hs *HTTPServer) GetDashboardPermissionList(c *models.ReqContext) Response
filteredAcls = append(filteredAcls, perm)
}
return JSON(200, filteredAcls)
return response.JSON(200, filteredAcls)
}
func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) response.Response {
if err := validatePermissionsUpdate(apiCmd); err != nil {
return Error(400, err.Error(), err)
return response.Error(400, err.Error(), err)
}
dashID := c.ParamsInt64(":dashboardId")
@@ -85,31 +86,31 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext, apiCmd dt
hiddenACL, err := g.GetHiddenACL(hs.Cfg)
if err != nil {
return Error(500, "Error while retrieving hidden permissions", err)
return response.Error(500, "Error while retrieving hidden permissions", err)
}
cmd.Items = append(cmd.Items, hiddenACL...)
if okToUpdate, err := g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, cmd.Items); err != nil || !okToUpdate {
if err != nil {
if errors.Is(err, guardian.ErrGuardianPermissionExists) || errors.Is(err, guardian.ErrGuardianOverride) {
return Error(400, err.Error(), err)
return response.Error(400, err.Error(), err)
}
return Error(500, "Error while checking dashboard permissions", err)
return response.Error(500, "Error while checking dashboard permissions", err)
}
return Error(403, "Cannot remove own admin permission for a folder", nil)
return response.Error(403, "Cannot remove own admin permission for a folder", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrDashboardAclInfoMissing) ||
errors.Is(err, models.ErrDashboardPermissionDashboardEmpty) {
return Error(409, err.Error(), err)
return response.Error(409, err.Error(), err)
}
return Error(500, "Failed to create permission", err)
return response.Error(500, "Failed to create permission", err)
}
return Success("Dashboard permissions updated")
return response.Success("Dashboard permissions updated")
}
func validatePermissionsUpdate(apiCmd dtos.UpdateDashboardAclCommand) error {

View File

@@ -6,6 +6,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
@@ -373,7 +375,7 @@ func updateDashboardPermissionScenario(t *testing.T, ctx updatePermissionContext
sc := setupScenarioContext(t, ctx.url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.OrgId = testOrgID
sc.context.UserId = testUserID

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
@@ -141,25 +142,25 @@ func CreateDashboardSnapshot(c *models.ReqContext, cmd models.CreateDashboardSna
}
// GET /api/snapshots/:key
func GetDashboardSnapshot(c *models.ReqContext) Response {
func GetDashboardSnapshot(c *models.ReqContext) response.Response {
key := c.Params(":key")
query := &models.GetDashboardSnapshotQuery{Key: key}
err := bus.Dispatch(query)
if err != nil {
return Error(500, "Failed to get dashboard snapshot", err)
return response.Error(500, "Failed to get dashboard snapshot", err)
}
snapshot := query.Result
// expired snapshots should also be removed from db
if snapshot.Expires.Before(time.Now()) {
return Error(404, "Dashboard snapshot not found", err)
return response.Error(404, "Dashboard snapshot not found", err)
}
dashboard, err := snapshot.DashboardJSON()
if err != nil {
return Error(500, "Failed to get dashboard data for dashboard snapshot", err)
return response.Error(500, "Failed to get dashboard data for dashboard snapshot", err)
}
dto := dtos.DashboardFullWithMeta{
@@ -174,7 +175,7 @@ func GetDashboardSnapshot(c *models.ReqContext) Response {
metrics.MApiDashboardSnapshotGet.Inc()
return JSON(200, dto).Header("Cache-Control", "public, max-age=3600")
return response.JSON(200, dto).Header("Cache-Control", "public, max-age=3600")
}
func deleteExternalDashboardSnapshot(externalUrl string) error {
@@ -207,86 +208,86 @@ func deleteExternalDashboardSnapshot(externalUrl string) error {
}
// GET /api/snapshots-delete/:deleteKey
func DeleteDashboardSnapshotByDeleteKey(c *models.ReqContext) Response {
func DeleteDashboardSnapshotByDeleteKey(c *models.ReqContext) response.Response {
key := c.Params(":deleteKey")
query := &models.GetDashboardSnapshotQuery{DeleteKey: key}
err := bus.Dispatch(query)
if err != nil {
return Error(500, "Failed to get dashboard snapshot", err)
return response.Error(500, "Failed to get dashboard snapshot", err)
}
if query.Result.External {
err := deleteExternalDashboardSnapshot(query.Result.ExternalDeleteUrl)
if err != nil {
return Error(500, "Failed to delete external dashboard", err)
return response.Error(500, "Failed to delete external dashboard", err)
}
}
cmd := &models.DeleteDashboardSnapshotCommand{DeleteKey: query.Result.DeleteKey}
if err := bus.Dispatch(cmd); err != nil {
return Error(500, "Failed to delete dashboard snapshot", err)
return response.Error(500, "Failed to delete dashboard snapshot", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Snapshot deleted. It might take an hour before it's cleared from any CDN caches.",
"id": query.Result.Id,
})
}
// DELETE /api/snapshots/:key
func DeleteDashboardSnapshot(c *models.ReqContext) Response {
func DeleteDashboardSnapshot(c *models.ReqContext) response.Response {
key := c.Params(":key")
query := &models.GetDashboardSnapshotQuery{Key: key}
err := bus.Dispatch(query)
if err != nil {
return Error(500, "Failed to get dashboard snapshot", err)
return response.Error(500, "Failed to get dashboard snapshot", err)
}
if query.Result == nil {
return Error(404, "Failed to get dashboard snapshot", nil)
return response.Error(404, "Failed to get dashboard snapshot", nil)
}
dashboard, err := query.Result.DashboardJSON()
if err != nil {
return Error(500, "Failed to get dashboard data for dashboard snapshot", err)
return response.Error(500, "Failed to get dashboard data for dashboard snapshot", err)
}
dashboardID := dashboard.Get("id").MustInt64()
guardian := guardian.New(dashboardID, c.OrgId, c.SignedInUser)
canEdit, err := guardian.CanEdit()
if err != nil {
return Error(500, "Error while checking permissions for snapshot", err)
return response.Error(500, "Error while checking permissions for snapshot", err)
}
if !canEdit && query.Result.UserId != c.SignedInUser.UserId {
return Error(403, "Access denied to this snapshot", nil)
return response.Error(403, "Access denied to this snapshot", nil)
}
if query.Result.External {
err := deleteExternalDashboardSnapshot(query.Result.ExternalDeleteUrl)
if err != nil {
return Error(500, "Failed to delete external dashboard", err)
return response.Error(500, "Failed to delete external dashboard", err)
}
}
cmd := &models.DeleteDashboardSnapshotCommand{DeleteKey: query.Result.DeleteKey}
if err := bus.Dispatch(cmd); err != nil {
return Error(500, "Failed to delete dashboard snapshot", err)
return response.Error(500, "Failed to delete dashboard snapshot", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Snapshot deleted. It might take an hour before it's cleared from any CDN caches.",
"id": query.Result.Id,
})
}
// GET /api/dashboard/snapshots
func SearchDashboardSnapshots(c *models.ReqContext) Response {
func SearchDashboardSnapshots(c *models.ReqContext) response.Response {
query := c.Query("query")
limit := c.QueryInt("limit")
@@ -303,7 +304,7 @@ func SearchDashboardSnapshots(c *models.ReqContext) Response {
err := bus.Dispatch(&searchQuery)
if err != nil {
return Error(500, "Search failed", err)
return response.Error(500, "Search failed", err)
}
dtos := make([]*models.DashboardSnapshotDTO, len(searchQuery.Result))
@@ -322,5 +323,5 @@ func SearchDashboardSnapshots(c *models.ReqContext) Response {
}
}
return JSON(200, dtos)
return response.JSON(200, dtos)
}

View File

@@ -8,6 +8,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
@@ -61,9 +63,9 @@ func TestGetHomeDashboard(t *testing.T) {
require.NoError(t, err, "must be able to marshal object to JSON")
res := hs.GetHomeDashboard(req)
nr, ok := res.(*NormalResponse)
nr, ok := res.(*response.NormalResponse)
require.True(t, ok, "should return *NormalResponse")
require.Equal(t, b, nr.body, "default home dashboard should equal content on disk")
require.Equal(t, b, nr.Body(), "default home dashboard should equal content on disk")
})
}
}
@@ -1192,7 +1194,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
}
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId}
@@ -1223,7 +1225,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
defer bus.ClearBusHandlers()
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{
OrgId: testOrgID,
@@ -1255,7 +1257,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
}
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{
OrgId: testOrgID,

View File

@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/datasource"
"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/infra/log"
"github.com/grafana/grafana/pkg/models"
@@ -19,11 +20,11 @@ import (
var datasourcesLogger = log.New("datasources")
func (hs *HTTPServer) GetDataSources(c *models.ReqContext) Response {
func (hs *HTTPServer) GetDataSources(c *models.ReqContext) response.Response {
query := models.GetDataSourcesQuery{OrgId: c.OrgId, DataSourceLimit: hs.Cfg.DataSourceLimit}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to query datasources", err)
return response.Error(500, "Failed to query datasources", err)
}
result := make(dtos.DataSourceList, 0)
@@ -56,10 +57,10 @@ func (hs *HTTPServer) GetDataSources(c *models.ReqContext) Response {
sort.Sort(result)
return JSON(200, &result)
return response.JSON(200, &result)
}
func GetDataSourceById(c *models.ReqContext) Response {
func GetDataSourceById(c *models.ReqContext) response.Response {
query := models.GetDataSourceQuery{
Id: c.ParamsInt64(":id"),
OrgId: c.OrgId,
@@ -67,135 +68,135 @@ func GetDataSourceById(c *models.ReqContext) Response {
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(500, "Failed to query datasources", err)
return response.Error(500, "Failed to query datasources", err)
}
ds := query.Result
dtos := convertModelToDtos(ds)
return JSON(200, &dtos)
return response.JSON(200, &dtos)
}
func DeleteDataSourceById(c *models.ReqContext) Response {
func DeleteDataSourceById(c *models.ReqContext) response.Response {
id := c.ParamsInt64(":id")
if id <= 0 {
return Error(400, "Missing valid datasource id", nil)
return response.Error(400, "Missing valid datasource id", nil)
}
ds, err := getRawDataSourceById(id, c.OrgId)
if err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(400, "Failed to delete datasource", nil)
return response.Error(400, "Failed to delete datasource", nil)
}
if ds.ReadOnly {
return Error(403, "Cannot delete read-only data source", nil)
return response.Error(403, "Cannot delete read-only data source", nil)
}
cmd := &models.DeleteDataSourceCommand{ID: id, OrgID: c.OrgId}
err = bus.Dispatch(cmd)
if err != nil {
return Error(500, "Failed to delete datasource", err)
return response.Error(500, "Failed to delete datasource", err)
}
return Success("Data source deleted")
return response.Success("Data source deleted")
}
// GET /api/datasources/uid/:uid
func GetDataSourceByUID(c *models.ReqContext) Response {
func GetDataSourceByUID(c *models.ReqContext) response.Response {
ds, err := getRawDataSourceByUID(c.Params(":uid"), c.OrgId)
if err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(500, "Failed to query datasources", err)
return response.Error(500, "Failed to query datasources", err)
}
dtos := convertModelToDtos(ds)
return JSON(200, &dtos)
return response.JSON(200, &dtos)
}
// DELETE /api/datasources/uid/:uid
func DeleteDataSourceByUID(c *models.ReqContext) Response {
func DeleteDataSourceByUID(c *models.ReqContext) response.Response {
uid := c.Params(":uid")
if uid == "" {
return Error(400, "Missing datasource uid", nil)
return response.Error(400, "Missing datasource uid", nil)
}
ds, err := getRawDataSourceByUID(uid, c.OrgId)
if err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(400, "Failed to delete datasource", nil)
return response.Error(400, "Failed to delete datasource", nil)
}
if ds.ReadOnly {
return Error(403, "Cannot delete read-only data source", nil)
return response.Error(403, "Cannot delete read-only data source", nil)
}
cmd := &models.DeleteDataSourceCommand{UID: uid, OrgID: c.OrgId}
err = bus.Dispatch(cmd)
if err != nil {
return Error(500, "Failed to delete datasource", err)
return response.Error(500, "Failed to delete datasource", err)
}
return Success("Data source deleted")
return response.Success("Data source deleted")
}
func DeleteDataSourceByName(c *models.ReqContext) Response {
func DeleteDataSourceByName(c *models.ReqContext) response.Response {
name := c.Params(":name")
if name == "" {
return Error(400, "Missing valid datasource name", nil)
return response.Error(400, "Missing valid datasource name", nil)
}
getCmd := &models.GetDataSourceQuery{Name: name, OrgId: c.OrgId}
if err := bus.Dispatch(getCmd); err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(500, "Failed to delete datasource", err)
return response.Error(500, "Failed to delete datasource", err)
}
if getCmd.Result.ReadOnly {
return Error(403, "Cannot delete read-only data source", nil)
return response.Error(403, "Cannot delete read-only data source", nil)
}
cmd := &models.DeleteDataSourceCommand{Name: name, OrgID: c.OrgId}
err := bus.Dispatch(cmd)
if err != nil {
return Error(500, "Failed to delete datasource", err)
return response.Error(500, "Failed to delete datasource", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Data source deleted",
"id": getCmd.Result.Id,
})
}
func validateURL(tp string, u string) Response {
func validateURL(tp string, u string) response.Response {
if u != "" {
if _, err := datasource.ValidateURL(tp, u); err != nil {
datasourcesLogger.Error("Received invalid data source URL as part of data source command",
"url", u)
return Error(400, fmt.Sprintf("Validation error, invalid URL: %q", u), err)
return response.Error(400, fmt.Sprintf("Validation error, invalid URL: %q", u), err)
}
}
return nil
}
func AddDataSource(c *models.ReqContext, cmd models.AddDataSourceCommand) Response {
func AddDataSource(c *models.ReqContext, cmd models.AddDataSourceCommand) response.Response {
datasourcesLogger.Debug("Received command to add data source", "url", cmd.Url)
cmd.OrgId = c.OrgId
if resp := validateURL(cmd.Type, cmd.Url); resp != nil {
@@ -204,14 +205,14 @@ func AddDataSource(c *models.ReqContext, cmd models.AddDataSourceCommand) Respon
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrDataSourceNameExists) || errors.Is(err, models.ErrDataSourceUidExists) {
return Error(409, err.Error(), err)
return response.Error(409, err.Error(), err)
}
return Error(500, "Failed to add datasource", err)
return response.Error(500, "Failed to add datasource", err)
}
ds := convertModelToDtos(cmd.Result)
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Datasource added",
"id": cmd.Result.Id,
"name": cmd.Result.Name,
@@ -219,7 +220,7 @@ func AddDataSource(c *models.ReqContext, cmd models.AddDataSourceCommand) Respon
})
}
func UpdateDataSource(c *models.ReqContext, cmd models.UpdateDataSourceCommand) Response {
func UpdateDataSource(c *models.ReqContext, cmd models.UpdateDataSourceCommand) response.Response {
datasourcesLogger.Debug("Received command to update data source", "url", cmd.Url)
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":id")
@@ -229,15 +230,15 @@ func UpdateDataSource(c *models.ReqContext, cmd models.UpdateDataSourceCommand)
err := fillWithSecureJSONData(&cmd)
if err != nil {
return Error(500, "Failed to update datasource", err)
return response.Error(500, "Failed to update datasource", err)
}
err = bus.Dispatch(&cmd)
if err != nil {
if errors.Is(err, models.ErrDataSourceUpdatingOldVersion) {
return Error(500, "Failed to update datasource. Reload new version and try again", err)
return response.Error(500, "Failed to update datasource. Reload new version and try again", err)
}
return Error(500, "Failed to update datasource", err)
return response.Error(500, "Failed to update datasource", err)
}
query := models.GetDataSourceQuery{
@@ -247,14 +248,14 @@ func UpdateDataSource(c *models.ReqContext, cmd models.UpdateDataSourceCommand)
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(500, "Failed to query datasources", err)
return response.Error(500, "Failed to query datasources", err)
}
dtos := convertModelToDtos(query.Result)
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Datasource updated",
"id": cmd.Id,
"name": cmd.Name,
@@ -313,29 +314,29 @@ func getRawDataSourceByUID(uid string, orgID int64) (*models.DataSource, error)
}
// Get /api/datasources/name/:name
func GetDataSourceByName(c *models.ReqContext) Response {
func GetDataSourceByName(c *models.ReqContext) response.Response {
query := models.GetDataSourceQuery{Name: c.Params(":name"), OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(500, "Failed to query datasources", err)
return response.Error(500, "Failed to query datasources", err)
}
dtos := convertModelToDtos(query.Result)
return JSON(200, &dtos)
return response.JSON(200, &dtos)
}
// Get /api/datasources/id/:name
func GetDataSourceIdByName(c *models.ReqContext) Response {
func GetDataSourceIdByName(c *models.ReqContext) response.Response {
query := models.GetDataSourceQuery{Name: c.Params(":name"), OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(404, "Data source not found", nil)
return response.Error(404, "Data source not found", nil)
}
return Error(500, "Failed to query datasources", err)
return response.Error(500, "Failed to query datasources", err)
}
ds := query.Result
@@ -343,7 +344,7 @@ func GetDataSourceIdByName(c *models.ReqContext) Response {
Id: ds.Id,
}
return JSON(200, &dtos)
return response.JSON(200, &dtos)
}
// /api/datasources/:id/resources/*
@@ -414,25 +415,25 @@ func convertModelToDtos(ds *models.DataSource) dtos.DataSource {
// CheckDatasourceHealth sends a health check request to the plugin datasource
// /api/datasource/:id/health
func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) Response {
func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) response.Response {
datasourceID := c.ParamsInt64("id")
ds, err := hs.DatasourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
if err != nil {
if errors.Is(err, models.ErrDataSourceAccessDenied) {
return Error(403, "Access denied to datasource", err)
return response.Error(403, "Access denied to datasource", err)
}
return Error(500, "Unable to load datasource metadata", err)
return response.Error(500, "Unable to load datasource metadata", err)
}
plugin, ok := hs.PluginManager.GetDatasource(ds.Type)
if !ok {
return Error(500, "Unable to find datasource plugin", err)
return response.Error(500, "Unable to find datasource plugin", err)
}
dsInstanceSettings, err := wrapper.ModelToInstanceSettings(ds)
if err != nil {
return Error(500, "Unable to get datasource model", err)
return response.Error(500, "Unable to get datasource model", err)
}
pCtx := backend.PluginContext{
User: wrapper.BackendUserFromSignedInUser(c.SignedInUser),
@@ -456,15 +457,15 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) Response {
var jsonDetails map[string]interface{}
err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
if err != nil {
return Error(500, "Failed to unmarshal detailed response from backend plugin", err)
return response.Error(500, "Failed to unmarshal detailed response from backend plugin", err)
}
payload["details"] = jsonDetails
}
if resp.Status != backend.HealthStatusOk {
return JSON(503, payload)
return response.JSON(503, payload)
}
return JSON(200, payload)
return response.JSON(200, payload)
}

View File

@@ -4,6 +4,8 @@ import (
"encoding/json"
"testing"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
@@ -61,7 +63,7 @@ func TestAddDataSource_InvalidURL(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources")
sc.m.Post(sc.url, Wrap(func(c *models.ReqContext) Response {
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: "Test",
Url: "invalid:url",
@@ -91,7 +93,7 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources")
sc.m.Post(sc.url, Wrap(func(c *models.ReqContext) Response {
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: name,
Url: url,
@@ -109,7 +111,7 @@ func TestUpdateDataSource_InvalidURL(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources/1234")
sc.m.Put(sc.url, Wrap(func(c *models.ReqContext) Response {
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: "Test",
Url: "invalid:url",
@@ -139,7 +141,7 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources/1234")
sc.m.Put(sc.url, Wrap(func(c *models.ReqContext) Response {
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
return AddDataSource(c, models.AddDataSourceCommand{
Name: name,
Url: url,

View File

@@ -5,13 +5,14 @@ import (
"fmt"
"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/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
)
func GetFolders(c *models.ReqContext) Response {
func GetFolders(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folders, err := s.GetFolders(c.QueryInt64("limit"))
@@ -29,10 +30,10 @@ func GetFolders(c *models.ReqContext) Response {
})
}
return JSON(200, result)
return response.JSON(200, result)
}
func GetFolderByUID(c *models.ReqContext) Response {
func GetFolderByUID(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderByUID(c.Params(":uid"))
@@ -41,10 +42,10 @@ func GetFolderByUID(c *models.ReqContext) Response {
}
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
return JSON(200, toFolderDto(g, folder))
return response.JSON(200, toFolderDto(g, folder))
}
func GetFolderByID(c *models.ReqContext) Response {
func GetFolderByID(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderByID(c.ParamsInt64(":id"))
if err != nil {
@@ -52,10 +53,10 @@ func GetFolderByID(c *models.ReqContext) Response {
}
g := guardian.New(folder.Id, c.OrgId, c.SignedInUser)
return JSON(200, toFolderDto(g, folder))
return response.JSON(200, toFolderDto(g, folder))
}
func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolderCommand) Response {
func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolderCommand) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
err := s.CreateFolder(&cmd)
if err != nil {
@@ -69,10 +70,10 @@ func (hs *HTTPServer) CreateFolder(c *models.ReqContext, cmd models.CreateFolder
}
g := guardian.New(cmd.Result.Id, c.OrgId, c.SignedInUser)
return JSON(200, toFolderDto(g, cmd.Result))
return response.JSON(200, toFolderDto(g, cmd.Result))
}
func UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) Response {
func UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
err := s.UpdateFolder(c.Params(":uid"), &cmd)
if err != nil {
@@ -80,17 +81,17 @@ func UpdateFolder(c *models.ReqContext, cmd models.UpdateFolderCommand) Response
}
g := guardian.New(cmd.Result.Id, c.OrgId, c.SignedInUser)
return JSON(200, toFolderDto(g, cmd.Result))
return response.JSON(200, toFolderDto(g, cmd.Result))
}
func DeleteFolder(c *models.ReqContext) Response {
func DeleteFolder(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
f, err := s.DeleteFolder(c.Params(":uid"))
if err != nil {
return toFolderError(err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"title": f.Title,
"message": fmt.Sprintf("Folder %s deleted", f.Title),
"id": f.Id,
@@ -128,10 +129,10 @@ func toFolderDto(g guardian.DashboardGuardian, folder *models.Folder) dtos.Folde
}
}
func toFolderError(err error) Response {
func toFolderError(err error) response.Response {
var dashboardErr models.DashboardErr
if ok := errors.As(err, &dashboardErr); ok {
return Error(dashboardErr.StatusCode, err.Error(), err)
return response.Error(dashboardErr.StatusCode, err.Error(), err)
}
if errors.Is(err, models.ErrFolderTitleEmpty) ||
@@ -140,20 +141,20 @@ func toFolderError(err error) Response {
errors.Is(err, models.ErrDashboardTypeMismatch) ||
errors.Is(err, models.ErrDashboardInvalidUid) ||
errors.Is(err, models.ErrDashboardUidTooLong) {
return Error(400, err.Error(), nil)
return response.Error(400, err.Error(), nil)
}
if errors.Is(err, models.ErrFolderAccessDenied) {
return Error(403, "Access denied", err)
return response.Error(403, "Access denied", err)
}
if errors.Is(err, models.ErrFolderNotFound) {
return JSON(404, util.DynMap{"status": "not-found", "message": models.ErrFolderNotFound.Error()})
return response.JSON(404, util.DynMap{"status": "not-found", "message": models.ErrFolderNotFound.Error()})
}
if errors.Is(err, models.ErrFolderVersionMismatch) {
return JSON(412, util.DynMap{"status": "version-mismatch", "message": models.ErrFolderVersionMismatch.Error()})
return response.JSON(412, util.DynMap{"status": "version-mismatch", "message": models.ErrFolderVersionMismatch.Error()})
}
return Error(500, "Folder API error", err)
return response.Error(500, "Folder API error", err)
}

View File

@@ -5,6 +5,7 @@ import (
"time"
"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/services/dashboards"
@@ -12,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/util"
)
func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) Response {
func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) response.Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderByUID(c.Params(":uid"))
@@ -28,7 +29,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) Response {
acl, err := g.GetAcl()
if err != nil {
return Error(500, "Failed to get folder permissions", err)
return response.Error(500, "Failed to get folder permissions", err)
}
filteredAcls := make([]*models.DashboardAclInfoDTO, 0, len(acl))
@@ -53,12 +54,12 @@ func (hs *HTTPServer) GetFolderPermissionList(c *models.ReqContext) Response {
filteredAcls = append(filteredAcls, perm)
}
return JSON(200, filteredAcls)
return response.JSON(200, filteredAcls)
}
func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) response.Response {
if err := validatePermissionsUpdate(apiCmd); err != nil {
return Error(400, err.Error(), err)
return response.Error(400, err.Error(), err)
}
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
@@ -96,7 +97,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
hiddenACL, err := g.GetHiddenACL(hs.Cfg)
if err != nil {
return Error(500, "Error while retrieving hidden permissions", err)
return response.Error(500, "Error while retrieving hidden permissions", err)
}
cmd.Items = append(cmd.Items, hiddenACL...)
@@ -104,13 +105,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
if err != nil {
if errors.Is(err, guardian.ErrGuardianPermissionExists) ||
errors.Is(err, guardian.ErrGuardianOverride) {
return Error(400, err.Error(), err)
return response.Error(400, err.Error(), err)
}
return Error(500, "Error while checking folder permissions", err)
return response.Error(500, "Error while checking folder permissions", err)
}
return Error(403, "Cannot remove own admin permission for a folder", nil)
return response.Error(403, "Cannot remove own admin permission for a folder", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
@@ -122,13 +123,13 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext, apiCmd dtos.
}
if errors.Is(err, models.ErrFolderAclInfoMissing) || errors.Is(err, models.ErrFolderPermissionFolderEmpty) {
return Error(409, err.Error(), err)
return response.Error(409, err.Error(), err)
}
return Error(500, "Failed to create permission", err)
return response.Error(500, "Failed to create permission", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "Folder permissions updated",
"id": folder.Id,
"title": folder.Title,

View File

@@ -6,6 +6,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
@@ -384,7 +386,7 @@ func updateFolderPermissionScenario(t *testing.T, ctx updatePermissionContext, h
sc := setupScenarioContext(t, ctx.url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.OrgId = testOrgID
sc.context.UserId = testUserID

View File

@@ -6,6 +6,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
@@ -143,7 +145,7 @@ func createFolderScenario(t *testing.T, desc string, url string, routePattern st
}
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
@@ -173,7 +175,7 @@ func updateFolderScenario(t *testing.T, desc string, url string, routePattern st
defer bus.ClearBusHandlers()
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}

View File

@@ -5,6 +5,7 @@ import (
"strings"
"github.com/getsentry/sentry-go"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/inconshreveable/log15"
@@ -77,7 +78,7 @@ func (event *frontendSentryEvent) ToLogContext() log15.Ctx {
return ctx
}
func (hs *HTTPServer) logFrontendMessage(c *models.ReqContext, event frontendSentryEvent) Response {
func (hs *HTTPServer) logFrontendMessage(c *models.ReqContext, event frontendSentryEvent) response.Response {
var msg = "unknown"
if len(event.Message) > 0 {
@@ -99,5 +100,5 @@ func (hs *HTTPServer) logFrontendMessage(c *models.ReqContext, event frontendSen
frontendLogger.Info(msg, ctx)
}
return Success("ok")
return response.Success("ok")
}

View File

@@ -6,6 +6,8 @@ import (
"time"
"github.com/getsentry/sentry-go"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/models"
log "github.com/inconshreveable/log15"
@@ -30,7 +32,7 @@ func logSentryEventScenario(t *testing.T, desc string, event frontendSentryEvent
sc := setupScenarioContext(t, "/log")
hs := HTTPServer{}
handler := Wrap(func(w http.ResponseWriter, c *models.ReqContext) Response {
handler := routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) response.Response {
sc.context = c
return hs.logFrontendMessage(c, event)
})

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"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/login"
@@ -97,38 +98,38 @@ func (user *LDAPUserDTO) FetchOrgs() error {
}
// ReloadLDAPCfg reloads the LDAP configuration
func (hs *HTTPServer) ReloadLDAPCfg() Response {
func (hs *HTTPServer) ReloadLDAPCfg() response.Response {
if !ldap.IsEnabled() {
return Error(http.StatusBadRequest, "LDAP is not enabled", nil)
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
err := ldap.ReloadConfig()
if err != nil {
return Error(http.StatusInternalServerError, "Failed to reload LDAP config", err)
return response.Error(http.StatusInternalServerError, "Failed to reload LDAP config", err)
}
return Success("LDAP config reloaded")
return response.Success("LDAP config reloaded")
}
// GetLDAPStatus attempts to connect to all the configured LDAP servers and returns information on whenever they're available or not.
func (hs *HTTPServer) GetLDAPStatus(c *models.ReqContext) Response {
func (hs *HTTPServer) GetLDAPStatus(c *models.ReqContext) response.Response {
if !ldap.IsEnabled() {
return Error(http.StatusBadRequest, "LDAP is not enabled", nil)
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
ldapConfig, err := getLDAPConfig(hs.Cfg)
if err != nil {
return Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
}
ldap := newLDAP(ldapConfig.Servers)
if ldap == nil {
return Error(http.StatusInternalServerError, "Failed to find the LDAP server", nil)
return response.Error(http.StatusInternalServerError, "Failed to find the LDAP server", nil)
}
statuses, err := ldap.Ping()
if err != nil {
return Error(http.StatusBadRequest, "Failed to connect to the LDAP server(s)", err)
return response.Error(http.StatusBadRequest, "Failed to connect to the LDAP server(s)", err)
}
serverDTOs := []*LDAPServerDTO{}
@@ -146,18 +147,18 @@ func (hs *HTTPServer) GetLDAPStatus(c *models.ReqContext) Response {
serverDTOs = append(serverDTOs, s)
}
return JSON(http.StatusOK, serverDTOs)
return response.JSON(http.StatusOK, serverDTOs)
}
// PostSyncUserWithLDAP enables a single Grafana user to be synchronized against LDAP
func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) Response {
func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) response.Response {
if !ldap.IsEnabled() {
return Error(http.StatusBadRequest, "LDAP is not enabled", nil)
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
ldapConfig, err := getLDAPConfig(hs.Cfg)
if err != nil {
return Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
}
userId := c.ParamsInt64(":id")
@@ -166,20 +167,20 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) Response {
if err := bus.Dispatch(&query); err != nil { // validate the userId exists
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to get user", err)
return response.Error(500, "Failed to get user", err)
}
authModuleQuery := &models.GetAuthInfoQuery{UserId: query.Result.Id, AuthModule: models.AuthModuleLDAP}
if err := bus.Dispatch(authModuleQuery); err != nil { // validate the userId comes from LDAP
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to get user", err)
return response.Error(500, "Failed to get user", err)
}
ldapServer := newLDAP(ldapConfig.Servers)
@@ -189,25 +190,25 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) Response {
if hs.Cfg.AdminUser == query.Result.Login { // User is *the* Grafana Admin. We cannot disable it.
errMsg := fmt.Sprintf(`Refusing to sync grafana super admin "%s" - it would be disabled`, query.Result.Login)
ldapLogger.Error(errMsg)
return Error(http.StatusBadRequest, errMsg, err)
return response.Error(http.StatusBadRequest, errMsg, err)
}
// Since the user was not in the LDAP server. Let's disable it.
err := login.DisableExternalUser(query.Result.Login)
if err != nil {
return Error(http.StatusInternalServerError, "Failed to disable the user", err)
return response.Error(http.StatusInternalServerError, "Failed to disable the user", err)
}
err = hs.AuthTokenService.RevokeAllUserTokens(c.Req.Context(), userId)
if err != nil {
return Error(http.StatusInternalServerError, "Failed to remove session tokens for the user", err)
return response.Error(http.StatusInternalServerError, "Failed to remove session tokens for the user", err)
}
return Error(http.StatusBadRequest, "User not found in LDAP. Disabled the user without updating information", nil) // should this be a success?
return response.Error(http.StatusBadRequest, "User not found in LDAP. Disabled the user without updating information", nil) // should this be a success?
}
ldapLogger.Debug("Failed to sync the user with LDAP", "err", err)
return Error(http.StatusBadRequest, "Something went wrong while finding the user in LDAP", err)
return response.Error(http.StatusBadRequest, "Something went wrong while finding the user in LDAP", err)
}
upsertCmd := &models.UpsertUserCommand{
@@ -218,21 +219,21 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) Response {
err = bus.Dispatch(upsertCmd)
if err != nil {
return Error(http.StatusInternalServerError, "Failed to update the user", err)
return response.Error(http.StatusInternalServerError, "Failed to update the user", err)
}
return Success("User synced successfully")
return response.Success("User synced successfully")
}
// GetUserFromLDAP finds an user based on a username in LDAP. This helps illustrate how would the particular user be mapped in Grafana when synced.
func (hs *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
func (hs *HTTPServer) GetUserFromLDAP(c *models.ReqContext) response.Response {
if !ldap.IsEnabled() {
return Error(http.StatusBadRequest, "LDAP is not enabled", nil)
return response.Error(http.StatusBadRequest, "LDAP is not enabled", nil)
}
ldapConfig, err := getLDAPConfig(hs.Cfg)
if err != nil {
return Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration", err)
return response.Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration", err)
}
ldap := newLDAP(ldapConfig.Servers)
@@ -240,13 +241,13 @@ func (hs *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
username := c.Params(":username")
if len(username) == 0 {
return Error(http.StatusBadRequest, "Validation error. You must specify an username", nil)
return response.Error(http.StatusBadRequest, "Validation error. You must specify an username", nil)
}
user, serverConfig, err := ldap.User(username)
if user == nil {
return Error(http.StatusNotFound, "No user was found in the LDAP server(s) with that username", err)
return response.Error(http.StatusNotFound, "No user was found in the LDAP server(s) with that username", err)
}
ldapLogger.Debug("user found", "user", user)
@@ -301,18 +302,18 @@ func (hs *HTTPServer) GetUserFromLDAP(c *models.ReqContext) Response {
ldapLogger.Debug("mapping org roles", "orgsRoles", u.OrgRoles)
err = u.FetchOrgs()
if err != nil {
return Error(http.StatusBadRequest, "An organization was not found - Please verify your LDAP configuration", err)
return response.Error(http.StatusBadRequest, "An organization was not found - Please verify your LDAP configuration", err)
}
cmd := &models.GetTeamsForLDAPGroupCommand{Groups: user.Groups}
err = bus.Dispatch(cmd)
if err != nil && !errors.Is(err, bus.ErrHandlerNotFound) {
return Error(http.StatusBadRequest, "Unable to find the teams for this user", err)
return response.Error(http.StatusBadRequest, "Unable to find the teams for this user", err)
}
u.Teams = cmd.Result
return JSON(200, u)
return response.JSON(200, u)
}
// splitName receives the full name of a user and splits it into two parts: A name and a surname.

View File

@@ -6,6 +6,8 @@ import (
"net/http/httptest"
"testing"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
@@ -58,7 +60,7 @@ func getUserFromLDAPContext(t *testing.T, requestURL string) *scenarioContext {
hs := &HTTPServer{Cfg: setting.NewCfg()}
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
return hs.GetUserFromLDAP(c)
})
@@ -327,7 +329,7 @@ func getLDAPStatusContext(t *testing.T) *scenarioContext {
hs := &HTTPServer{Cfg: setting.NewCfg()}
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
return hs.GetLDAPStatus(c)
})
@@ -391,7 +393,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(*
AuthTokenService: auth.NewFakeUserAuthTokenService(),
}
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
return hs.PostSyncUserWithLDAP(c)
})

View File

@@ -8,6 +8,7 @@ import (
"strings"
"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/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
@@ -159,36 +160,36 @@ func tryOAuthAutoLogin(c *models.ReqContext) bool {
return false
}
func (hs *HTTPServer) LoginAPIPing(c *models.ReqContext) Response {
func (hs *HTTPServer) LoginAPIPing(c *models.ReqContext) response.Response {
if c.IsSignedIn || c.IsAnonymous {
return JSON(200, "Logged in")
return response.JSON(200, "Logged in")
}
return Error(401, "Unauthorized", nil)
return response.Error(401, "Unauthorized", nil)
}
func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Response {
func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) response.Response {
authModule := ""
var user *models.User
var response *NormalResponse
var resp *response.NormalResponse
defer func() {
err := response.err
if err == nil && response.errMessage != "" {
err = errors.New(response.errMessage)
err := resp.Err()
if err == nil && resp.ErrMessage() != "" {
err = errors.New(resp.ErrMessage())
}
hs.HooksService.RunLoginHook(&models.LoginInfo{
AuthModule: authModule,
User: user,
LoginUsername: cmd.User,
HTTPStatus: response.status,
HTTPStatus: resp.Status(),
Error: err,
}, c)
}()
if setting.DisableLoginForm {
response = Error(http.StatusUnauthorized, "Login is disabled", nil)
return response
resp = response.Error(http.StatusUnauthorized, "Login is disabled", nil)
return resp
}
authQuery := &models.LoginUserQuery{
@@ -202,29 +203,29 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Res
err := bus.Dispatch(authQuery)
authModule = authQuery.AuthModule
if err != nil {
response = Error(401, "Invalid username or password", err)
resp = response.Error(401, "Invalid username or password", err)
if errors.Is(err, login.ErrInvalidCredentials) || errors.Is(err, login.ErrTooManyLoginAttempts) || errors.Is(err,
models.ErrUserNotFound) {
return response
return resp
}
// Do not expose disabled status,
// just show incorrect user credentials error (see #17947)
if errors.Is(err, login.ErrUserDisabled) {
hs.log.Warn("User is disabled", "user", cmd.User)
return response
return resp
}
response = Error(500, "Error while trying to authenticate user", err)
return response
resp = response.Error(500, "Error while trying to authenticate user", err)
return resp
}
user = authQuery.User
err = hs.loginUserWithUser(user, c)
if err != nil {
response = Error(http.StatusInternalServerError, "Error while signing in user", err)
return response
resp = response.Error(http.StatusInternalServerError, "Error while signing in user", err)
return resp
}
result := map[string]interface{}{
@@ -241,8 +242,8 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Res
}
metrics.MApiLoginPost.Inc()
response = JSON(http.StatusOK, result)
return response
resp = response.JSON(http.StatusOK, result)
return resp
}
func (hs *HTTPServer) loginUserWithUser(user *models.User, c *models.ReqContext) error {
@@ -324,11 +325,11 @@ func (hs *HTTPServer) redirectWithError(ctx *models.ReqContext, err error, v ...
ctx.Redirect(setting.AppSubUrl + "/login")
}
func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err error, v ...interface{}) *RedirectResponse {
func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err error, v ...interface{}) *response.RedirectResponse {
ctx.Logger.Error(err.Error(), v...)
if err := hs.trySetEncryptedCookie(ctx, loginErrorCookieName, err.Error(), 60); err != nil {
hs.log.Error("Failed to set encrypted cookie", "err", err)
}
return Redirect(setting.AppSubUrl + "/login")
return response.Redirect(setting.AppSubUrl + "/login")
}

View File

@@ -11,6 +11,8 @@ import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
@@ -96,7 +98,7 @@ func TestLoginErrorCookieAPIEndpoint(t *testing.T) {
License: &licensing.OSSLicensingService{},
}
sc.defaultHandler = Wrap(func(w http.ResponseWriter, c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) {
hs.LoginView(c)
})
@@ -158,7 +160,7 @@ func TestLoginViewRedirect(t *testing.T) {
}
hs.Cfg.CookieSecure = true
sc.defaultHandler = Wrap(func(w http.ResponseWriter, c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) {
c.IsSignedIn = true
c.SignedInUser = &models.SignedInUser{
UserId: 10,
@@ -335,7 +337,7 @@ func TestLoginPostRedirect(t *testing.T) {
}
hs.Cfg.CookieSecure = true
sc.defaultHandler = Wrap(func(w http.ResponseWriter, c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) response.Response {
cmd := dtos.LoginCommand{
User: "admin",
Password: "admin",
@@ -488,7 +490,7 @@ func TestLoginOAuthRedirect(t *testing.T) {
License: &licensing.OSSLicensingService{},
}
sc.defaultHandler = Wrap(func(c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) {
hs.LoginView(c)
})
@@ -522,7 +524,7 @@ func TestLoginInternal(t *testing.T) {
log: log.New("test"),
}
sc.defaultHandler = Wrap(func(c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) {
c.Req.URL.RawQuery = "disableAutoLogin=true"
hs.LoginView(c)
})
@@ -580,7 +582,7 @@ func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioConte
log: log.New("hello"),
}
sc.defaultHandler = Wrap(func(c *models.ReqContext) {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) {
c.IsSignedIn = true
c.SignedInUser = &models.SignedInUser{
UserId: 10,
@@ -618,7 +620,7 @@ func TestLoginPostRunLokingHook(t *testing.T) {
HooksService: hookService,
}
sc.defaultHandler = Wrap(func(w http.ResponseWriter, c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(w http.ResponseWriter, c *models.ReqContext) response.Response {
cmd := dtos.LoginCommand{
User: "admin",
Password: "admin",

View File

@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/tsdb"
@@ -18,9 +19,9 @@ import (
// QueryMetricsV2 returns query metrics.
// POST /api/ds/query DataSource query w/ expressions
func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricRequest) Response {
func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricRequest) response.Response {
if len(reqDTO.Queries) == 0 {
return Error(400, "No queries found in query", nil)
return response.Error(400, "No queries found in query", nil)
}
request := &tsdb.TsdbQuery{
@@ -41,7 +42,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
datasourceID, err := query.Get("datasourceId").Int64()
if err != nil {
hs.log.Debug("Can't process query since it's missing data source ID")
return Error(400, "Query missing data source ID", nil)
return response.Error(400, "Query missing data source ID", nil)
}
if i == 0 && !hasExpr {
@@ -49,12 +50,12 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
if err != nil {
hs.log.Debug("Encountered error getting data source", "err", err, "id", datasourceID)
if errors.Is(err, models.ErrDataSourceAccessDenied) {
return Error(403, "Access denied to data source", err)
return response.Error(403, "Access denied to data source", err)
}
if errors.Is(err, models.ErrDataSourceNotFound) {
return Error(400, "Invalid data source ID", err)
return response.Error(400, "Invalid data source ID", err)
}
return Error(500, "Unable to load data source metadata", err)
return response.Error(500, "Unable to load data source metadata", err)
}
}
@@ -73,16 +74,16 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
if !hasExpr {
resp, err = tsdb.HandleRequest(c.Req.Context(), ds, request)
if err != nil {
return Error(500, "Metric request error", err)
return response.Error(500, "Metric request error", err)
}
} else {
if !hs.Cfg.IsExpressionsEnabled() {
return Error(404, "Expressions feature toggle is not enabled", nil)
return response.Error(404, "Expressions feature toggle is not enabled", nil)
}
resp, err = expr.WrapTransformData(c.Req.Context(), request)
if err != nil {
return Error(500, "Transform request error", err)
return response.Error(500, "Transform request error", err)
}
}
@@ -95,29 +96,29 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
}
}
return jsonStreaming(statusCode, resp)
return response.JSONStreaming(statusCode, resp)
}
// QueryMetrics returns query metrics
// POST /api/tsdb/query
func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricRequest) Response {
func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricRequest) response.Response {
timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To)
if len(reqDto.Queries) == 0 {
return Error(400, "No queries found in query", nil)
return response.Error(400, "No queries found in query", nil)
}
datasourceId, err := reqDto.Queries[0].Get("datasourceId").Int64()
if err != nil {
return Error(400, "Query missing datasourceId", nil)
return response.Error(400, "Query missing datasourceId", nil)
}
ds, err := hs.DatasourceCache.GetDatasource(datasourceId, c.SignedInUser, c.SkipCache)
if err != nil {
if errors.Is(err, models.ErrDataSourceAccessDenied) {
return Error(403, "Access denied to datasource", err)
return response.Error(403, "Access denied to datasource", err)
}
return Error(500, "Unable to load datasource meta data", err)
return response.Error(500, "Unable to load datasource meta data", err)
}
request := &tsdb.TsdbQuery{
@@ -138,7 +139,7 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
resp, err := tsdb.HandleRequest(c.Req.Context(), ds, request)
if err != nil {
return Error(500, "Metric request error", err)
return response.Error(500, "Metric request error", err)
}
statusCode := 200
@@ -150,11 +151,11 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
}
}
return JSON(statusCode, &resp)
return response.JSON(statusCode, &resp)
}
// GET /api/tsdb/testdata/scenarios
func GetTestDataScenarios(c *models.ReqContext) Response {
func GetTestDataScenarios(c *models.ReqContext) response.Response {
result := make([]interface{}, 0)
scenarioIds := make([]string, 0)
@@ -173,27 +174,27 @@ func GetTestDataScenarios(c *models.ReqContext) Response {
})
}
return JSON(200, &result)
return response.JSON(200, &result)
}
// GenerateError generates a index out of range error
func GenerateError(c *models.ReqContext) Response {
func GenerateError(c *models.ReqContext) response.Response {
var array []string
// nolint: govet
return JSON(200, array[20])
return response.JSON(200, array[20])
}
// GET /api/tsdb/testdata/gensql
func GenerateSQLTestData(c *models.ReqContext) Response {
func GenerateSQLTestData(c *models.ReqContext) response.Response {
if err := bus.Dispatch(&models.InsertSQLTestDataCommand{}); err != nil {
return Error(500, "Failed to insert test data", err)
return response.Error(500, "Failed to insert test data", err)
}
return JSON(200, &util.DynMap{"message": "OK"})
return response.JSON(200, &util.DynMap{"message": "OK"})
}
// GET /api/tsdb/testdata/random-walk
func GetTestDataRandomWalk(c *models.ReqContext) Response {
func GetTestDataRandomWalk(c *models.ReqContext) response.Response {
from := c.Query("from")
to := c.Query("to")
intervalMs := c.QueryInt64("intervalMs")
@@ -213,8 +214,8 @@ func GetTestDataRandomWalk(c *models.ReqContext) Response {
resp, err := tsdb.HandleRequest(context.Background(), dsInfo, request)
if err != nil {
return Error(500, "Metric request error", err)
return response.Error(500, "Metric request error", err)
}
return JSON(200, &resp)
return response.JSON(200, &resp)
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"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/infra/metrics"
"github.com/grafana/grafana/pkg/models"
@@ -12,24 +13,24 @@ import (
)
// GET /api/org
func GetOrgCurrent(c *models.ReqContext) Response {
func GetOrgCurrent(c *models.ReqContext) response.Response {
return getOrgHelper(c.OrgId)
}
// GET /api/orgs/:orgId
func GetOrgByID(c *models.ReqContext) Response {
func GetOrgByID(c *models.ReqContext) response.Response {
return getOrgHelper(c.ParamsInt64(":orgId"))
}
// Get /api/orgs/name/:name
func (hs *HTTPServer) GetOrgByName(c *models.ReqContext) Response {
func (hs *HTTPServer) GetOrgByName(c *models.ReqContext) response.Response {
org, err := hs.SQLStore.GetOrgByName(c.Params(":name"))
if err != nil {
if errors.Is(err, models.ErrOrgNotFound) {
return Error(404, "Organization not found", err)
return response.Error(404, "Organization not found", err)
}
return Error(500, "Failed to get organization", err)
return response.Error(500, "Failed to get organization", err)
}
result := models.OrgDetailsDTO{
Id: org.Id,
@@ -44,18 +45,18 @@ func (hs *HTTPServer) GetOrgByName(c *models.ReqContext) Response {
},
}
return JSON(200, &result)
return response.JSON(200, &result)
}
func getOrgHelper(orgID int64) Response {
func getOrgHelper(orgID int64) response.Response {
query := models.GetOrgByIdQuery{Id: orgID}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrOrgNotFound) {
return Error(404, "Organization not found", err)
return response.Error(404, "Organization not found", err)
}
return Error(500, "Failed to get organization", err)
return response.Error(500, "Failed to get organization", err)
}
org := query.Result
@@ -72,64 +73,64 @@ func getOrgHelper(orgID int64) Response {
},
}
return JSON(200, &result)
return response.JSON(200, &result)
}
// POST /api/orgs
func CreateOrg(c *models.ReqContext, cmd models.CreateOrgCommand) Response {
func CreateOrg(c *models.ReqContext, cmd models.CreateOrgCommand) response.Response {
if !c.IsSignedIn || (!setting.AllowUserOrgCreate && !c.IsGrafanaAdmin) {
return Error(403, "Access denied", nil)
return response.Error(403, "Access denied", nil)
}
cmd.UserId = c.UserId
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrOrgNameTaken) {
return Error(409, "Organization name taken", err)
return response.Error(409, "Organization name taken", err)
}
return Error(500, "Failed to create organization", err)
return response.Error(500, "Failed to create organization", err)
}
metrics.MApiOrgCreate.Inc()
return JSON(200, &util.DynMap{
return response.JSON(200, &util.DynMap{
"orgId": cmd.Result.Id,
"message": "Organization created",
})
}
// PUT /api/org
func UpdateOrgCurrent(c *models.ReqContext, form dtos.UpdateOrgForm) Response {
func UpdateOrgCurrent(c *models.ReqContext, form dtos.UpdateOrgForm) response.Response {
return updateOrgHelper(form, c.OrgId)
}
// PUT /api/orgs/:orgId
func UpdateOrg(c *models.ReqContext, form dtos.UpdateOrgForm) Response {
func UpdateOrg(c *models.ReqContext, form dtos.UpdateOrgForm) response.Response {
return updateOrgHelper(form, c.ParamsInt64(":orgId"))
}
func updateOrgHelper(form dtos.UpdateOrgForm, orgID int64) Response {
func updateOrgHelper(form dtos.UpdateOrgForm, orgID int64) response.Response {
cmd := models.UpdateOrgCommand{Name: form.Name, OrgId: orgID}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrOrgNameTaken) {
return Error(400, "Organization name taken", err)
return response.Error(400, "Organization name taken", err)
}
return Error(500, "Failed to update organization", err)
return response.Error(500, "Failed to update organization", err)
}
return Success("Organization updated")
return response.Success("Organization updated")
}
// PUT /api/org/address
func UpdateOrgAddressCurrent(c *models.ReqContext, form dtos.UpdateOrgAddressForm) Response {
func UpdateOrgAddressCurrent(c *models.ReqContext, form dtos.UpdateOrgAddressForm) response.Response {
return updateOrgAddressHelper(form, c.OrgId)
}
// PUT /api/orgs/:orgId/address
func UpdateOrgAddress(c *models.ReqContext, form dtos.UpdateOrgAddressForm) Response {
func UpdateOrgAddress(c *models.ReqContext, form dtos.UpdateOrgAddressForm) response.Response {
return updateOrgAddressHelper(form, c.ParamsInt64(":orgId"))
}
func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgID int64) Response {
func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgID int64) response.Response {
cmd := models.UpdateOrgAddressCommand{
OrgId: orgID,
Address: models.Address{
@@ -143,24 +144,24 @@ func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgID int64) Respons
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update org address", err)
return response.Error(500, "Failed to update org address", err)
}
return Success("Address updated")
return response.Success("Address updated")
}
// GET /api/orgs/:orgId
func DeleteOrgByID(c *models.ReqContext) Response {
func DeleteOrgByID(c *models.ReqContext) response.Response {
if err := bus.Dispatch(&models.DeleteOrgCommand{Id: c.ParamsInt64(":orgId")}); err != nil {
if errors.Is(err, models.ErrOrgNotFound) {
return Error(404, "Failed to delete organization. ID not found", nil)
return response.Error(404, "Failed to delete organization. ID not found", nil)
}
return Error(500, "Failed to update organization", err)
return response.Error(500, "Failed to update organization", err)
}
return Success("Organization deleted")
return response.Success("Organization deleted")
}
func SearchOrgs(c *models.ReqContext) Response {
func SearchOrgs(c *models.ReqContext) response.Response {
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
@@ -176,8 +177,8 @@ func SearchOrgs(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to search orgs", err)
return response.Error(500, "Failed to search orgs", err)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"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/events"
"github.com/grafana/grafana/pkg/infra/metrics"
@@ -13,37 +14,37 @@ import (
"github.com/grafana/grafana/pkg/util"
)
func GetPendingOrgInvites(c *models.ReqContext) Response {
func GetPendingOrgInvites(c *models.ReqContext) response.Response {
query := models.GetTempUsersQuery{OrgId: c.OrgId, Status: models.TmpUserInvitePending}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get invites from db", err)
return response.Error(500, "Failed to get invites from db", err)
}
for _, invite := range query.Result {
invite.Url = setting.ToAbsUrl("invite/" + invite.Code)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
func AddOrgInvite(c *models.ReqContext, inviteDto dtos.AddInviteForm) Response {
func AddOrgInvite(c *models.ReqContext, inviteDto dtos.AddInviteForm) response.Response {
if !inviteDto.Role.IsValid() {
return Error(400, "Invalid role specified", nil)
return response.Error(400, "Invalid role specified", nil)
}
// first try get existing user
userQuery := models.GetUserByLoginQuery{LoginOrEmail: inviteDto.LoginOrEmail}
if err := bus.Dispatch(&userQuery); err != nil {
if !errors.Is(err, models.ErrUserNotFound) {
return Error(500, "Failed to query db for existing user check", err)
return response.Error(500, "Failed to query db for existing user check", err)
}
} else {
return inviteExistingUserToOrg(c, userQuery.Result, &inviteDto)
}
if setting.DisableLoginForm {
return Error(400, "Cannot invite when login is disabled.", nil)
return response.Error(400, "Cannot invite when login is disabled.", nil)
}
cmd := models.CreateTempUserCommand{}
@@ -55,13 +56,13 @@ func AddOrgInvite(c *models.ReqContext, inviteDto dtos.AddInviteForm) Response {
var err error
cmd.Code, err = util.GetRandomString(30)
if err != nil {
return Error(500, "Could not generate random string", err)
return response.Error(500, "Could not generate random string", err)
}
cmd.Role = inviteDto.Role
cmd.RemoteAddr = c.Req.RemoteAddr
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to save invite to database", err)
return response.Error(500, "Failed to save invite to database", err)
}
// send invite email
@@ -80,31 +81,31 @@ func AddOrgInvite(c *models.ReqContext, inviteDto dtos.AddInviteForm) Response {
if err := bus.Dispatch(&emailCmd); err != nil {
if errors.Is(err, models.ErrSmtpNotEnabled) {
return Error(412, err.Error(), err)
return response.Error(412, err.Error(), err)
}
return Error(500, "Failed to send email invite", err)
return response.Error(500, "Failed to send email invite", err)
}
emailSentCmd := models.UpdateTempUserWithEmailSentCommand{Code: cmd.Result.Code}
if err := bus.Dispatch(&emailSentCmd); err != nil {
return Error(500, "Failed to update invite with email sent info", err)
return response.Error(500, "Failed to update invite with email sent info", err)
}
return Success(fmt.Sprintf("Sent invite to %s", inviteDto.LoginOrEmail))
return response.Success(fmt.Sprintf("Sent invite to %s", inviteDto.LoginOrEmail))
}
return Success(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail))
return response.Success(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail))
}
func inviteExistingUserToOrg(c *models.ReqContext, user *models.User, inviteDto *dtos.AddInviteForm) Response {
func inviteExistingUserToOrg(c *models.ReqContext, user *models.User, inviteDto *dtos.AddInviteForm) response.Response {
// user exists, add org role
createOrgUserCmd := models.AddOrgUserCommand{OrgId: c.OrgId, UserId: user.Id, Role: inviteDto.Role}
if err := bus.Dispatch(&createOrgUserCmd); err != nil {
if errors.Is(err, models.ErrOrgUserAlreadyAdded) {
return Error(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err)
return response.Error(412, fmt.Sprintf("User %s is already added to organization", inviteDto.LoginOrEmail), err)
}
return Error(500, "Error while trying to create org user", err)
return response.Error(500, "Error while trying to create org user", err)
}
if inviteDto.SendEmail && util.IsEmail(user.Email) {
@@ -119,42 +120,42 @@ func inviteExistingUserToOrg(c *models.ReqContext, user *models.User, inviteDto
}
if err := bus.Dispatch(&emailCmd); err != nil {
return Error(500, "Failed to send email invited_to_org", err)
return response.Error(500, "Failed to send email invited_to_org", err)
}
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": fmt.Sprintf("Existing Grafana user %s added to org %s", user.NameOrFallback(), c.OrgName),
"userId": user.Id,
})
}
func RevokeInvite(c *models.ReqContext) Response {
func RevokeInvite(c *models.ReqContext) response.Response {
if ok, rsp := updateTempUserStatus(c.Params(":code"), models.TmpUserRevoked); !ok {
return rsp
}
return Success("Invite revoked")
return response.Success("Invite revoked")
}
// GetInviteInfoByCode gets a pending user invite corresponding to a certain code.
// A response containing an InviteInfo object is returned if the invite is found.
// If a (pending) invite is not found, 404 is returned.
func GetInviteInfoByCode(c *models.ReqContext) Response {
func GetInviteInfoByCode(c *models.ReqContext) response.Response {
query := models.GetTempUserByCodeQuery{Code: c.Params(":code")}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrTempUserNotFound) {
return Error(404, "Invite not found", nil)
return response.Error(404, "Invite not found", nil)
}
return Error(500, "Failed to get invite", err)
return response.Error(500, "Failed to get invite", err)
}
invite := query.Result
if invite.Status != models.TmpUserInvitePending {
return Error(404, "Invite not found", nil)
return response.Error(404, "Invite not found", nil)
}
return JSON(200, dtos.InviteInfo{
return response.JSON(200, dtos.InviteInfo{
Email: invite.Email,
Name: invite.Name,
Username: invite.Email,
@@ -162,19 +163,19 @@ func GetInviteInfoByCode(c *models.ReqContext) Response {
})
}
func (hs *HTTPServer) CompleteInvite(c *models.ReqContext, completeInvite dtos.CompleteInviteForm) Response {
func (hs *HTTPServer) CompleteInvite(c *models.ReqContext, completeInvite dtos.CompleteInviteForm) response.Response {
query := models.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrTempUserNotFound) {
return Error(404, "Invite not found", nil)
return response.Error(404, "Invite not found", nil)
}
return Error(500, "Failed to get invite", err)
return response.Error(500, "Failed to get invite", err)
}
invite := query.Result
if invite.Status != models.TmpUserInvitePending {
return Error(412, fmt.Sprintf("Invite cannot be used in status %s", invite.Status), nil)
return response.Error(412, fmt.Sprintf("Invite cannot be used in status %s", invite.Status), nil)
}
cmd := models.CreateUserCommand{
@@ -187,10 +188,10 @@ func (hs *HTTPServer) CompleteInvite(c *models.ReqContext, completeInvite dtos.C
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrUserAlreadyExists) {
return Error(412, fmt.Sprintf("User with email '%s' or username '%s' already exists", completeInvite.Email, completeInvite.Username), err)
return response.Error(412, fmt.Sprintf("User with email '%s' or username '%s' already exists", completeInvite.Email, completeInvite.Username), err)
}
return Error(500, "failed to create user", err)
return response.Error(500, "failed to create user", err)
}
user := &cmd.Result
@@ -199,7 +200,7 @@ func (hs *HTTPServer) CompleteInvite(c *models.ReqContext, completeInvite dtos.C
Name: user.NameOrFallback(),
Email: user.Email,
}); err != nil {
return Error(500, "failed to publish event", err)
return response.Error(500, "failed to publish event", err)
}
if ok, rsp := applyUserInvite(user, invite, true); !ok {
@@ -208,34 +209,34 @@ func (hs *HTTPServer) CompleteInvite(c *models.ReqContext, completeInvite dtos.C
err := hs.loginUserWithUser(user, c)
if err != nil {
return Error(500, "failed to accept invite", err)
return response.Error(500, "failed to accept invite", err)
}
metrics.MApiUserSignUpCompleted.Inc()
metrics.MApiUserSignUpInvite.Inc()
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "User created and logged in",
"id": user.Id,
})
}
func updateTempUserStatus(code string, status models.TempUserStatus) (bool, Response) {
func updateTempUserStatus(code string, status models.TempUserStatus) (bool, response.Response) {
// update temp user status
updateTmpUserCmd := models.UpdateTempUserStatusCommand{Code: code, Status: status}
if err := bus.Dispatch(&updateTmpUserCmd); err != nil {
return false, Error(500, "Failed to update invite status", err)
return false, response.Error(500, "Failed to update invite status", err)
}
return true, nil
}
func applyUserInvite(user *models.User, invite *models.TempUserDTO, setActive bool) (bool, Response) {
func applyUserInvite(user *models.User, invite *models.TempUserDTO, setActive bool) (bool, response.Response) {
// add to org
addOrgUserCmd := models.AddOrgUserCommand{OrgId: invite.OrgId, UserId: user.Id, Role: invite.Role}
if err := bus.Dispatch(&addOrgUserCmd); err != nil {
if !errors.Is(err, models.ErrOrgUserAlreadyAdded) {
return false, Error(500, "Error while trying to create org user", err)
return false, response.Error(500, "Error while trying to create org user", err)
}
}
@@ -247,7 +248,7 @@ func applyUserInvite(user *models.User, invite *models.TempUserDTO, setActive bo
if setActive {
// set org to active
if err := bus.Dispatch(&models.SetUsingOrgCommand{OrgId: invite.OrgId, UserId: user.Id}); err != nil {
return false, Error(500, "Failed to set org as active", err)
return false, response.Error(500, "Failed to set org as active", err)
}
}

View File

@@ -4,32 +4,33 @@ import (
"errors"
"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/util"
)
// POST /api/org/users
func AddOrgUserToCurrentOrg(c *models.ReqContext, cmd models.AddOrgUserCommand) Response {
func AddOrgUserToCurrentOrg(c *models.ReqContext, cmd models.AddOrgUserCommand) response.Response {
cmd.OrgId = c.OrgId
return addOrgUserHelper(cmd)
}
// POST /api/orgs/:orgId/users
func AddOrgUser(c *models.ReqContext, cmd models.AddOrgUserCommand) Response {
func AddOrgUser(c *models.ReqContext, cmd models.AddOrgUserCommand) response.Response {
cmd.OrgId = c.ParamsInt64(":orgId")
return addOrgUserHelper(cmd)
}
func addOrgUserHelper(cmd models.AddOrgUserCommand) Response {
func addOrgUserHelper(cmd models.AddOrgUserCommand) response.Response {
if !cmd.Role.IsValid() {
return Error(400, "Invalid role specified", nil)
return response.Error(400, "Invalid role specified", nil)
}
userQuery := models.GetUserByLoginQuery{LoginOrEmail: cmd.LoginOrEmail}
err := bus.Dispatch(&userQuery)
if err != nil {
return Error(404, "User not found", nil)
return response.Error(404, "User not found", nil)
}
userToAdd := userQuery.Result
@@ -38,22 +39,22 @@ func addOrgUserHelper(cmd models.AddOrgUserCommand) Response {
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrOrgUserAlreadyAdded) {
return JSON(409, util.DynMap{
return response.JSON(409, util.DynMap{
"message": "User is already member of this organization",
"userId": cmd.UserId,
})
}
return Error(500, "Could not add user to organization", err)
return response.Error(500, "Could not add user to organization", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "User added to organization",
"userId": cmd.UserId,
})
}
// GET /api/org/users
func (hs *HTTPServer) GetOrgUsersForCurrentOrg(c *models.ReqContext) Response {
func (hs *HTTPServer) GetOrgUsersForCurrentOrg(c *models.ReqContext) response.Response {
result, err := hs.getOrgUsersHelper(&models.GetOrgUsersQuery{
OrgId: c.OrgId,
Query: c.Query("query"),
@@ -61,21 +62,21 @@ func (hs *HTTPServer) GetOrgUsersForCurrentOrg(c *models.ReqContext) Response {
}, c.SignedInUser)
if err != nil {
return Error(500, "Failed to get users for current organization", err)
return response.Error(500, "Failed to get users for current organization", err)
}
return JSON(200, result)
return response.JSON(200, result)
}
// GET /api/org/users/lookup
func (hs *HTTPServer) GetOrgUsersForCurrentOrgLookup(c *models.ReqContext) Response {
func (hs *HTTPServer) GetOrgUsersForCurrentOrgLookup(c *models.ReqContext) response.Response {
isAdmin, err := isOrgAdminFolderAdminOrTeamAdmin(c)
if err != nil {
return Error(500, "Failed to get users for current organization", err)
return response.Error(500, "Failed to get users for current organization", err)
}
if !isAdmin {
return Error(403, "Permission denied", nil)
return response.Error(403, "Permission denied", nil)
}
orgUsers, err := hs.getOrgUsersHelper(&models.GetOrgUsersQuery{
@@ -85,7 +86,7 @@ func (hs *HTTPServer) GetOrgUsersForCurrentOrgLookup(c *models.ReqContext) Respo
}, c.SignedInUser)
if err != nil {
return Error(500, "Failed to get users for current organization", err)
return response.Error(500, "Failed to get users for current organization", err)
}
result := make([]*dtos.UserLookupDTO, 0)
@@ -98,7 +99,7 @@ func (hs *HTTPServer) GetOrgUsersForCurrentOrgLookup(c *models.ReqContext) Respo
})
}
return JSON(200, result)
return response.JSON(200, result)
}
func isOrgAdminFolderAdminOrTeamAdmin(c *models.ReqContext) (bool, error) {
@@ -124,7 +125,7 @@ func isOrgAdminFolderAdminOrTeamAdmin(c *models.ReqContext) (bool, error) {
}
// GET /api/orgs/:orgId/users
func (hs *HTTPServer) GetOrgUsers(c *models.ReqContext) Response {
func (hs *HTTPServer) GetOrgUsers(c *models.ReqContext) response.Response {
result, err := hs.getOrgUsersHelper(&models.GetOrgUsersQuery{
OrgId: c.ParamsInt64(":orgId"),
Query: "",
@@ -132,10 +133,10 @@ func (hs *HTTPServer) GetOrgUsers(c *models.ReqContext) Response {
}, c.SignedInUser)
if err != nil {
return Error(500, "Failed to get users for organization", err)
return response.Error(500, "Failed to get users for organization", err)
}
return JSON(200, result)
return response.JSON(200, result)
}
func (hs *HTTPServer) getOrgUsersHelper(query *models.GetOrgUsersQuery, signedInUser *models.SignedInUser) ([]*models.OrgUserDTO, error) {
@@ -157,36 +158,36 @@ func (hs *HTTPServer) getOrgUsersHelper(query *models.GetOrgUsersQuery, signedIn
}
// PATCH /api/org/users/:userId
func UpdateOrgUserForCurrentOrg(c *models.ReqContext, cmd models.UpdateOrgUserCommand) Response {
func UpdateOrgUserForCurrentOrg(c *models.ReqContext, cmd models.UpdateOrgUserCommand) response.Response {
cmd.OrgId = c.OrgId
cmd.UserId = c.ParamsInt64(":userId")
return updateOrgUserHelper(cmd)
}
// PATCH /api/orgs/:orgId/users/:userId
func UpdateOrgUser(c *models.ReqContext, cmd models.UpdateOrgUserCommand) Response {
func UpdateOrgUser(c *models.ReqContext, cmd models.UpdateOrgUserCommand) response.Response {
cmd.OrgId = c.ParamsInt64(":orgId")
cmd.UserId = c.ParamsInt64(":userId")
return updateOrgUserHelper(cmd)
}
func updateOrgUserHelper(cmd models.UpdateOrgUserCommand) Response {
func updateOrgUserHelper(cmd models.UpdateOrgUserCommand) response.Response {
if !cmd.Role.IsValid() {
return Error(400, "Invalid role specified", nil)
return response.Error(400, "Invalid role specified", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrLastOrgAdmin) {
return Error(400, "Cannot change role so that there is no organization admin left", nil)
return response.Error(400, "Cannot change role so that there is no organization admin left", nil)
}
return Error(500, "Failed update org user", err)
return response.Error(500, "Failed update org user", err)
}
return Success("Organization user updated")
return response.Success("Organization user updated")
}
// DELETE /api/org/users/:userId
func RemoveOrgUserForCurrentOrg(c *models.ReqContext) Response {
func RemoveOrgUserForCurrentOrg(c *models.ReqContext) response.Response {
return removeOrgUserHelper(&models.RemoveOrgUserCommand{
UserId: c.ParamsInt64(":userId"),
OrgId: c.OrgId,
@@ -195,24 +196,24 @@ func RemoveOrgUserForCurrentOrg(c *models.ReqContext) Response {
}
// DELETE /api/orgs/:orgId/users/:userId
func RemoveOrgUser(c *models.ReqContext) Response {
func RemoveOrgUser(c *models.ReqContext) response.Response {
return removeOrgUserHelper(&models.RemoveOrgUserCommand{
UserId: c.ParamsInt64(":userId"),
OrgId: c.ParamsInt64(":orgId"),
})
}
func removeOrgUserHelper(cmd *models.RemoveOrgUserCommand) Response {
func removeOrgUserHelper(cmd *models.RemoveOrgUserCommand) response.Response {
if err := bus.Dispatch(cmd); err != nil {
if errors.Is(err, models.ErrLastOrgAdmin) {
return Error(400, "Cannot remove last organization admin", nil)
return response.Error(400, "Cannot remove last organization admin", nil)
}
return Error(500, "Failed to remove user from organization", err)
return response.Error(500, "Failed to remove user from organization", err)
}
if cmd.UserWasDeleted {
return Success("User deleted")
return response.Success("User deleted")
}
return Success("User removed from organization")
return response.Success("User removed from organization")
}

View File

@@ -4,47 +4,48 @@ import (
"errors"
"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/setting"
"github.com/grafana/grafana/pkg/util"
)
func SendResetPasswordEmail(c *models.ReqContext, form dtos.SendResetPasswordEmailForm) Response {
func SendResetPasswordEmail(c *models.ReqContext, form dtos.SendResetPasswordEmailForm) response.Response {
if setting.LDAPEnabled || setting.AuthProxyEnabled {
return Error(401, "Not allowed to reset password when LDAP or Auth Proxy is enabled", nil)
return response.Error(401, "Not allowed to reset password when LDAP or Auth Proxy is enabled", nil)
}
if setting.DisableLoginForm {
return Error(401, "Not allowed to reset password when login form is disabled", nil)
return response.Error(401, "Not allowed to reset password when login form is disabled", nil)
}
userQuery := models.GetUserByLoginQuery{LoginOrEmail: form.UserOrEmail}
if err := bus.Dispatch(&userQuery); err != nil {
c.Logger.Info("Requested password reset for user that was not found", "user", userQuery.LoginOrEmail)
return Error(200, "Email sent", err)
return response.Error(200, "Email sent", err)
}
emailCmd := models.SendResetPasswordEmailCommand{User: userQuery.Result}
if err := bus.Dispatch(&emailCmd); err != nil {
return Error(500, "Failed to send email", err)
return response.Error(500, "Failed to send email", err)
}
return Success("Email sent")
return response.Success("Email sent")
}
func ResetPassword(c *models.ReqContext, form dtos.ResetUserPasswordForm) Response {
func ResetPassword(c *models.ReqContext, form dtos.ResetUserPasswordForm) response.Response {
query := models.ValidateResetPasswordCodeQuery{Code: form.Code}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrInvalidEmailCode) {
return Error(400, "Invalid or expired reset password code", nil)
return response.Error(400, "Invalid or expired reset password code", nil)
}
return Error(500, "Unknown error validating email code", err)
return response.Error(500, "Unknown error validating email code", err)
}
if form.NewPassword != form.ConfirmPassword {
return Error(400, "Passwords do not match", nil)
return response.Error(400, "Passwords do not match", nil)
}
cmd := models.ChangeUserPasswordCommand{}
@@ -52,12 +53,12 @@ func ResetPassword(c *models.ReqContext, form dtos.ResetUserPasswordForm) Respon
var err error
cmd.NewPassword, err = util.EncodePassword(form.NewPassword, query.Result.Salt)
if err != nil {
return Error(500, "Failed to encode password", err)
return response.Error(500, "Failed to encode password", err)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to change user password", err)
return response.Error(500, "Failed to change user password", err)
}
return Success("User password changed")
return response.Success("User password changed")
}

View File

@@ -1,6 +1,7 @@
package api
import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
)
@@ -26,7 +27,7 @@ func ValidateOrgPlaylist(c *models.ReqContext) {
}
}
func SearchPlaylists(c *models.ReqContext) Response {
func SearchPlaylists(c *models.ReqContext) response.Response {
query := c.Query("query")
limit := c.QueryInt("limit")
@@ -42,18 +43,18 @@ func SearchPlaylists(c *models.ReqContext) Response {
err := bus.Dispatch(&searchQuery)
if err != nil {
return Error(500, "Search failed", err)
return response.Error(500, "Search failed", err)
}
return JSON(200, searchQuery.Result)
return response.JSON(200, searchQuery.Result)
}
func GetPlaylist(c *models.ReqContext) Response {
func GetPlaylist(c *models.ReqContext) response.Response {
id := c.ParamsInt64(":id")
cmd := models.GetPlaylistByIdQuery{Id: id}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Playlist not found", err)
return response.Error(500, "Playlist not found", err)
}
playlistDTOs, _ := LoadPlaylistItemDTOs(id)
@@ -66,7 +67,7 @@ func GetPlaylist(c *models.ReqContext) Response {
Items: playlistDTOs,
}
return JSON(200, dto)
return response.JSON(200, dto)
}
func LoadPlaylistItemDTOs(id int64) ([]models.PlaylistItemDTO, error) {
@@ -101,63 +102,63 @@ func LoadPlaylistItems(id int64) ([]models.PlaylistItem, error) {
return *itemQuery.Result, nil
}
func GetPlaylistItems(c *models.ReqContext) Response {
func GetPlaylistItems(c *models.ReqContext) response.Response {
id := c.ParamsInt64(":id")
playlistDTOs, err := LoadPlaylistItemDTOs(id)
if err != nil {
return Error(500, "Could not load playlist items", err)
return response.Error(500, "Could not load playlist items", err)
}
return JSON(200, playlistDTOs)
return response.JSON(200, playlistDTOs)
}
func GetPlaylistDashboards(c *models.ReqContext) Response {
func GetPlaylistDashboards(c *models.ReqContext) response.Response {
playlistID := c.ParamsInt64(":id")
playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistID)
if err != nil {
return Error(500, "Could not load dashboards", err)
return response.Error(500, "Could not load dashboards", err)
}
return JSON(200, playlists)
return response.JSON(200, playlists)
}
func DeletePlaylist(c *models.ReqContext) Response {
func DeletePlaylist(c *models.ReqContext) response.Response {
id := c.ParamsInt64(":id")
cmd := models.DeletePlaylistCommand{Id: id, OrgId: c.OrgId}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to delete playlist", err)
return response.Error(500, "Failed to delete playlist", err)
}
return JSON(200, "")
return response.JSON(200, "")
}
func CreatePlaylist(c *models.ReqContext, cmd models.CreatePlaylistCommand) Response {
func CreatePlaylist(c *models.ReqContext, cmd models.CreatePlaylistCommand) response.Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to create playlist", err)
return response.Error(500, "Failed to create playlist", err)
}
return JSON(200, cmd.Result)
return response.JSON(200, cmd.Result)
}
func UpdatePlaylist(c *models.ReqContext, cmd models.UpdatePlaylistCommand) Response {
func UpdatePlaylist(c *models.ReqContext, cmd models.UpdatePlaylistCommand) response.Response {
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":id")
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to save playlist", err)
return response.Error(500, "Failed to save playlist", err)
}
playlistDTOs, err := LoadPlaylistItemDTOs(cmd.Id)
if err != nil {
return Error(500, "Failed to save playlist", err)
return response.Error(500, "Failed to save playlist", err)
}
cmd.Result.Items = playlistDTOs
return JSON(200, cmd.Result)
return response.JSON(200, cmd.Result)
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"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/plugins"
@@ -61,7 +62,7 @@ func (hs *HTTPServer) getPluginContext(pluginID string, user *models.SignedInUse
}, nil
}
func (hs *HTTPServer) GetPluginList(c *models.ReqContext) Response {
func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
typeFilter := c.Query("type")
enabledFilter := c.Query("enabled")
embeddedFilter := c.Query("embedded")
@@ -75,7 +76,7 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) Response {
pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
if err != nil {
return Error(500, "Failed to get list of plugins", err)
return response.Error(500, "Failed to get list of plugins", err)
}
result := make(dtos.PluginList, 0)
@@ -139,15 +140,15 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) Response {
}
sort.Sort(result)
return JSON(200, result)
return response.JSON(200, result)
}
func GetPluginSettingByID(c *models.ReqContext) Response {
func GetPluginSettingByID(c *models.ReqContext) response.Response {
pluginID := c.Params(":pluginId")
def, exists := plugins.Plugins[pluginID]
if !exists {
return Error(404, "Plugin not found, no installed plugin with that id", nil)
return response.Error(404, "Plugin not found, no installed plugin with that id", nil)
}
dto := &dtos.PluginSetting{
@@ -171,7 +172,7 @@ func GetPluginSettingByID(c *models.ReqContext) Response {
query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
if !errors.Is(err, models.ErrPluginSettingNotFound) {
return Error(500, "Failed to get login settings", nil)
return response.Error(500, "Failed to get login settings", nil)
}
} else {
dto.Enabled = query.Result.Enabled
@@ -179,43 +180,43 @@ func GetPluginSettingByID(c *models.ReqContext) Response {
dto.JsonData = query.Result.JsonData
}
return JSON(200, dto)
return response.JSON(200, dto)
}
func UpdatePluginSetting(c *models.ReqContext, cmd models.UpdatePluginSettingCmd) Response {
func UpdatePluginSetting(c *models.ReqContext, cmd models.UpdatePluginSettingCmd) response.Response {
pluginID := c.Params(":pluginId")
cmd.OrgId = c.OrgId
cmd.PluginId = pluginID
if _, ok := plugins.Apps[cmd.PluginId]; !ok {
return Error(404, "Plugin not installed.", nil)
return response.Error(404, "Plugin not installed.", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update plugin setting", err)
return response.Error(500, "Failed to update plugin setting", err)
}
return Success("Plugin settings updated")
return response.Success("Plugin settings updated")
}
func GetPluginDashboards(c *models.ReqContext) Response {
func GetPluginDashboards(c *models.ReqContext) response.Response {
pluginID := c.Params(":pluginId")
list, err := plugins.GetPluginDashboards(c.OrgId, pluginID)
if err != nil {
var notFound plugins.PluginNotFoundError
if errors.As(err, &notFound) {
return Error(404, notFound.Error(), nil)
return response.Error(404, notFound.Error(), nil)
}
return Error(500, "Failed to get plugin dashboards", err)
return response.Error(500, "Failed to get plugin dashboards", err)
}
return JSON(200, list)
return response.JSON(200, list)
}
func GetPluginMarkdown(c *models.ReqContext) Response {
func GetPluginMarkdown(c *models.ReqContext) response.Response {
pluginID := c.Params(":pluginId")
name := c.Params(":name")
@@ -223,28 +224,28 @@ func GetPluginMarkdown(c *models.ReqContext) Response {
if err != nil {
var notFound plugins.PluginNotFoundError
if errors.As(err, &notFound) {
return Error(404, notFound.Error(), nil)
return response.Error(404, notFound.Error(), nil)
}
return Error(500, "Could not get markdown file", err)
return response.Error(500, "Could not get markdown file", err)
}
// fallback try readme
if len(content) == 0 {
content, err = plugins.GetPluginMarkdown(pluginID, "readme")
if err != nil {
return Error(501, "Could not get markdown file", err)
return response.Error(501, "Could not get markdown file", err)
}
}
resp := Respond(200, content)
resp := response.Respond(200, content)
resp.Header("Content-Type", "text/plain; charset=utf-8")
return resp
}
func ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDashboardCommand) Response {
func ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDashboardCommand) response.Response {
if apiCmd.PluginId == "" && apiCmd.Dashboard == nil {
return Error(422, "Dashboard must be set", nil)
return response.Error(422, "Dashboard must be set", nil)
}
cmd := plugins.ImportDashboardCommand{
@@ -262,17 +263,17 @@ func ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDashboardCommand) R
return dashboardSaveErrorToApiResponse(err)
}
return JSON(200, cmd.Result)
return response.JSON(200, cmd.Result)
}
// CollectPluginMetrics collect metrics from a plugin.
//
// /api/plugins/:pluginId/metrics
func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) Response {
func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) response.Response {
pluginID := c.Params("pluginId")
plugin, exists := plugins.Plugins[pluginID]
if !exists {
return Error(404, "Plugin not found", nil)
return response.Error(404, "Plugin not found", nil)
}
resp, err := hs.BackendPluginManager.CollectMetrics(c.Req.Context(), plugin.Id)
@@ -283,25 +284,21 @@ func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) Response {
headers := make(http.Header)
headers.Set("Content-Type", "text/plain")
return &NormalResponse{
header: headers,
body: resp.PrometheusMetrics,
status: http.StatusOK,
}
return response.CreateNormalResponse(headers, resp.PrometheusMetrics, http.StatusOK)
}
// CheckHealth returns the health of a plugin.
// /api/plugins/:pluginId/health
func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response {
func (hs *HTTPServer) CheckHealth(c *models.ReqContext) response.Response {
pluginID := c.Params("pluginId")
pCtx, err := hs.getPluginContext(pluginID, c.SignedInUser)
if err != nil {
if errors.Is(err, ErrPluginNotFound) {
return Error(404, "Plugin not found", nil)
return response.Error(404, "Plugin not found", nil)
}
return Error(500, "Failed to get plugin settings", err)
return response.Error(500, "Failed to get plugin settings", err)
}
resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), pCtx)
@@ -319,17 +316,17 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response {
var jsonDetails map[string]interface{}
err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
if err != nil {
return Error(500, "Failed to unmarshal detailed response from backend plugin", err)
return response.Error(500, "Failed to unmarshal detailed response from backend plugin", err)
}
payload["details"] = jsonDetails
}
if resp.Status != backend.HealthStatusOk {
return JSON(503, payload)
return response.JSON(503, payload)
}
return JSON(200, payload)
return response.JSON(200, payload)
}
// CallResource passes a resource call from a plugin to the backend plugin.
@@ -370,26 +367,26 @@ func (hs *HTTPServer) getCachedPluginSettings(pluginID string, user *models.Sign
return query.Result, nil
}
func (hs *HTTPServer) GetPluginErrorsList(c *models.ReqContext) Response {
return JSON(200, plugins.ScanningErrors())
func (hs *HTTPServer) GetPluginErrorsList(c *models.ReqContext) response.Response {
return response.JSON(200, plugins.ScanningErrors())
}
func translatePluginRequestErrorToAPIError(err error) Response {
func translatePluginRequestErrorToAPIError(err error) response.Response {
if errors.Is(err, backendplugin.ErrPluginNotRegistered) {
return Error(404, "Plugin not found", err)
return response.Error(404, "Plugin not found", err)
}
if errors.Is(err, backendplugin.ErrMethodNotImplemented) {
return Error(404, "Not found", err)
return response.Error(404, "Not found", err)
}
if errors.Is(err, backendplugin.ErrHealthCheckFailed) {
return Error(500, "Plugin health check failed", err)
return response.Error(500, "Plugin health check failed", err)
}
if errors.Is(err, backendplugin.ErrPluginUnavailable) {
return Error(503, "Plugin unavailable", err)
return response.Error(503, "Plugin unavailable", err)
}
return Error(500, "Plugin request failed", err)
return response.Error(500, "Plugin request failed", err)
}

View File

@@ -2,32 +2,33 @@ package api
import (
"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"
)
// POST /api/preferences/set-home-dash
func SetHomeDashboard(c *models.ReqContext, cmd models.SavePreferencesCommand) Response {
func SetHomeDashboard(c *models.ReqContext, cmd models.SavePreferencesCommand) response.Response {
cmd.UserId = c.UserId
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to set home dashboard", err)
return response.Error(500, "Failed to set home dashboard", err)
}
return Success("Home dashboard set")
return response.Success("Home dashboard set")
}
// GET /api/user/preferences
func GetUserPreferences(c *models.ReqContext) Response {
func GetUserPreferences(c *models.ReqContext) response.Response {
return getPreferencesFor(c.OrgId, c.UserId, 0)
}
func getPreferencesFor(orgID, userID, teamID int64) Response {
func getPreferencesFor(orgID, userID, teamID int64) response.Response {
prefsQuery := models.GetPreferencesQuery{UserId: userID, OrgId: orgID, TeamId: teamID}
if err := bus.Dispatch(&prefsQuery); err != nil {
return Error(500, "Failed to get preferences", err)
return response.Error(500, "Failed to get preferences", err)
}
dto := dtos.Prefs{
@@ -36,15 +37,15 @@ func getPreferencesFor(orgID, userID, teamID int64) Response {
Timezone: prefsQuery.Result.Timezone,
}
return JSON(200, &dto)
return response.JSON(200, &dto)
}
// PUT /api/user/preferences
func UpdateUserPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
func UpdateUserPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) response.Response {
return updatePreferencesFor(c.OrgId, c.UserId, 0, &dtoCmd)
}
func updatePreferencesFor(orgID, userID, teamId int64, dtoCmd *dtos.UpdatePrefsCmd) Response {
func updatePreferencesFor(orgID, userID, teamId int64, dtoCmd *dtos.UpdatePrefsCmd) response.Response {
saveCmd := models.SavePreferencesCommand{
UserId: userID,
OrgId: orgID,
@@ -55,18 +56,18 @@ func updatePreferencesFor(orgID, userID, teamId int64, dtoCmd *dtos.UpdatePrefsC
}
if err := bus.Dispatch(&saveCmd); err != nil {
return Error(500, "Failed to save preferences", err)
return response.Error(500, "Failed to save preferences", err)
}
return Success("Preferences updated")
return response.Success("Preferences updated")
}
// GET /api/org/preferences
func GetOrgPreferences(c *models.ReqContext) Response {
func GetOrgPreferences(c *models.ReqContext) response.Response {
return getPreferencesFor(c.OrgId, 0, 0)
}
// PUT /api/org/preferences
func UpdateOrgPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
func UpdateOrgPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) response.Response {
return updatePreferencesFor(c.OrgId, 0, 0, &dtoCmd)
}

View File

@@ -1,67 +1,68 @@
package api
import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
func GetOrgQuotas(c *models.ReqContext) Response {
func GetOrgQuotas(c *models.ReqContext) response.Response {
if !setting.Quota.Enabled {
return Error(404, "Quotas not enabled", nil)
return response.Error(404, "Quotas not enabled", nil)
}
query := models.GetOrgQuotasQuery{OrgId: c.ParamsInt64(":orgId")}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get org quotas", err)
return response.Error(500, "Failed to get org quotas", err)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
func UpdateOrgQuota(c *models.ReqContext, cmd models.UpdateOrgQuotaCmd) Response {
func UpdateOrgQuota(c *models.ReqContext, cmd models.UpdateOrgQuotaCmd) response.Response {
if !setting.Quota.Enabled {
return Error(404, "Quotas not enabled", nil)
return response.Error(404, "Quotas not enabled", nil)
}
cmd.OrgId = c.ParamsInt64(":orgId")
cmd.Target = c.Params(":target")
if _, ok := setting.Quota.Org.ToMap()[cmd.Target]; !ok {
return Error(404, "Invalid quota target", nil)
return response.Error(404, "Invalid quota target", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update org quotas", err)
return response.Error(500, "Failed to update org quotas", err)
}
return Success("Organization quota updated")
return response.Success("Organization quota updated")
}
func GetUserQuotas(c *models.ReqContext) Response {
func GetUserQuotas(c *models.ReqContext) response.Response {
if !setting.Quota.Enabled {
return Error(404, "Quotas not enabled", nil)
return response.Error(404, "Quotas not enabled", nil)
}
query := models.GetUserQuotasQuery{UserId: c.ParamsInt64(":id")}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get org quotas", err)
return response.Error(500, "Failed to get org quotas", err)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
func UpdateUserQuota(c *models.ReqContext, cmd models.UpdateUserQuotaCmd) Response {
func UpdateUserQuota(c *models.ReqContext, cmd models.UpdateUserQuotaCmd) response.Response {
if !setting.Quota.Enabled {
return Error(404, "Quotas not enabled", nil)
return response.Error(404, "Quotas not enabled", nil)
}
cmd.UserId = c.ParamsInt64(":id")
cmd.Target = c.Params(":target")
if _, ok := setting.Quota.User.ToMap()[cmd.Target]; !ok {
return Error(404, "Invalid quota target", nil)
return response.Error(404, "Invalid quota target", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update org quotas", err)
return response.Error(500, "Failed to update org quotas", err)
}
return Success("Organization quota updated")
return response.Success("Organization quota updated")
}

View File

@@ -1,9 +1,11 @@
package api
package response
import (
"encoding/json"
"net/http"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
jsoniter "github.com/json-iterator/go"
)
@@ -17,6 +19,14 @@ type Response interface {
Status() int
}
func CreateNormalResponse(header http.Header, body []byte, status int) *NormalResponse {
return &NormalResponse{
header: header,
body: body,
status: status,
}
}
type NormalResponse struct {
status int
body []byte
@@ -35,6 +45,16 @@ func (r *NormalResponse) Body() []byte {
return r.body
}
// Err gets the response's err.
func (r *NormalResponse) Err() error {
return r.err
}
// ErrMessage gets the response's errMessage.
func (r *NormalResponse) ErrMessage() string {
return r.errMessage
}
func (r *NormalResponse) WriteTo(ctx *models.ReqContext) {
if r.err != nil {
ctx.Logger.Error(r.errMessage, "error", r.err, "remote_addr", ctx.RemoteAddr())
@@ -55,13 +75,8 @@ func (r *NormalResponse) Header(key, value string) *NormalResponse {
return r
}
// Empty creates an empty NormalResponse.
func Empty(status int) *NormalResponse {
return Respond(status, nil)
}
// streamingResponse is a response that streams itself back to the client.
type streamingResponse struct {
// StreamingResponse is a response that streams itself back to the client.
type StreamingResponse struct {
body interface{}
status int
header http.Header
@@ -69,19 +84,19 @@ type streamingResponse struct {
// Status gets the response's status.
// Required to implement api.Response.
func (r streamingResponse) Status() int {
func (r StreamingResponse) Status() int {
return r.status
}
// Body gets the response's body.
// Required to implement api.Response.
func (r streamingResponse) Body() []byte {
func (r StreamingResponse) Body() []byte {
return nil
}
// WriteTo writes the response to the provided context.
// Required to implement api.Response.
func (r streamingResponse) WriteTo(ctx *models.ReqContext) {
func (r StreamingResponse) WriteTo(ctx *models.ReqContext) {
header := ctx.Resp.Header()
for k, v := range r.header {
header[k] = v
@@ -114,3 +129,88 @@ func (*RedirectResponse) Status() int {
func (r *RedirectResponse) Body() []byte {
return nil
}
// JSON creates a JSON response.
func JSON(status int, body interface{}) *NormalResponse {
return Respond(status, body).Header("Content-Type", "application/json")
}
// JSONStreaming creates a streaming JSON response.
func JSONStreaming(status int, body interface{}) StreamingResponse {
header := make(http.Header)
header.Set("Content-Type", "application/json")
return StreamingResponse{
body: body,
status: status,
header: header,
}
}
// Success create a successful response
func Success(message string) *NormalResponse {
resp := make(map[string]interface{})
resp["message"] = message
return JSON(200, resp)
}
// Error creates an error response.
func Error(status int, message string, err error) *NormalResponse {
data := make(map[string]interface{})
switch status {
case 404:
data["message"] = "Not Found"
case 500:
data["message"] = "Internal Server Error"
}
if message != "" {
data["message"] = message
}
if err != nil {
if setting.Env != setting.Prod {
data["error"] = err.Error()
}
}
resp := JSON(status, data)
if err != nil {
resp.errMessage = message
resp.err = err
}
return resp
}
// Empty creates an empty NormalResponse.
func Empty(status int) *NormalResponse {
return Respond(status, nil)
}
// Respond creates a response.
func Respond(status int, body interface{}) *NormalResponse {
var b []byte
switch t := body.(type) {
case []byte:
b = t
case string:
b = []byte(t)
default:
var err error
if b, err = json.Marshal(body); err != nil {
return Error(500, "body json marshal", err)
}
}
return &NormalResponse{
status: status,
body: b,
header: make(http.Header),
}
}
func Redirect(location string) *RedirectResponse {
return &RedirectResponse{location: location}
}

View File

@@ -0,0 +1,27 @@
package routing
import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"gopkg.in/macaron.v1"
)
var (
ServerError = func(err error) response.Response {
return response.Error(500, "Server error", err)
}
)
func Wrap(action interface{}) macaron.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)
}
res.WriteTo(c)
}
}

View File

@@ -6,13 +6,14 @@ import (
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
)
func Search(c *models.ReqContext) Response {
func Search(c *models.ReqContext) response.Response {
query := c.Query("query")
tags := c.QueryStrings("tag")
starred := c.Query("starred")
@@ -23,7 +24,7 @@ func Search(c *models.ReqContext) Response {
permission := models.PERMISSION_VIEW
if limit > 5000 {
return Error(422, "Limit is above maximum allowed (5000), use page parameter to access hits beyond limit", nil)
return response.Error(422, "Limit is above maximum allowed (5000), use page parameter to access hits beyond limit", nil)
}
if c.Query("permission") == "Edit" {
@@ -63,14 +64,14 @@ func Search(c *models.ReqContext) Response {
err := bus.Dispatch(&searchQuery)
if err != nil {
return Error(500, "Search failed", err)
return response.Error(500, "Search failed", err)
}
c.TimeRequest(metrics.MApiDashboardSearch)
return JSON(200, searchQuery.Result)
return response.JSON(200, searchQuery.Result)
}
func (hs *HTTPServer) ListSortOptions(c *models.ReqContext) Response {
func (hs *HTTPServer) ListSortOptions(c *models.ReqContext) response.Response {
opts := hs.SearchService.SortOptions()
res := []util.DynMap{}
@@ -82,7 +83,7 @@ func (hs *HTTPServer) ListSortOptions(c *models.ReqContext) Response {
})
}
return JSON(http.StatusOK, util.DynMap{
return response.JSON(http.StatusOK, util.DynMap{
"sortOptions": res,
})
}

View File

@@ -7,25 +7,26 @@ import (
"strings"
"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/setting"
"github.com/grafana/grafana/pkg/util"
)
// createShortURL handles requests to create short URLs.
func (hs *HTTPServer) createShortURL(c *models.ReqContext, cmd dtos.CreateShortURLCmd) Response {
func (hs *HTTPServer) createShortURL(c *models.ReqContext, cmd dtos.CreateShortURLCmd) response.Response {
hs.log.Debug("Received request to create short URL", "path", cmd.Path)
cmd.Path = strings.TrimSpace(cmd.Path)
if path.IsAbs(cmd.Path) {
hs.log.Error("Invalid short URL path", "path", cmd.Path)
return Error(400, "Path should be relative", nil)
return response.Error(400, "Path should be relative", nil)
}
shortURL, err := hs.ShortURLService.CreateShortURL(c.Req.Context(), c.SignedInUser, cmd.Path)
if err != nil {
return Error(500, "Failed to create short URL", err)
return response.Error(500, "Failed to create short URL", err)
}
url := fmt.Sprintf("%s/goto/%s", strings.TrimSuffix(setting.AppUrl, "/"), shortURL.Uid)
@@ -36,7 +37,7 @@ func (hs *HTTPServer) createShortURL(c *models.ReqContext, cmd dtos.CreateShortU
URL: url,
}
return JSON(200, dto)
return response.JSON(200, dto)
}
func (hs *HTTPServer) redirectFromShortURL(c *models.ReqContext) {

View File

@@ -4,6 +4,7 @@ import (
"errors"
"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/events"
"github.com/grafana/grafana/pkg/infra/metrics"
@@ -13,22 +14,22 @@ import (
)
// GET /api/user/signup/options
func GetSignUpOptions(c *models.ReqContext) Response {
return JSON(200, util.DynMap{
func GetSignUpOptions(c *models.ReqContext) response.Response {
return response.JSON(200, util.DynMap{
"verifyEmailEnabled": setting.VerifyEmailEnabled,
"autoAssignOrg": setting.AutoAssignOrg,
})
}
// POST /api/user/signup
func SignUp(c *models.ReqContext, form dtos.SignUpForm) Response {
func SignUp(c *models.ReqContext, form dtos.SignUpForm) response.Response {
if !setting.AllowUserSignUp {
return Error(401, "User signup is disabled", nil)
return response.Error(401, "User signup is disabled", nil)
}
existing := models.GetUserByLoginQuery{LoginOrEmail: form.Email}
if err := bus.Dispatch(&existing); err == nil {
return Error(422, "User with same email address already exists", nil)
return response.Error(422, "User with same email address already exists", nil)
}
cmd := models.CreateTempUserCommand{}
@@ -39,29 +40,29 @@ func SignUp(c *models.ReqContext, form dtos.SignUpForm) Response {
var err error
cmd.Code, err = util.GetRandomString(20)
if err != nil {
return Error(500, "Failed to generate random string", err)
return response.Error(500, "Failed to generate random string", err)
}
cmd.RemoteAddr = c.Req.RemoteAddr
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to create signup", err)
return response.Error(500, "Failed to create signup", err)
}
if err := bus.Publish(&events.SignUpStarted{
Email: form.Email,
Code: cmd.Code,
}); err != nil {
return Error(500, "Failed to publish event", err)
return response.Error(500, "Failed to publish event", err)
}
metrics.MApiUserSignUpStarted.Inc()
return JSON(200, util.DynMap{"status": "SignUpCreated"})
return response.JSON(200, util.DynMap{"status": "SignUpCreated"})
}
func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2Form) Response {
func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2Form) response.Response {
if !setting.AllowUserSignUp {
return Error(401, "User signup is disabled", nil)
return response.Error(401, "User signup is disabled", nil)
}
createUserCmd := models.CreateUserCommand{
@@ -83,10 +84,10 @@ func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2For
// dispatch create command
if err := bus.Dispatch(&createUserCmd); err != nil {
if errors.Is(err, models.ErrUserAlreadyExists) {
return Error(401, "User with same email address already exists", nil)
return response.Error(401, "User with same email address already exists", nil)
}
return Error(500, "Failed to create user", err)
return response.Error(500, "Failed to create user", err)
}
// publish signup event
@@ -95,7 +96,7 @@ func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2For
Email: user.Email,
Name: user.NameOrFallback(),
}); err != nil {
return Error(500, "Failed to publish event", err)
return response.Error(500, "Failed to publish event", err)
}
// mark temp user as completed
@@ -106,7 +107,7 @@ func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2For
// check for pending invites
invitesQuery := models.GetTempUsersQuery{Email: form.Email, Status: models.TmpUserInvitePending}
if err := bus.Dispatch(&invitesQuery); err != nil {
return Error(500, "Failed to query database for invites", err)
return response.Error(500, "Failed to query database for invites", err)
}
apiResponse := util.DynMap{"message": "User sign up completed successfully", "code": "redirect-to-landing-page"}
@@ -119,27 +120,27 @@ func (hs *HTTPServer) SignUpStep2(c *models.ReqContext, form dtos.SignUpStep2For
err := hs.loginUserWithUser(user, c)
if err != nil {
return Error(500, "failed to login user", err)
return response.Error(500, "failed to login user", err)
}
metrics.MApiUserSignUpCompleted.Inc()
return JSON(200, apiResponse)
return response.JSON(200, apiResponse)
}
func verifyUserSignUpEmail(email string, code string) (bool, Response) {
func verifyUserSignUpEmail(email string, code string) (bool, response.Response) {
query := models.GetTempUserByCodeQuery{Code: code}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrTempUserNotFound) {
return false, Error(404, "Invalid email verification code", nil)
return false, response.Error(404, "Invalid email verification code", nil)
}
return false, Error(500, "Failed to read temp user", err)
return false, response.Error(500, "Failed to read temp user", err)
}
tempUser := query.Result
if tempUser.Email != email {
return false, Error(404, "Email verification code does not match email", nil)
return false, response.Error(404, "Email verification code does not match email", nil)
}
return true, nil

View File

@@ -1,38 +1,39 @@
package api
import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
)
func StarDashboard(c *models.ReqContext) Response {
func StarDashboard(c *models.ReqContext) response.Response {
if !c.IsSignedIn {
return Error(412, "You need to sign in to star dashboards", nil)
return response.Error(412, "You need to sign in to star dashboards", nil)
}
cmd := models.StarDashboardCommand{UserId: c.UserId, DashboardId: c.ParamsInt64(":id")}
if cmd.DashboardId <= 0 {
return Error(400, "Missing dashboard id", nil)
return response.Error(400, "Missing dashboard id", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to star dashboard", err)
return response.Error(500, "Failed to star dashboard", err)
}
return Success("Dashboard starred!")
return response.Success("Dashboard starred!")
}
func UnstarDashboard(c *models.ReqContext) Response {
func UnstarDashboard(c *models.ReqContext) response.Response {
cmd := models.UnstarDashboardCommand{UserId: c.UserId, DashboardId: c.ParamsInt64(":id")}
if cmd.DashboardId <= 0 {
return Error(400, "Missing dashboard id", nil)
return response.Error(400, "Missing dashboard id", nil)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to unstar dashboard", err)
return response.Error(500, "Failed to unstar dashboard", err)
}
return Success("Dashboard unstarred")
return response.Success("Dashboard unstarred")
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"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/services/teamguardian"
@@ -11,18 +12,18 @@ import (
)
// POST /api/teams
func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamCommand) Response {
func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamCommand) response.Response {
cmd.OrgId = c.OrgId
if c.OrgRole == models.ROLE_VIEWER {
return Error(403, "Not allowed to create team.", nil)
return response.Error(403, "Not allowed to create team.", nil)
}
if err := hs.Bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrTeamNameTaken) {
return Error(409, "Team name taken", err)
return response.Error(409, "Team name taken", err)
}
return Error(500, "Failed to create Team", err)
return response.Error(500, "Failed to create Team", err)
}
if c.OrgRole == models.ROLE_EDITOR && hs.Cfg.EditorsCanAdmin {
@@ -45,52 +46,52 @@ func (hs *HTTPServer) CreateTeam(c *models.ReqContext, cmd models.CreateTeamComm
}
}
return JSON(200, &util.DynMap{
return response.JSON(200, &util.DynMap{
"teamId": cmd.Result.Id,
"message": "Team created",
})
}
// PUT /api/teams/:teamId
func (hs *HTTPServer) UpdateTeam(c *models.ReqContext, cmd models.UpdateTeamCommand) Response {
func (hs *HTTPServer) UpdateTeam(c *models.ReqContext, cmd models.UpdateTeamCommand) response.Response {
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":teamId")
if err := teamguardian.CanAdmin(hs.Bus, cmd.OrgId, cmd.Id, c.SignedInUser); err != nil {
return Error(403, "Not allowed to update team", err)
return response.Error(403, "Not allowed to update team", err)
}
if err := hs.Bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrTeamNameTaken) {
return Error(400, "Team name taken", err)
return response.Error(400, "Team name taken", err)
}
return Error(500, "Failed to update Team", err)
return response.Error(500, "Failed to update Team", err)
}
return Success("Team updated")
return response.Success("Team updated")
}
// DELETE /api/teams/:teamId
func (hs *HTTPServer) DeleteTeamByID(c *models.ReqContext) Response {
func (hs *HTTPServer) DeleteTeamByID(c *models.ReqContext) response.Response {
orgId := c.OrgId
teamId := c.ParamsInt64(":teamId")
user := c.SignedInUser
if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, user); err != nil {
return Error(403, "Not allowed to delete team", err)
return response.Error(403, "Not allowed to delete team", err)
}
if err := hs.Bus.Dispatch(&models.DeleteTeamCommand{OrgId: orgId, Id: teamId}); err != nil {
if errors.Is(err, models.ErrTeamNotFound) {
return Error(404, "Failed to delete Team. ID not found", nil)
return response.Error(404, "Failed to delete Team. ID not found", nil)
}
return Error(500, "Failed to delete Team", err)
return response.Error(500, "Failed to delete Team", err)
}
return Success("Team deleted")
return response.Success("Team deleted")
}
// GET /api/teams/search
func (hs *HTTPServer) SearchTeams(c *models.ReqContext) Response {
func (hs *HTTPServer) SearchTeams(c *models.ReqContext) response.Response {
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
@@ -117,7 +118,7 @@ func (hs *HTTPServer) SearchTeams(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to search Teams", err)
return response.Error(500, "Failed to search Teams", err)
}
for _, team := range query.Result.Teams {
@@ -127,11 +128,11 @@ func (hs *HTTPServer) SearchTeams(c *models.ReqContext) Response {
query.Result.Page = page
query.Result.PerPage = perPage
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
// GET /api/teams/:teamId
func (hs *HTTPServer) GetTeamByID(c *models.ReqContext) Response {
func (hs *HTTPServer) GetTeamByID(c *models.ReqContext) response.Response {
query := models.GetTeamByIdQuery{
OrgId: c.OrgId,
Id: c.ParamsInt64(":teamId"),
@@ -141,35 +142,35 @@ func (hs *HTTPServer) GetTeamByID(c *models.ReqContext) Response {
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrTeamNotFound) {
return Error(404, "Team not found", err)
return response.Error(404, "Team not found", err)
}
return Error(500, "Failed to get Team", err)
return response.Error(500, "Failed to get Team", err)
}
query.Result.AvatarUrl = dtos.GetGravatarUrlWithDefault(query.Result.Email, query.Result.Name)
return JSON(200, &query.Result)
return response.JSON(200, &query.Result)
}
// GET /api/teams/:teamId/preferences
func (hs *HTTPServer) GetTeamPreferences(c *models.ReqContext) Response {
func (hs *HTTPServer) GetTeamPreferences(c *models.ReqContext) response.Response {
teamId := c.ParamsInt64(":teamId")
orgId := c.OrgId
if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil {
return Error(403, "Not allowed to view team preferences.", err)
return response.Error(403, "Not allowed to view team preferences.", err)
}
return getPreferencesFor(orgId, 0, teamId)
}
// PUT /api/teams/:teamId/preferences
func (hs *HTTPServer) UpdateTeamPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
func (hs *HTTPServer) UpdateTeamPreferences(c *models.ReqContext, dtoCmd dtos.UpdatePrefsCmd) response.Response {
teamId := c.ParamsInt64(":teamId")
orgId := c.OrgId
if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil {
return Error(403, "Not allowed to update team preferences.", err)
return response.Error(403, "Not allowed to update team preferences.", err)
}
return updatePreferencesFor(orgId, 0, teamId, &dtoCmd)

View File

@@ -4,6 +4,7 @@ import (
"errors"
"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/services/teamguardian"
@@ -11,11 +12,11 @@ import (
)
// GET /api/teams/:teamId/members
func (hs *HTTPServer) GetTeamMembers(c *models.ReqContext) Response {
func (hs *HTTPServer) GetTeamMembers(c *models.ReqContext) response.Response {
query := models.GetTeamMembersQuery{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId")}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get Team Members", err)
return response.Error(500, "Failed to get Team Members", err)
}
filteredMembers := make([]*models.TeamMemberDTO, 0, len(query.Result))
@@ -35,42 +36,42 @@ func (hs *HTTPServer) GetTeamMembers(c *models.ReqContext) Response {
filteredMembers = append(filteredMembers, member)
}
return JSON(200, filteredMembers)
return response.JSON(200, filteredMembers)
}
// POST /api/teams/:teamId/members
func (hs *HTTPServer) AddTeamMember(c *models.ReqContext, cmd models.AddTeamMemberCommand) Response {
func (hs *HTTPServer) AddTeamMember(c *models.ReqContext, cmd models.AddTeamMemberCommand) response.Response {
cmd.OrgId = c.OrgId
cmd.TeamId = c.ParamsInt64(":teamId")
if err := teamguardian.CanAdmin(hs.Bus, cmd.OrgId, cmd.TeamId, c.SignedInUser); err != nil {
return Error(403, "Not allowed to add team member", err)
return response.Error(403, "Not allowed to add team member", err)
}
if err := hs.Bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrTeamNotFound) {
return Error(404, "Team not found", nil)
return response.Error(404, "Team not found", nil)
}
if errors.Is(err, models.ErrTeamMemberAlreadyAdded) {
return Error(400, "User is already added to this team", nil)
return response.Error(400, "User is already added to this team", nil)
}
return Error(500, "Failed to add Member to Team", err)
return response.Error(500, "Failed to add Member to Team", err)
}
return JSON(200, &util.DynMap{
return response.JSON(200, &util.DynMap{
"message": "Member added to Team",
})
}
// PUT /:teamId/members/:userId
func (hs *HTTPServer) UpdateTeamMember(c *models.ReqContext, cmd models.UpdateTeamMemberCommand) Response {
func (hs *HTTPServer) UpdateTeamMember(c *models.ReqContext, cmd models.UpdateTeamMemberCommand) response.Response {
teamId := c.ParamsInt64(":teamId")
orgId := c.OrgId
if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil {
return Error(403, "Not allowed to update team member", err)
return response.Error(403, "Not allowed to update team member", err)
}
if c.OrgRole != models.ROLE_ADMIN {
@@ -83,21 +84,21 @@ func (hs *HTTPServer) UpdateTeamMember(c *models.ReqContext, cmd models.UpdateTe
if err := hs.Bus.Dispatch(&cmd); err != nil {
if errors.Is(err, models.ErrTeamMemberNotFound) {
return Error(404, "Team member not found.", nil)
return response.Error(404, "Team member not found.", nil)
}
return Error(500, "Failed to update team member.", err)
return response.Error(500, "Failed to update team member.", err)
}
return Success("Team member updated")
return response.Success("Team member updated")
}
// DELETE /api/teams/:teamId/members/:userId
func (hs *HTTPServer) RemoveTeamMember(c *models.ReqContext) Response {
func (hs *HTTPServer) RemoveTeamMember(c *models.ReqContext) response.Response {
orgId := c.OrgId
teamId := c.ParamsInt64(":teamId")
userId := c.ParamsInt64(":userId")
if err := teamguardian.CanAdmin(hs.Bus, orgId, teamId, c.SignedInUser); err != nil {
return Error(403, "Not allowed to remove team member", err)
return response.Error(403, "Not allowed to remove team member", err)
}
protectLastAdmin := false
@@ -107,14 +108,14 @@ func (hs *HTTPServer) RemoveTeamMember(c *models.ReqContext) Response {
if err := hs.Bus.Dispatch(&models.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId, ProtectLastAdmin: protectLastAdmin}); err != nil {
if errors.Is(err, models.ErrTeamNotFound) {
return Error(404, "Team not found", nil)
return response.Error(404, "Team not found", nil)
}
if errors.Is(err, models.ErrTeamMemberNotFound) {
return Error(404, "Team member not found", nil)
return response.Error(404, "Team member not found", nil)
}
return Error(500, "Failed to remove Member from Team", err)
return response.Error(500, "Failed to remove Member from Team", err)
}
return Success("Team Member removed")
return response.Success("Team Member removed")
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"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/setting"
@@ -11,23 +12,23 @@ import (
)
// GET /api/user (current authenticated user)
func GetSignedInUser(c *models.ReqContext) Response {
func GetSignedInUser(c *models.ReqContext) response.Response {
return getUserUserProfile(c.UserId)
}
// GET /api/users/:id
func GetUserByID(c *models.ReqContext) Response {
func GetUserByID(c *models.ReqContext) response.Response {
return getUserUserProfile(c.ParamsInt64(":id"))
}
func getUserUserProfile(userID int64) Response {
func getUserUserProfile(userID int64) response.Response {
query := models.GetUserProfileQuery{UserId: userID}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to get user", err)
return response.Error(500, "Failed to get user", err)
}
getAuthQuery := models.GetAuthInfoQuery{UserId: userID}
@@ -40,17 +41,17 @@ func getUserUserProfile(userID int64) Response {
query.Result.AvatarUrl = dtos.GetGravatarUrl(query.Result.Email)
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
// GET /api/users/lookup
func GetUserByLoginOrEmail(c *models.ReqContext) Response {
func GetUserByLoginOrEmail(c *models.ReqContext) response.Response {
query := models.GetUserByLoginQuery{LoginOrEmail: c.Query("loginOrEmail")}
if err := bus.Dispatch(&query); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, models.ErrUserNotFound.Error(), nil)
return response.Error(404, models.ErrUserNotFound.Error(), nil)
}
return Error(500, "Failed to get user", err)
return response.Error(500, "Failed to get user", err)
}
user := query.Result
result := models.UserProfileDTO{
@@ -64,17 +65,17 @@ func GetUserByLoginOrEmail(c *models.ReqContext) Response {
UpdatedAt: user.Updated,
CreatedAt: user.Created,
}
return JSON(200, &result)
return response.JSON(200, &result)
}
// POST /api/user
func UpdateSignedInUser(c *models.ReqContext, cmd models.UpdateUserCommand) Response {
func UpdateSignedInUser(c *models.ReqContext, cmd models.UpdateUserCommand) response.Response {
if setting.AuthProxyEnabled {
if setting.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
return Error(400, "Not allowed to change email when auth proxy is using email property", nil)
return response.Error(400, "Not allowed to change email when auth proxy is using email property", nil)
}
if setting.AuthProxyHeaderProperty == "username" && cmd.Login != c.Login {
return Error(400, "Not allowed to change username when auth proxy is using username property", nil)
return response.Error(400, "Not allowed to change username when auth proxy is using username property", nil)
}
}
cmd.UserId = c.UserId
@@ -82,85 +83,85 @@ func UpdateSignedInUser(c *models.ReqContext, cmd models.UpdateUserCommand) Resp
}
// POST /api/users/:id
func UpdateUser(c *models.ReqContext, cmd models.UpdateUserCommand) Response {
func UpdateUser(c *models.ReqContext, cmd models.UpdateUserCommand) response.Response {
cmd.UserId = c.ParamsInt64(":id")
return handleUpdateUser(cmd)
}
// POST /api/users/:id/using/:orgId
func UpdateUserActiveOrg(c *models.ReqContext) Response {
func UpdateUserActiveOrg(c *models.ReqContext) response.Response {
userID := c.ParamsInt64(":id")
orgID := c.ParamsInt64(":orgId")
if !validateUsingOrg(userID, orgID) {
return Error(401, "Not a valid organization", nil)
return response.Error(401, "Not a valid organization", nil)
}
cmd := models.SetUsingOrgCommand{UserId: userID, OrgId: orgID}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to change active organization", err)
return response.Error(500, "Failed to change active organization", err)
}
return Success("Active organization changed")
return response.Success("Active organization changed")
}
func handleUpdateUser(cmd models.UpdateUserCommand) Response {
func handleUpdateUser(cmd models.UpdateUserCommand) response.Response {
if len(cmd.Login) == 0 {
cmd.Login = cmd.Email
if len(cmd.Login) == 0 {
return Error(400, "Validation error, need to specify either username or email", nil)
return response.Error(400, "Validation error, need to specify either username or email", nil)
}
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update user", err)
return response.Error(500, "Failed to update user", err)
}
return Success("User updated")
return response.Success("User updated")
}
// GET /api/user/orgs
func GetSignedInUserOrgList(c *models.ReqContext) Response {
func GetSignedInUserOrgList(c *models.ReqContext) response.Response {
return getUserOrgList(c.UserId)
}
// GET /api/user/teams
func GetSignedInUserTeamList(c *models.ReqContext) Response {
func GetSignedInUserTeamList(c *models.ReqContext) response.Response {
return getUserTeamList(c.OrgId, c.UserId)
}
// GET /api/users/:id/teams
func GetUserTeams(c *models.ReqContext) Response {
func GetUserTeams(c *models.ReqContext) response.Response {
return getUserTeamList(c.OrgId, c.ParamsInt64(":id"))
}
func getUserTeamList(orgID int64, userID int64) Response {
func getUserTeamList(orgID int64, userID int64) response.Response {
query := models.GetTeamsByUserQuery{OrgId: orgID, UserId: userID}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get user teams", err)
return response.Error(500, "Failed to get user teams", err)
}
for _, team := range query.Result {
team.AvatarUrl = dtos.GetGravatarUrlWithDefault(team.Email, team.Name)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
// GET /api/users/:id/orgs
func GetUserOrgList(c *models.ReqContext) Response {
func GetUserOrgList(c *models.ReqContext) response.Response {
return getUserOrgList(c.ParamsInt64(":id"))
}
func getUserOrgList(userID int64) Response {
func getUserOrgList(userID int64) response.Response {
query := models.GetUserOrgListQuery{UserId: userID}
if err := bus.Dispatch(&query); err != nil {
return Error(500, "Failed to get user organizations", err)
return response.Error(500, "Failed to get user organizations", err)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
func validateUsingOrg(userID int64, orgID int64) bool {
@@ -182,20 +183,20 @@ func validateUsingOrg(userID int64, orgID int64) bool {
}
// POST /api/user/using/:id
func UserSetUsingOrg(c *models.ReqContext) Response {
func UserSetUsingOrg(c *models.ReqContext) response.Response {
orgID := c.ParamsInt64(":id")
if !validateUsingOrg(c.UserId, orgID) {
return Error(401, "Not a valid organization", nil)
return response.Error(401, "Not a valid organization", nil)
}
cmd := models.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgID}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to change active organization", err)
return response.Error(500, "Failed to change active organization", err)
}
return Success("Active organization changed")
return response.Success("Active organization changed")
}
// GET /profile/switch-org/:id
@@ -215,41 +216,41 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *models.ReqContext) {
c.Redirect(setting.AppSubUrl + "/")
}
func ChangeUserPassword(c *models.ReqContext, cmd models.ChangeUserPasswordCommand) Response {
func ChangeUserPassword(c *models.ReqContext, cmd models.ChangeUserPasswordCommand) response.Response {
if setting.LDAPEnabled || setting.AuthProxyEnabled {
return Error(400, "Not allowed to change password when LDAP or Auth Proxy is enabled", nil)
return response.Error(400, "Not allowed to change password when LDAP or Auth Proxy is enabled", nil)
}
userQuery := models.GetUserByIdQuery{Id: c.UserId}
if err := bus.Dispatch(&userQuery); err != nil {
return Error(500, "Could not read user from database", err)
return response.Error(500, "Could not read user from database", err)
}
passwordHashed, err := util.EncodePassword(cmd.OldPassword, userQuery.Result.Salt)
if err != nil {
return Error(500, "Failed to encode password", err)
return response.Error(500, "Failed to encode password", err)
}
if passwordHashed != userQuery.Result.Password {
return Error(401, "Invalid old password", nil)
return response.Error(401, "Invalid old password", nil)
}
password := models.Password(cmd.NewPassword)
if password.IsWeak() {
return Error(400, "New password is too short", nil)
return response.Error(400, "New password is too short", nil)
}
cmd.UserId = c.UserId
cmd.NewPassword, err = util.EncodePassword(cmd.NewPassword, userQuery.Result.Salt)
if err != nil {
return Error(500, "Failed to encode password", err)
return response.Error(500, "Failed to encode password", err)
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to change user password", err)
return response.Error(500, "Failed to change user password", err)
}
return Success("User password changed")
return response.Success("User password changed")
}
// redirectToChangePassword handles GET /.well-known/change-password.
@@ -258,23 +259,23 @@ func redirectToChangePassword(c *models.ReqContext) {
}
// GET /api/users
func SearchUsers(c *models.ReqContext) Response {
func SearchUsers(c *models.ReqContext) response.Response {
query, err := searchUser(c)
if err != nil {
return Error(500, "Failed to fetch users", err)
return response.Error(500, "Failed to fetch users", err)
}
return JSON(200, query.Result.Users)
return response.JSON(200, query.Result.Users)
}
// GET /api/users/search
func SearchUsersWithPaging(c *models.ReqContext) Response {
func SearchUsersWithPaging(c *models.ReqContext) response.Response {
query, err := searchUser(c)
if err != nil {
return Error(500, "Failed to fetch users", err)
return response.Error(500, "Failed to fetch users", err)
}
return JSON(200, query.Result)
return response.JSON(200, query.Result)
}
func searchUser(c *models.ReqContext) (*models.SearchUsersQuery, error) {
@@ -311,7 +312,7 @@ func searchUser(c *models.ReqContext) (*models.SearchUsersQuery, error) {
return query, nil
}
func SetHelpFlag(c *models.ReqContext) Response {
func SetHelpFlag(c *models.ReqContext) response.Response {
flag := c.ParamsInt64(":id")
bitmask := &c.HelpFlags1
@@ -323,23 +324,23 @@ func SetHelpFlag(c *models.ReqContext) Response {
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update help flag", err)
return response.Error(500, "Failed to update help flag", err)
}
return JSON(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
return response.JSON(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
}
func ClearHelpFlags(c *models.ReqContext) Response {
func ClearHelpFlags(c *models.ReqContext) response.Response {
cmd := models.SetUserHelpFlagCommand{
UserId: c.UserId,
HelpFlags1: models.HelpFlags1(0),
}
if err := bus.Dispatch(&cmd); err != nil {
return Error(500, "Failed to update help flag", err)
return response.Error(500, "Failed to update help flag", err)
}
return JSON(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
return response.JSON(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
}
func GetAuthProviderLabel(authModule string) string {

View File

@@ -6,6 +6,7 @@ import (
"time"
"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/util"
@@ -13,48 +14,48 @@ import (
)
// GET /api/user/auth-tokens
func (hs *HTTPServer) GetUserAuthTokens(c *models.ReqContext) Response {
func (hs *HTTPServer) GetUserAuthTokens(c *models.ReqContext) response.Response {
return hs.getUserAuthTokensInternal(c, c.UserId)
}
// POST /api/user/revoke-auth-token
func (hs *HTTPServer) RevokeUserAuthToken(c *models.ReqContext, cmd models.RevokeAuthTokenCmd) Response {
func (hs *HTTPServer) RevokeUserAuthToken(c *models.ReqContext, cmd models.RevokeAuthTokenCmd) response.Response {
return hs.revokeUserAuthTokenInternal(c, c.UserId, cmd)
}
func (hs *HTTPServer) logoutUserFromAllDevicesInternal(ctx context.Context, userID int64) Response {
func (hs *HTTPServer) logoutUserFromAllDevicesInternal(ctx context.Context, userID int64) response.Response {
userQuery := models.GetUserByIdQuery{Id: userID}
if err := bus.Dispatch(&userQuery); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, "User not found", err)
return response.Error(404, "User not found", err)
}
return Error(500, "Could not read user from database", err)
return response.Error(500, "Could not read user from database", err)
}
err := hs.AuthTokenService.RevokeAllUserTokens(ctx, userID)
if err != nil {
return Error(500, "Failed to logout user", err)
return response.Error(500, "Failed to logout user", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "User logged out",
})
}
func (hs *HTTPServer) getUserAuthTokensInternal(c *models.ReqContext, userID int64) Response {
func (hs *HTTPServer) getUserAuthTokensInternal(c *models.ReqContext, userID int64) response.Response {
userQuery := models.GetUserByIdQuery{Id: userID}
if err := bus.Dispatch(&userQuery); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, "User not found", err)
return response.Error(404, "User not found", err)
}
return Error(500, "Failed to get user", err)
return response.Error(500, "Failed to get user", err)
}
tokens, err := hs.AuthTokenService.GetUserTokens(c.Req.Context(), userID)
if err != nil {
return Error(500, "Failed to get user auth tokens", err)
return response.Error(500, "Failed to get user auth tokens", err)
}
result := []*dtos.UserToken{}
@@ -106,40 +107,40 @@ func (hs *HTTPServer) getUserAuthTokensInternal(c *models.ReqContext, userID int
})
}
return JSON(200, result)
return response.JSON(200, result)
}
func (hs *HTTPServer) revokeUserAuthTokenInternal(c *models.ReqContext, userID int64, cmd models.RevokeAuthTokenCmd) Response {
func (hs *HTTPServer) revokeUserAuthTokenInternal(c *models.ReqContext, userID int64, cmd models.RevokeAuthTokenCmd) response.Response {
userQuery := models.GetUserByIdQuery{Id: userID}
if err := bus.Dispatch(&userQuery); err != nil {
if errors.Is(err, models.ErrUserNotFound) {
return Error(404, "User not found", err)
return response.Error(404, "User not found", err)
}
return Error(500, "Failed to get user", err)
return response.Error(500, "Failed to get user", err)
}
token, err := hs.AuthTokenService.GetUserToken(c.Req.Context(), userID, cmd.AuthTokenId)
if err != nil {
if errors.Is(err, models.ErrUserTokenNotFound) {
return Error(404, "User auth token not found", err)
return response.Error(404, "User auth token not found", err)
}
return Error(500, "Failed to get user auth token", err)
return response.Error(500, "Failed to get user auth token", err)
}
if c.UserToken != nil && c.UserToken.Id == token.Id {
return Error(400, "Cannot revoke active user auth token", nil)
return response.Error(400, "Cannot revoke active user auth token", nil)
}
err = hs.AuthTokenService.RevokeToken(c.Req.Context(), token)
if err != nil {
if errors.Is(err, models.ErrUserTokenNotFound) {
return Error(404, "User auth token not found", err)
return response.Error(404, "User auth token not found", err)
}
return Error(500, "Failed to revoke user auth token", err)
return response.Error(500, "Failed to revoke user auth token", err)
}
return JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"message": "User auth token revoked",
})
}

View File

@@ -6,6 +6,8 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
@@ -181,7 +183,7 @@ func revokeUserAuthTokenScenario(t *testing.T, desc string, url string, routePat
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = userId
sc.context.OrgId = testOrgID
@@ -209,7 +211,7 @@ func getUserAuthTokensScenario(t *testing.T, desc string, url string, routePatte
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = userId
sc.context.OrgId = testOrgID
@@ -234,7 +236,7 @@ func logoutUserFromAllDevicesInternalScenario(t *testing.T, desc string, userId
}
sc := setupScenarioContext(t, "/")
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -263,7 +265,7 @@ func revokeUserAuthTokenInternalScenario(t *testing.T, desc string, cmd models.R
sc := setupScenarioContext(t, "/")
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
@@ -292,7 +294,7 @@ func getUserAuthTokensInternalScenario(t *testing.T, desc string, token *models.
sc := setupScenarioContext(t, "/")
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID

View File

@@ -4,7 +4,7 @@ import (
"errors"
"github.com/go-macaron/binding"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
@@ -17,119 +17,119 @@ func (lps *LibraryPanelService) registerAPIEndpoints() {
}
lps.RouteRegister.Group("/api/library-panels", func(libraryPanels routing.RouteRegister) {
libraryPanels.Post("/", middleware.ReqSignedIn, binding.Bind(createLibraryPanelCommand{}), api.Wrap(lps.createHandler))
libraryPanels.Post("/:uid/dashboards/:dashboardId", middleware.ReqSignedIn, api.Wrap(lps.connectHandler))
libraryPanels.Delete("/:uid", middleware.ReqSignedIn, api.Wrap(lps.deleteHandler))
libraryPanels.Delete("/:uid/dashboards/:dashboardId", middleware.ReqSignedIn, api.Wrap(lps.disconnectHandler))
libraryPanels.Get("/", middleware.ReqSignedIn, api.Wrap(lps.getAllHandler))
libraryPanels.Get("/:uid", middleware.ReqSignedIn, api.Wrap(lps.getHandler))
libraryPanels.Get("/:uid/dashboards/", middleware.ReqSignedIn, api.Wrap(lps.getConnectedDashboardsHandler))
libraryPanels.Patch("/:uid", middleware.ReqSignedIn, binding.Bind(patchLibraryPanelCommand{}), api.Wrap(lps.patchHandler))
libraryPanels.Post("/", middleware.ReqSignedIn, binding.Bind(createLibraryPanelCommand{}), routing.Wrap(lps.createHandler))
libraryPanels.Post("/:uid/dashboards/:dashboardId", middleware.ReqSignedIn, routing.Wrap(lps.connectHandler))
libraryPanels.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(lps.deleteHandler))
libraryPanels.Delete("/:uid/dashboards/:dashboardId", middleware.ReqSignedIn, routing.Wrap(lps.disconnectHandler))
libraryPanels.Get("/", middleware.ReqSignedIn, routing.Wrap(lps.getAllHandler))
libraryPanels.Get("/:uid", middleware.ReqSignedIn, routing.Wrap(lps.getHandler))
libraryPanels.Get("/:uid/dashboards/", middleware.ReqSignedIn, routing.Wrap(lps.getConnectedDashboardsHandler))
libraryPanels.Patch("/:uid", middleware.ReqSignedIn, binding.Bind(patchLibraryPanelCommand{}), routing.Wrap(lps.patchHandler))
})
}
// createHandler handles POST /api/library-panels.
func (lps *LibraryPanelService) createHandler(c *models.ReqContext, cmd createLibraryPanelCommand) api.Response {
func (lps *LibraryPanelService) createHandler(c *models.ReqContext, cmd createLibraryPanelCommand) response.Response {
panel, err := lps.createLibraryPanel(c, cmd)
if err != nil {
if errors.Is(err, errLibraryPanelAlreadyExists) {
return api.Error(400, errLibraryPanelAlreadyExists.Error(), err)
return response.Error(400, errLibraryPanelAlreadyExists.Error(), err)
}
return api.Error(500, "Failed to create library panel", err)
return response.Error(500, "Failed to create library panel", err)
}
return api.JSON(200, util.DynMap{"result": panel})
return response.JSON(200, util.DynMap{"result": panel})
}
// connectHandler handles POST /api/library-panels/:uid/dashboards/:dashboardId.
func (lps *LibraryPanelService) connectHandler(c *models.ReqContext) api.Response {
func (lps *LibraryPanelService) connectHandler(c *models.ReqContext) response.Response {
if err := lps.connectDashboard(c, c.Params(":uid"), c.ParamsInt64(":dashboardId")); err != nil {
if errors.Is(err, errLibraryPanelNotFound) {
return api.Error(404, errLibraryPanelNotFound.Error(), err)
return response.Error(404, errLibraryPanelNotFound.Error(), err)
}
return api.Error(500, "Failed to connect library panel", err)
return response.Error(500, "Failed to connect library panel", err)
}
return api.Success("Library panel connected")
return response.Success("Library panel connected")
}
// deleteHandler handles DELETE /api/library-panels/:uid.
func (lps *LibraryPanelService) deleteHandler(c *models.ReqContext) api.Response {
func (lps *LibraryPanelService) deleteHandler(c *models.ReqContext) response.Response {
err := lps.deleteLibraryPanel(c, c.Params(":uid"))
if err != nil {
if errors.Is(err, errLibraryPanelNotFound) {
return api.Error(404, errLibraryPanelNotFound.Error(), err)
return response.Error(404, errLibraryPanelNotFound.Error(), err)
}
return api.Error(500, "Failed to delete library panel", err)
return response.Error(500, "Failed to delete library panel", err)
}
return api.Success("Library panel deleted")
return response.Success("Library panel deleted")
}
// disconnectHandler handles DELETE /api/library-panels/:uid/dashboards/:dashboardId.
func (lps *LibraryPanelService) disconnectHandler(c *models.ReqContext) api.Response {
func (lps *LibraryPanelService) disconnectHandler(c *models.ReqContext) response.Response {
err := lps.disconnectDashboard(c, c.Params(":uid"), c.ParamsInt64(":dashboardId"))
if err != nil {
if errors.Is(err, errLibraryPanelNotFound) {
return api.Error(404, errLibraryPanelNotFound.Error(), err)
return response.Error(404, errLibraryPanelNotFound.Error(), err)
}
if errors.Is(err, errLibraryPanelDashboardNotFound) {
return api.Error(404, errLibraryPanelDashboardNotFound.Error(), err)
return response.Error(404, errLibraryPanelDashboardNotFound.Error(), err)
}
return api.Error(500, "Failed to disconnect library panel", err)
return response.Error(500, "Failed to disconnect library panel", err)
}
return api.Success("Library panel disconnected")
return response.Success("Library panel disconnected")
}
// getHandler handles GET /api/library-panels/:uid.
func (lps *LibraryPanelService) getHandler(c *models.ReqContext) api.Response {
func (lps *LibraryPanelService) getHandler(c *models.ReqContext) response.Response {
libraryPanel, err := lps.getLibraryPanel(c, c.Params(":uid"))
if err != nil {
if errors.Is(err, errLibraryPanelNotFound) {
return api.Error(404, errLibraryPanelNotFound.Error(), err)
return response.Error(404, errLibraryPanelNotFound.Error(), err)
}
return api.Error(500, "Failed to get library panel", err)
return response.Error(500, "Failed to get library panel", err)
}
return api.JSON(200, util.DynMap{"result": libraryPanel})
return response.JSON(200, util.DynMap{"result": libraryPanel})
}
// getAllHandler handles GET /api/library-panels/.
func (lps *LibraryPanelService) getAllHandler(c *models.ReqContext) api.Response {
func (lps *LibraryPanelService) getAllHandler(c *models.ReqContext) response.Response {
libraryPanels, err := lps.getAllLibraryPanels(c)
if err != nil {
return api.Error(500, "Failed to get library panels", err)
return response.Error(500, "Failed to get library panels", err)
}
return api.JSON(200, util.DynMap{"result": libraryPanels})
return response.JSON(200, util.DynMap{"result": libraryPanels})
}
// getConnectedDashboardsHandler handles GET /api/library-panels/:uid/dashboards/.
func (lps *LibraryPanelService) getConnectedDashboardsHandler(c *models.ReqContext) api.Response {
func (lps *LibraryPanelService) getConnectedDashboardsHandler(c *models.ReqContext) response.Response {
dashboardIDs, err := lps.getConnectedDashboards(c, c.Params(":uid"))
if err != nil {
if errors.Is(err, errLibraryPanelNotFound) {
return api.Error(404, errLibraryPanelNotFound.Error(), err)
return response.Error(404, errLibraryPanelNotFound.Error(), err)
}
return api.Error(500, "Failed to get connected dashboards", err)
return response.Error(500, "Failed to get connected dashboards", err)
}
return api.JSON(200, util.DynMap{"result": dashboardIDs})
return response.JSON(200, util.DynMap{"result": dashboardIDs})
}
// patchHandler handles PATCH /api/library-panels/:uid
func (lps *LibraryPanelService) patchHandler(c *models.ReqContext, cmd patchLibraryPanelCommand) api.Response {
func (lps *LibraryPanelService) patchHandler(c *models.ReqContext, cmd patchLibraryPanelCommand) response.Response {
libraryPanel, err := lps.patchLibraryPanel(c, cmd, c.Params(":uid"))
if err != nil {
if errors.Is(err, errLibraryPanelAlreadyExists) {
return api.Error(400, errLibraryPanelAlreadyExists.Error(), err)
return response.Error(400, errLibraryPanelAlreadyExists.Error(), err)
}
if errors.Is(err, errLibraryPanelNotFound) {
return api.Error(404, errLibraryPanelNotFound.Error(), err)
return response.Error(404, errLibraryPanelNotFound.Error(), err)
}
return api.Error(500, "Failed to update library panel", err)
return response.Error(500, "Failed to update library panel", err)
}
return api.JSON(200, util.DynMap{"result": libraryPanel})
return response.JSON(200, util.DynMap{"result": libraryPanel})
}

View File

@@ -3,7 +3,7 @@ package ngalert
import (
"github.com/go-macaron/binding"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
@@ -14,79 +14,79 @@ import (
func (ng *AlertNG) registerAPIEndpoints() {
ng.RouteRegister.Group("/api/alert-definitions", func(alertDefinitions routing.RouteRegister) {
alertDefinitions.Get("", middleware.ReqSignedIn, api.Wrap(ng.listAlertDefinitions))
alertDefinitions.Get("/eval/:alertDefinitionUID", ng.validateOrgAlertDefinition, api.Wrap(ng.alertDefinitionEvalEndpoint))
alertDefinitions.Post("/eval", middleware.ReqSignedIn, binding.Bind(evalAlertConditionCommand{}), api.Wrap(ng.conditionEvalEndpoint))
alertDefinitions.Get("/:alertDefinitionUID", ng.validateOrgAlertDefinition, api.Wrap(ng.getAlertDefinitionEndpoint))
alertDefinitions.Delete("/:alertDefinitionUID", ng.validateOrgAlertDefinition, api.Wrap(ng.deleteAlertDefinitionEndpoint))
alertDefinitions.Post("/", middleware.ReqSignedIn, binding.Bind(saveAlertDefinitionCommand{}), api.Wrap(ng.createAlertDefinitionEndpoint))
alertDefinitions.Put("/:alertDefinitionUID", ng.validateOrgAlertDefinition, binding.Bind(updateAlertDefinitionCommand{}), api.Wrap(ng.updateAlertDefinitionEndpoint))
alertDefinitions.Get("", middleware.ReqSignedIn, routing.Wrap(ng.listAlertDefinitions))
alertDefinitions.Get("/eval/:alertDefinitionUID", ng.validateOrgAlertDefinition, routing.Wrap(ng.alertDefinitionEvalEndpoint))
alertDefinitions.Post("/eval", middleware.ReqSignedIn, binding.Bind(evalAlertConditionCommand{}), routing.Wrap(ng.conditionEvalEndpoint))
alertDefinitions.Get("/:alertDefinitionUID", ng.validateOrgAlertDefinition, routing.Wrap(ng.getAlertDefinitionEndpoint))
alertDefinitions.Delete("/:alertDefinitionUID", ng.validateOrgAlertDefinition, routing.Wrap(ng.deleteAlertDefinitionEndpoint))
alertDefinitions.Post("/", middleware.ReqSignedIn, binding.Bind(saveAlertDefinitionCommand{}), routing.Wrap(ng.createAlertDefinitionEndpoint))
alertDefinitions.Put("/:alertDefinitionUID", ng.validateOrgAlertDefinition, binding.Bind(updateAlertDefinitionCommand{}), routing.Wrap(ng.updateAlertDefinitionEndpoint))
})
ng.RouteRegister.Group("/api/ngalert/", func(schedulerRouter routing.RouteRegister) {
schedulerRouter.Post("/pause", api.Wrap(ng.pauseScheduler))
schedulerRouter.Post("/unpause", api.Wrap(ng.unpauseScheduler))
schedulerRouter.Post("/pause", routing.Wrap(ng.pauseScheduler))
schedulerRouter.Post("/unpause", routing.Wrap(ng.unpauseScheduler))
}, middleware.ReqOrgAdmin)
}
// conditionEvalEndpoint handles POST /api/alert-definitions/eval.
func (ng *AlertNG) conditionEvalEndpoint(c *models.ReqContext, dto evalAlertConditionCommand) api.Response {
func (ng *AlertNG) conditionEvalEndpoint(c *models.ReqContext, dto evalAlertConditionCommand) response.Response {
if err := ng.validateCondition(dto.Condition, c.SignedInUser); err != nil {
return api.Error(400, "invalid condition", err)
return response.Error(400, "invalid condition", err)
}
evalResults, err := eval.ConditionEval(&dto.Condition, timeNow())
if err != nil {
return api.Error(400, "Failed to evaluate conditions", err)
return response.Error(400, "Failed to evaluate conditions", err)
}
frame := evalResults.AsDataFrame()
df := tsdb.NewDecodedDataFrames([]*data.Frame{&frame})
instances, err := df.Encoded()
if err != nil {
return api.Error(400, "Failed to encode result dataframes", err)
return response.Error(400, "Failed to encode result dataframes", err)
}
return api.JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"instances": instances,
})
}
// alertDefinitionEvalEndpoint handles GET /api/alert-definitions/eval/:alertDefinitionUID.
func (ng *AlertNG) alertDefinitionEvalEndpoint(c *models.ReqContext) api.Response {
func (ng *AlertNG) alertDefinitionEvalEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.ParamsEscape(":alertDefinitionUID")
condition, err := ng.LoadAlertCondition(alertDefinitionUID, c.SignedInUser.OrgId)
if err != nil {
return api.Error(400, "Failed to load alert definition conditions", err)
return response.Error(400, "Failed to load alert definition conditions", err)
}
if err := ng.validateCondition(*condition, c.SignedInUser); err != nil {
return api.Error(400, "invalid condition", err)
return response.Error(400, "invalid condition", err)
}
evalResults, err := eval.ConditionEval(condition, timeNow())
if err != nil {
return api.Error(400, "Failed to evaluate alert", err)
return response.Error(400, "Failed to evaluate alert", err)
}
frame := evalResults.AsDataFrame()
df := tsdb.NewDecodedDataFrames([]*data.Frame{&frame})
if err != nil {
return api.Error(400, "Failed to instantiate Dataframes from the decoded frames", err)
return response.Error(400, "Failed to instantiate Dataframes from the decoded frames", err)
}
instances, err := df.Encoded()
if err != nil {
return api.Error(400, "Failed to encode result dataframes", err)
return response.Error(400, "Failed to encode result dataframes", err)
}
return api.JSON(200, util.DynMap{
return response.JSON(200, util.DynMap{
"instances": instances,
})
}
// getAlertDefinitionEndpoint handles GET /api/alert-definitions/:alertDefinitionUID.
func (ng *AlertNG) getAlertDefinitionEndpoint(c *models.ReqContext) api.Response {
func (ng *AlertNG) getAlertDefinitionEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.ParamsEscape(":alertDefinitionUID")
query := getAlertDefinitionByUIDQuery{
@@ -95,14 +95,14 @@ func (ng *AlertNG) getAlertDefinitionEndpoint(c *models.ReqContext) api.Response
}
if err := ng.getAlertDefinitionByUID(&query); err != nil {
return api.Error(500, "Failed to get alert definition", err)
return response.Error(500, "Failed to get alert definition", err)
}
return api.JSON(200, &query.Result)
return response.JSON(200, &query.Result)
}
// deleteAlertDefinitionEndpoint handles DELETE /api/alert-definitions/:alertDefinitionUID.
func (ng *AlertNG) deleteAlertDefinitionEndpoint(c *models.ReqContext) api.Response {
func (ng *AlertNG) deleteAlertDefinitionEndpoint(c *models.ReqContext) response.Response {
alertDefinitionUID := c.ParamsEscape(":alertDefinitionUID")
cmd := deleteAlertDefinitionByUIDCommand{
@@ -111,66 +111,66 @@ func (ng *AlertNG) deleteAlertDefinitionEndpoint(c *models.ReqContext) api.Respo
}
if err := ng.deleteAlertDefinitionByUID(&cmd); err != nil {
return api.Error(500, "Failed to delete alert definition", err)
return response.Error(500, "Failed to delete alert definition", err)
}
return api.Success("Alert definition deleted")
return response.Success("Alert definition deleted")
}
// updateAlertDefinitionEndpoint handles PUT /api/alert-definitions/:alertDefinitionUID.
func (ng *AlertNG) updateAlertDefinitionEndpoint(c *models.ReqContext, cmd updateAlertDefinitionCommand) api.Response {
func (ng *AlertNG) updateAlertDefinitionEndpoint(c *models.ReqContext, cmd updateAlertDefinitionCommand) response.Response {
cmd.UID = c.ParamsEscape(":alertDefinitionUID")
cmd.OrgID = c.SignedInUser.OrgId
if err := ng.validateCondition(cmd.Condition, c.SignedInUser); err != nil {
return api.Error(400, "invalid condition", err)
return response.Error(400, "invalid condition", err)
}
if err := ng.updateAlertDefinition(&cmd); err != nil {
return api.Error(500, "Failed to update alert definition", err)
return response.Error(500, "Failed to update alert definition", err)
}
return api.JSON(200, cmd.Result)
return response.JSON(200, cmd.Result)
}
// createAlertDefinitionEndpoint handles POST /api/alert-definitions.
func (ng *AlertNG) createAlertDefinitionEndpoint(c *models.ReqContext, cmd saveAlertDefinitionCommand) api.Response {
func (ng *AlertNG) createAlertDefinitionEndpoint(c *models.ReqContext, cmd saveAlertDefinitionCommand) response.Response {
cmd.OrgID = c.SignedInUser.OrgId
if err := ng.validateCondition(cmd.Condition, c.SignedInUser); err != nil {
return api.Error(400, "invalid condition", err)
return response.Error(400, "invalid condition", err)
}
if err := ng.saveAlertDefinition(&cmd); err != nil {
return api.Error(500, "Failed to create alert definition", err)
return response.Error(500, "Failed to create alert definition", err)
}
return api.JSON(200, cmd.Result)
return response.JSON(200, cmd.Result)
}
// listAlertDefinitions handles GET /api/alert-definitions.
func (ng *AlertNG) listAlertDefinitions(c *models.ReqContext) api.Response {
func (ng *AlertNG) listAlertDefinitions(c *models.ReqContext) response.Response {
query := listAlertDefinitionsQuery{OrgID: c.SignedInUser.OrgId}
if err := ng.getOrgAlertDefinitions(&query); err != nil {
return api.Error(500, "Failed to list alert definitions", err)
return response.Error(500, "Failed to list alert definitions", err)
}
return api.JSON(200, util.DynMap{"results": query.Result})
return response.JSON(200, util.DynMap{"results": query.Result})
}
func (ng *AlertNG) pauseScheduler() api.Response {
func (ng *AlertNG) pauseScheduler() response.Response {
err := ng.schedule.pause()
if err != nil {
return api.Error(500, "Failed to pause scheduler", err)
return response.Error(500, "Failed to pause scheduler", err)
}
return api.JSON(200, util.DynMap{"message": "alert definition scheduler paused"})
return response.JSON(200, util.DynMap{"message": "alert definition scheduler paused"})
}
func (ng *AlertNG) unpauseScheduler() api.Response {
func (ng *AlertNG) unpauseScheduler() response.Response {
err := ng.schedule.unpause()
if err != nil {
return api.Error(500, "Failed to unpause scheduler", err)
return response.Error(500, "Failed to unpause scheduler", err)
}
return api.JSON(200, util.DynMap{"message": "alert definition scheduler unpaused"})
return response.JSON(200, util.DynMap{"message": "alert definition scheduler unpaused"})
}