mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 17:43:35 -06:00
* Chore: Add user service method SetUsingOrg * Chore: Add user service method GetSignedInUserWithCacheCtx * Use method GetSignedInUserWithCacheCtx from user service * Fix lint after rebase * Fix lint * Fix lint error * roll back some changes * Roll back changes in api and middleware * Add xorm tags to SignedInUser ID fields
242 lines
8.7 KiB
Go
242 lines
8.7 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"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/api/routing"
|
|
"github.com/grafana/grafana/pkg/middleware"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
|
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
|
"github.com/grafana/grafana/pkg/services/query"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
type Api struct {
|
|
PublicDashboardService publicdashboards.Service
|
|
RouteRegister routing.RouteRegister
|
|
AccessControl accesscontrol.AccessControl
|
|
QueryDataService *query.Service
|
|
Features *featuremgmt.FeatureManager
|
|
}
|
|
|
|
func ProvideApi(
|
|
pd publicdashboards.Service,
|
|
rr routing.RouteRegister,
|
|
ac accesscontrol.AccessControl,
|
|
qds *query.Service,
|
|
features *featuremgmt.FeatureManager,
|
|
) *Api {
|
|
api := &Api{
|
|
PublicDashboardService: pd,
|
|
RouteRegister: rr,
|
|
AccessControl: ac,
|
|
QueryDataService: qds,
|
|
Features: features,
|
|
}
|
|
|
|
// attach api if PublicDashboards feature flag is enabled
|
|
if features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
|
api.RegisterAPIEndpoints()
|
|
}
|
|
|
|
return api
|
|
}
|
|
|
|
func (api *Api) RegisterAPIEndpoints() {
|
|
auth := accesscontrol.Middleware(api.AccessControl)
|
|
reqSignedIn := middleware.ReqSignedIn
|
|
|
|
// Anonymous access to public dashboard route is configured in pkg/api/api.go
|
|
// because it is deeply dependent on the HTTPServer.Index() method and would result in a
|
|
// circular dependency
|
|
|
|
api.RouteRegister.Get("/api/public/dashboards/:accessToken", routing.Wrap(api.GetPublicDashboard))
|
|
api.RouteRegister.Post("/api/public/dashboards/:accessToken/panels/:panelId/query", routing.Wrap(api.QueryPublicDashboard))
|
|
|
|
// Create/Update Public Dashboard
|
|
api.RouteRegister.Get("/api/dashboards/uid/:uid/public-config", auth(reqSignedIn, accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(api.GetPublicDashboardConfig))
|
|
api.RouteRegister.Post("/api/dashboards/uid/:uid/public-config", auth(reqSignedIn, accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite)), routing.Wrap(api.SavePublicDashboardConfig))
|
|
}
|
|
|
|
// gets public dashboard
|
|
func (api *Api) GetPublicDashboard(c *models.ReqContext) response.Response {
|
|
accessToken := web.Params(c.Req)[":accessToken"]
|
|
|
|
dash, err := api.PublicDashboardService.GetPublicDashboard(c.Req.Context(), accessToken)
|
|
if err != nil {
|
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard", err)
|
|
}
|
|
|
|
pubDash, err := api.PublicDashboardService.GetPublicDashboardConfig(c.Req.Context(), dash.OrgId, dash.Uid)
|
|
if err != nil {
|
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard config", err)
|
|
}
|
|
|
|
meta := dtos.DashboardMeta{
|
|
Slug: dash.Slug,
|
|
Type: models.DashTypeDB,
|
|
CanStar: false,
|
|
CanSave: false,
|
|
CanEdit: false,
|
|
CanAdmin: false,
|
|
CanDelete: false,
|
|
Created: dash.Created,
|
|
Updated: dash.Updated,
|
|
Version: dash.Version,
|
|
IsFolder: false,
|
|
FolderId: dash.FolderId,
|
|
PublicDashboardAccessToken: accessToken,
|
|
PublicDashboardUID: pubDash.Uid,
|
|
}
|
|
|
|
dto := dtos.DashboardFullWithMeta{Meta: meta, Dashboard: dash.Data}
|
|
|
|
return response.JSON(http.StatusOK, dto)
|
|
}
|
|
|
|
// gets public dashboard configuration for dashboard
|
|
func (api *Api) GetPublicDashboardConfig(c *models.ReqContext) response.Response {
|
|
pdc, err := api.PublicDashboardService.GetPublicDashboardConfig(c.Req.Context(), c.OrgID, web.Params(c.Req)[":uid"])
|
|
if err != nil {
|
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard config", err)
|
|
}
|
|
return response.JSON(http.StatusOK, pdc)
|
|
}
|
|
|
|
// sets public dashboard configuration for dashboard
|
|
func (api *Api) SavePublicDashboardConfig(c *models.ReqContext) response.Response {
|
|
pubdash := &PublicDashboard{}
|
|
if err := web.Bind(c.Req, pubdash); err != nil {
|
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
|
}
|
|
|
|
// Always set the org id to the current auth session orgId
|
|
pubdash.OrgId = c.OrgID
|
|
|
|
dto := SavePublicDashboardConfigDTO{
|
|
OrgId: c.OrgID,
|
|
DashboardUid: web.Params(c.Req)[":uid"],
|
|
UserId: c.UserID,
|
|
PublicDashboard: pubdash,
|
|
}
|
|
|
|
// Save the public dashboard
|
|
pubdash, err := api.PublicDashboardService.SavePublicDashboardConfig(c.Req.Context(), &dto)
|
|
if err != nil {
|
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to save public dashboard configuration", err)
|
|
}
|
|
|
|
return response.JSON(http.StatusOK, pubdash)
|
|
}
|
|
|
|
// QueryPublicDashboard returns all results for a given panel on a public dashboard
|
|
// POST /api/public/dashboard/:accessToken/panels/:panelId/query
|
|
func (api *Api) QueryPublicDashboard(c *models.ReqContext) response.Response {
|
|
panelId, err := strconv.ParseInt(web.Params(c.Req)[":panelId"], 10, 64)
|
|
if err != nil {
|
|
return response.Error(http.StatusBadRequest, "invalid panel ID", err)
|
|
}
|
|
|
|
dashboard, err := api.PublicDashboardService.GetPublicDashboard(c.Req.Context(), web.Params(c.Req)[":accessToken"])
|
|
if err != nil {
|
|
return response.Error(http.StatusInternalServerError, "could not fetch dashboard", err)
|
|
}
|
|
|
|
publicDashboard, err := api.PublicDashboardService.GetPublicDashboardConfig(c.Req.Context(), dashboard.OrgId, dashboard.Uid)
|
|
if err != nil {
|
|
return response.Error(http.StatusInternalServerError, "could not fetch public dashboard", err)
|
|
}
|
|
|
|
reqDTO, err := api.PublicDashboardService.BuildPublicDashboardMetricRequest(
|
|
c.Req.Context(),
|
|
dashboard,
|
|
publicDashboard,
|
|
panelId,
|
|
)
|
|
if err != nil {
|
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to get queries for public dashboard", err)
|
|
}
|
|
|
|
anonymousUser, err := api.PublicDashboardService.BuildAnonymousUser(c.Req.Context(), dashboard)
|
|
|
|
if err != nil {
|
|
return response.Error(http.StatusInternalServerError, "could not create anonymous user", err)
|
|
}
|
|
|
|
resp, err := api.QueryDataService.QueryDataMultipleSources(c.Req.Context(), anonymousUser, c.SkipCache, reqDTO, true)
|
|
|
|
if err != nil {
|
|
return handleQueryMetricsError(err)
|
|
}
|
|
return toJsonStreamingResponse(api.Features, resp)
|
|
}
|
|
|
|
// util to help us unpack dashboard and publicdashboard errors or use default http code and message
|
|
// we should look to do some future refactoring of these errors as publicdashboard err is the same as a dashboarderr, just defined in a
|
|
// different package.
|
|
func handleDashboardErr(defaultCode int, defaultMsg string, err error) response.Response {
|
|
var publicDashboardErr PublicDashboardErr
|
|
|
|
// handle public dashboard er
|
|
if ok := errors.As(err, &publicDashboardErr); ok {
|
|
return response.Error(publicDashboardErr.StatusCode, publicDashboardErr.Error(), publicDashboardErr)
|
|
}
|
|
|
|
// handle dashboard errors as well
|
|
var dashboardErr dashboards.DashboardErr
|
|
if ok := errors.As(err, &dashboardErr); ok {
|
|
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), dashboardErr)
|
|
}
|
|
|
|
return response.Error(defaultCode, defaultMsg, err)
|
|
}
|
|
|
|
// Copied from pkg/api/metrics.go
|
|
func handleQueryMetricsError(err error) *response.NormalResponse {
|
|
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
|
return response.Error(http.StatusForbidden, "Access denied to data source", err)
|
|
}
|
|
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
|
return response.Error(http.StatusNotFound, "Data source not found", err)
|
|
}
|
|
var badQuery *query.ErrBadQuery
|
|
if errors.As(err, &badQuery) {
|
|
return response.Error(http.StatusBadRequest, util.Capitalize(badQuery.Message), err)
|
|
}
|
|
|
|
if errors.Is(err, backendplugin.ErrPluginNotRegistered) {
|
|
return response.Error(http.StatusNotFound, "Plugin not found", err)
|
|
}
|
|
|
|
return response.Error(http.StatusInternalServerError, "Query data error", err)
|
|
}
|
|
|
|
// Copied from pkg/api/metrics.go
|
|
func toJsonStreamingResponse(features *featuremgmt.FeatureManager, qdr *backend.QueryDataResponse) response.Response {
|
|
statusWhenError := http.StatusBadRequest
|
|
if features.IsEnabled(featuremgmt.FlagDatasourceQueryMultiStatus) {
|
|
statusWhenError = http.StatusMultiStatus
|
|
}
|
|
|
|
statusCode := http.StatusOK
|
|
for _, res := range qdr.Responses {
|
|
if res.Error != nil {
|
|
statusCode = statusWhenError
|
|
}
|
|
}
|
|
|
|
return response.JSONStreaming(statusCode, qdr)
|
|
}
|