Plugins: Refactor plugin dashboards (#44315)
Moves/refactor Grafana specific functionality related to plugin dashboards out to specific services for importing dashboards and keep app plugin dashboards up-to-date. Fixes #44257
This commit is contained in:
@@ -342,7 +342,6 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
dashboardRoute.Post("/db", routing.Wrap(hs.PostDashboard))
|
||||
dashboardRoute.Get("/home", routing.Wrap(hs.GetHomeDashboard))
|
||||
dashboardRoute.Get("/tags", GetDashboardTags)
|
||||
dashboardRoute.Post("/import", routing.Wrap(hs.ImportDashboard))
|
||||
|
||||
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) {
|
||||
dashIdRoute.Get("/versions", routing.Wrap(GetDashboardVersions))
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package apierrors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// ToDashboardErrorResponse returns a different response status according to the dashboard error type
|
||||
func ToDashboardErrorResponse(ctx context.Context, pluginStore plugins.Store, err error) response.Response {
|
||||
var dashboardErr models.DashboardErr
|
||||
if ok := errors.As(err, &dashboardErr); ok {
|
||||
if body := dashboardErr.Body(); body != nil {
|
||||
return response.JSON(dashboardErr.StatusCode, body)
|
||||
}
|
||||
if dashboardErr.StatusCode != http.StatusBadRequest {
|
||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
|
||||
}
|
||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), nil)
|
||||
}
|
||||
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(http.StatusBadRequest, err.Error(), nil)
|
||||
}
|
||||
|
||||
var validationErr alerting.ValidationError
|
||||
if ok := errors.As(err, &validationErr); ok {
|
||||
return response.Error(http.StatusUnprocessableEntity, validationErr.Error(), err)
|
||||
}
|
||||
|
||||
var pluginErr models.UpdatePluginDashboardError
|
||||
if ok := errors.As(err, &pluginErr); ok {
|
||||
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
||||
// look up plugin name
|
||||
if plugin, exists := pluginStore.Plugin(ctx, pluginErr.PluginId); exists {
|
||||
message = fmt.Sprintf("The dashboard belongs to plugin %s.", plugin.Name)
|
||||
}
|
||||
return response.JSON(http.StatusPreconditionFailed, util.DynMap{"status": "plugin-dashboard", "message": message})
|
||||
}
|
||||
|
||||
return response.Error(http.StatusInternalServerError, "Failed to save dashboard", err)
|
||||
}
|
||||
+2
-35
@@ -10,6 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/apierrors"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -381,7 +382,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return hs.dashboardSaveErrorToApiResponse(ctx, err)
|
||||
return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, err)
|
||||
}
|
||||
|
||||
if hs.Cfg.EditorsCanAdmin && newDashboard {
|
||||
@@ -409,40 +410,6 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
||||
})
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) dashboardSaveErrorToApiResponse(ctx context.Context, err error) response.Response {
|
||||
var dashboardErr models.DashboardErr
|
||||
if ok := errors.As(err, &dashboardErr); ok {
|
||||
if body := dashboardErr.Body(); body != nil {
|
||||
return response.JSON(dashboardErr.StatusCode, body)
|
||||
}
|
||||
if dashboardErr.StatusCode != 400 {
|
||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), err)
|
||||
}
|
||||
return response.Error(dashboardErr.StatusCode, dashboardErr.Error(), nil)
|
||||
}
|
||||
|
||||
if errors.Is(err, models.ErrFolderNotFound) {
|
||||
return response.Error(400, err.Error(), nil)
|
||||
}
|
||||
|
||||
var validationErr alerting.ValidationError
|
||||
if ok := errors.As(err, &validationErr); ok {
|
||||
return response.Error(422, validationErr.Error(), err)
|
||||
}
|
||||
|
||||
var pluginErr models.UpdatePluginDashboardError
|
||||
if ok := errors.As(err, &pluginErr); ok {
|
||||
message := fmt.Sprintf("The dashboard belongs to plugin %s.", pluginErr.PluginId)
|
||||
// look up plugin name
|
||||
if plugin, exists := hs.pluginStore.Plugin(ctx, pluginErr.PluginId); exists {
|
||||
message = fmt.Sprintf("The dashboard belongs to plugin %s.", plugin.Name)
|
||||
}
|
||||
return response.JSON(412, util.DynMap{"status": "plugin-dashboard", "message": message})
|
||||
}
|
||||
|
||||
return response.Error(500, "Failed to save dashboard", err)
|
||||
}
|
||||
|
||||
// GetHomeDashboard returns the home dashboard.
|
||||
func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) response.Response {
|
||||
prefsQuery := models.GetPreferencesWithDefaultsQuery{User: c.SignedInUser}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package dtos
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
)
|
||||
|
||||
@@ -59,16 +58,6 @@ func (slice PluginList) Swap(i, j int) {
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
|
||||
type ImportDashboardCommand struct {
|
||||
PluginId string `json:"pluginId"`
|
||||
Path string `json:"path"`
|
||||
Overwrite bool `json:"overwrite"`
|
||||
Dashboard *simplejson.Json `json:"dashboard"`
|
||||
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
||||
FolderId int64 `json:"folderId"`
|
||||
FolderUid string `json:"folderUid"`
|
||||
}
|
||||
|
||||
type InstallPluginCommand struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
@@ -217,51 +217,6 @@ func (hs *HTTPServer) GetPluginMarkdown(c *models.ReqContext) response.Response
|
||||
return resp
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) ImportDashboard(c *models.ReqContext) response.Response {
|
||||
apiCmd := dtos.ImportDashboardCommand{}
|
||||
if err := web.Bind(c.Req, &apiCmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
var err error
|
||||
if apiCmd.PluginId == "" && apiCmd.Dashboard == nil {
|
||||
return response.Error(422, "Dashboard must be set", nil)
|
||||
}
|
||||
|
||||
limitReached, err := hs.QuotaService.QuotaReached(c, "dashboard")
|
||||
if err != nil {
|
||||
return response.Error(500, "failed to get quota", err)
|
||||
}
|
||||
if limitReached {
|
||||
return response.Error(403, "Quota reached", nil)
|
||||
}
|
||||
|
||||
trimDefaults := c.QueryBoolWithDefault("trimdefaults", true)
|
||||
if trimDefaults && !hs.LoadSchemaService.IsDisabled() {
|
||||
apiCmd.Dashboard, err = hs.LoadSchemaService.DashboardApplyDefaults(apiCmd.Dashboard)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while applying default value to the dashboard json", err)
|
||||
}
|
||||
}
|
||||
|
||||
dashInfo, dash, err := hs.pluginDashboardManager.ImportDashboard(c.Req.Context(), apiCmd.PluginId, apiCmd.Path, c.OrgId, apiCmd.FolderId,
|
||||
apiCmd.Dashboard, apiCmd.Overwrite, apiCmd.Inputs, c.SignedInUser)
|
||||
if err != nil {
|
||||
return hs.dashboardSaveErrorToApiResponse(c.Req.Context(), err)
|
||||
}
|
||||
|
||||
err = hs.LibraryPanelService.ImportLibraryPanelsForDashboard(c.Req.Context(), c.SignedInUser, dash, apiCmd.FolderId)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while importing library panels", err)
|
||||
}
|
||||
|
||||
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(c.Req.Context(), c.SignedInUser, dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while connecting library panels", err)
|
||||
}
|
||||
|
||||
return response.JSON(200, dashInfo)
|
||||
}
|
||||
|
||||
// CollectPluginMetrics collect metrics from a plugin.
|
||||
//
|
||||
// /api/plugins/:pluginId/metrics
|
||||
|
||||
Reference in New Issue
Block a user