The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/dashboardimport/service/service.go

168 lines
5.6 KiB

package service
import (
"context"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboardimport/api"
"github.com/grafana/grafana/pkg/services/dashboardimport/utils"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/librarypanels"
"github.com/grafana/grafana/pkg/services/plugindashboards"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/services/quota"
)
func ProvideService(routeRegister routing.RouteRegister,
quotaService quota.Service,
pluginDashboardService plugindashboards.Service, pluginStore pluginstore.Store,
libraryPanelService librarypanels.Service, dashboardService dashboards.DashboardService,
ac accesscontrol.AccessControl, folderService folder.Service,
) *ImportDashboardService {
s := &ImportDashboardService{
pluginDashboardService: pluginDashboardService,
dashboardService: dashboardService,
libraryPanelService: libraryPanelService,
folderService: folderService,
}
dashboardImportAPI := api.New(s, quotaService, pluginStore, ac)
dashboardImportAPI.RegisterAPIEndpoints(routeRegister)
return s
}
type ImportDashboardService struct {
pluginDashboardService plugindashboards.Service
dashboardService dashboards.DashboardService
libraryPanelService librarypanels.Service
folderService folder.Service
}
func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashboardimport.ImportDashboardRequest) (*dashboardimport.ImportDashboardResponse, error) {
var draftDashboard *dashboards.Dashboard
if req.PluginId != "" {
loadReq := &plugindashboards.LoadPluginDashboardRequest{
PluginID: req.PluginId,
Reference: req.Path,
}
if resp, err := s.pluginDashboardService.LoadPluginDashboard(ctx, loadReq); err != nil {
return nil, err
} else {
draftDashboard = resp.Dashboard
}
} else {
draftDashboard = dashboards.NewDashboardFromJson(req.Dashboard)
}
evaluator := utils.NewDashTemplateEvaluator(draftDashboard.Data, req.Inputs)
generatedDash, err := evaluator.Eval()
if err != nil {
return nil, err
}
// Maintain backwards compatibility by transforming array of library elements to map
libraryElements := generatedDash.Get("__elements")
libElementsArr, err := libraryElements.Array()
if err == nil {
elementMap := map[string]any{}
for _, el := range libElementsArr {
libElement := simplejson.NewFromAny(el)
elementMap[libElement.Get("uid").MustString()] = el
}
libraryElements = simplejson.NewFromAny(elementMap)
}
// No need to keep these in the stored dashboard JSON
generatedDash.Del("__elements")
generatedDash.Del("__inputs")
generatedDash.Del("__requires")
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
// here we need to get FolderId from FolderUID if it present in the request, if both exist, FolderUID would overwrite FolderID
if req.FolderUid != "" {
folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{
OrgID: req.User.GetOrgID(),
UID: &req.FolderUid,
SignedInUser: req.User,
})
if err != nil {
return nil, err
}
// nolint:staticcheck
req.FolderId = folder.ID
} else {
folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{
ID: &req.FolderId, // nolint:staticcheck
OrgID: req.User.GetOrgID(),
SignedInUser: req.User,
})
if err != nil {
return nil, err
}
req.FolderUid = folder.UID
}
var userID int64
if id, err := identity.UserIdentifier(req.User.GetID()); err == nil {
userID = id
}
saveCmd := dashboards.SaveDashboardCommand{
Dashboard: generatedDash,
OrgID: req.User.GetOrgID(),
UserID: userID,
Overwrite: req.Overwrite,
PluginID: req.PluginId,
FolderID: req.FolderId, // nolint:staticcheck
FolderUID: req.FolderUid,
}
dto := &dashboards.SaveDashboardDTO{
OrgID: saveCmd.OrgID,
Dashboard: saveCmd.GetDashboardModel(),
Overwrite: saveCmd.Overwrite,
User: req.User,
}
savedDashboard, err := s.dashboardService.ImportDashboard(ctx, dto)
if err != nil {
return nil, err
}
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
// nolint:staticcheck
err = s.libraryPanelService.ImportLibraryPanelsForDashboard(ctx, req.User, libraryElements, generatedDash.Get("panels").MustArray(), req.FolderId, req.FolderUid)
if err != nil {
return nil, err
}
err = s.libraryPanelService.ConnectLibraryPanelsForDashboard(ctx, req.User, savedDashboard)
if err != nil {
return nil, err
}
revision := savedDashboard.Data.Get("revision").MustInt64(0)
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
return &dashboardimport.ImportDashboardResponse{
UID: savedDashboard.UID,
PluginId: req.PluginId,
Title: savedDashboard.Title,
Path: req.Path,
Revision: revision, // only used for plugin version tracking
FolderId: savedDashboard.FolderID, // nolint:staticcheck
FolderUID: req.FolderUid,
ImportedUri: "db/" + savedDashboard.Slug,
ImportedUrl: savedDashboard.GetURL(),
ImportedRevision: revision,
Imported: true,
DashboardId: savedDashboard.ID,
Slug: savedDashboard.Slug,
}, nil
}