1package manager
2
3import (
4	"context"
5	"os"
6	"path/filepath"
7
8	"github.com/grafana/grafana/pkg/bus"
9	"github.com/grafana/grafana/pkg/components/simplejson"
10	"github.com/grafana/grafana/pkg/models"
11	"github.com/grafana/grafana/pkg/plugins"
12	"github.com/grafana/grafana/pkg/services/dashboards"
13)
14
15func (m *PluginManager) GetPluginDashboards(orgID int64, pluginID string) ([]*plugins.PluginDashboardInfoDTO, error) {
16	plugin, exists := m.Plugin(context.TODO(), pluginID)
17	if !exists {
18		return nil, plugins.NotFoundError{PluginID: pluginID}
19	}
20
21	result := make([]*plugins.PluginDashboardInfoDTO, 0)
22
23	// load current dashboards
24	query := models.GetDashboardsByPluginIdQuery{OrgId: orgID, PluginId: pluginID}
25	if err := bus.Dispatch(&query); err != nil {
26		return nil, err
27	}
28
29	existingMatches := make(map[int64]bool)
30	for _, include := range plugin.Includes {
31		if include.Type != plugins.TypeDashboard {
32			continue
33		}
34
35		dashboard, err := m.LoadPluginDashboard(plugin.ID, include.Path)
36		if err != nil {
37			return nil, err
38		}
39
40		res := &plugins.PluginDashboardInfoDTO{}
41		res.Path = include.Path
42		res.PluginId = plugin.ID
43		res.Title = dashboard.Title
44		res.Revision = dashboard.Data.Get("revision").MustInt64(1)
45
46		// find existing dashboard
47		for _, existingDash := range query.Result {
48			if existingDash.Slug == dashboard.Slug {
49				res.DashboardId = existingDash.Id
50				res.Imported = true
51				res.ImportedUri = "db/" + existingDash.Slug
52				res.ImportedUrl = existingDash.GetUrl()
53				res.ImportedRevision = existingDash.Data.Get("revision").MustInt64(1)
54				existingMatches[existingDash.Id] = true
55			}
56		}
57
58		result = append(result, res)
59	}
60
61	// find deleted dashboards
62	for _, dash := range query.Result {
63		if _, exists := existingMatches[dash.Id]; !exists {
64			result = append(result, &plugins.PluginDashboardInfoDTO{
65				Slug:        dash.Slug,
66				DashboardId: dash.Id,
67				Removed:     true,
68			})
69		}
70	}
71
72	return result, nil
73}
74
75func (m *PluginManager) LoadPluginDashboard(pluginID, path string) (*models.Dashboard, error) {
76	plugin, exists := m.Plugin(context.TODO(), pluginID)
77	if !exists {
78		return nil, plugins.NotFoundError{PluginID: pluginID}
79	}
80
81	dashboardFilePath := filepath.Join(plugin.PluginDir, path)
82	// nolint:gosec
83	// We can ignore the gosec G304 warning on this one because `plugin.PluginDir` is based
84	// on plugin folder structure on disk and not user input. `path` comes from the
85	// `plugin.json` configuration file for the loaded plugin
86	reader, err := os.Open(dashboardFilePath)
87	if err != nil {
88		return nil, err
89	}
90
91	defer func() {
92		if err := reader.Close(); err != nil {
93			m.log.Warn("Failed to close file", "path", dashboardFilePath, "err", err)
94		}
95	}()
96
97	data, err := simplejson.NewFromReader(reader)
98	if err != nil {
99		return nil, err
100	}
101
102	return models.NewDashboardFromJson(data), nil
103}
104
105func (m *PluginManager) ImportDashboard(ctx context.Context, pluginID, path string, orgID, folderID int64, dashboardModel *simplejson.Json,
106	overwrite bool, inputs []plugins.ImportDashboardInput, user *models.SignedInUser) (plugins.PluginDashboardInfoDTO,
107	*models.Dashboard, error) {
108	var dashboard *models.Dashboard
109	if pluginID != "" {
110		var err error
111		if dashboard, err = m.LoadPluginDashboard(pluginID, path); err != nil {
112			return plugins.PluginDashboardInfoDTO{}, &models.Dashboard{}, err
113		}
114	} else {
115		dashboard = models.NewDashboardFromJson(dashboardModel)
116	}
117
118	evaluator := &DashTemplateEvaluator{
119		template: dashboard.Data,
120		inputs:   inputs,
121	}
122
123	generatedDash, err := evaluator.Eval()
124	if err != nil {
125		return plugins.PluginDashboardInfoDTO{}, &models.Dashboard{}, err
126	}
127
128	saveCmd := models.SaveDashboardCommand{
129		Dashboard: generatedDash,
130		OrgId:     orgID,
131		UserId:    user.UserId,
132		Overwrite: overwrite,
133		PluginId:  pluginID,
134		FolderId:  folderID,
135	}
136
137	dto := &dashboards.SaveDashboardDTO{
138		OrgId:     orgID,
139		Dashboard: saveCmd.GetDashboardModel(),
140		Overwrite: saveCmd.Overwrite,
141		User:      user,
142	}
143
144	savedDash, err := dashboards.NewService(m.sqlStore).ImportDashboard(ctx, dto)
145	if err != nil {
146		return plugins.PluginDashboardInfoDTO{}, &models.Dashboard{}, err
147	}
148
149	return plugins.PluginDashboardInfoDTO{
150		PluginId:         pluginID,
151		Title:            savedDash.Title,
152		Path:             path,
153		Revision:         savedDash.Data.Get("revision").MustInt64(1),
154		FolderId:         savedDash.FolderId,
155		ImportedUri:      "db/" + savedDash.Slug,
156		ImportedUrl:      savedDash.GetUrl(),
157		ImportedRevision: dashboard.Data.Get("revision").MustInt64(1),
158		Imported:         true,
159		DashboardId:      savedDash.Id,
160		Slug:             savedDash.Slug,
161	}, savedDash, nil
162}
163