Files
grafana/pkg/plugins/dashboard_importer.go
Marcus Efraimsson 87284d284e dashboard: fix import dashboard with alert rule
Importing a dashboard with alert rule(s) should be possible without receiving invalid
alert data error. This fix reverts the import logic to how it worked before Grafana v5.0,
that is import will allow dashboard with alert rule(s) but no alerts will be created.
After an import the user will need to update the dashboard for the alerts to be created.
Fixes #11227
2018-03-14 14:24:56 +01:00

187 lines
4.3 KiB
Go

package plugins
import (
"encoding/json"
"fmt"
"regexp"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
)
type ImportDashboardCommand struct {
Dashboard *simplejson.Json
Path string
Inputs []ImportDashboardInput
Overwrite bool
OrgId int64
User *m.SignedInUser
PluginId string
Result *PluginDashboardInfoDTO
}
type ImportDashboardInput struct {
Type string `json:"type"`
PluginId string `json:"pluginId"`
Name string `json:"name"`
Value string `json:"value"`
}
type DashboardInputMissingError struct {
VariableName string
}
func (e DashboardInputMissingError) Error() string {
return fmt.Sprintf("Dashboard input variable: %v missing from import command", e.VariableName)
}
func init() {
bus.AddHandler("plugins", ImportDashboard)
}
func ImportDashboard(cmd *ImportDashboardCommand) error {
var dashboard *m.Dashboard
var err error
if cmd.PluginId != "" {
if dashboard, err = loadPluginDashboard(cmd.PluginId, cmd.Path); err != nil {
return err
}
} else {
dashboard = m.NewDashboardFromJson(cmd.Dashboard)
}
evaluator := &DashTemplateEvaluator{
template: dashboard.Data,
inputs: cmd.Inputs,
}
generatedDash, err := evaluator.Eval()
if err != nil {
return err
}
saveCmd := m.SaveDashboardCommand{
Dashboard: generatedDash,
OrgId: cmd.OrgId,
UserId: cmd.User.UserId,
Overwrite: cmd.Overwrite,
PluginId: cmd.PluginId,
FolderId: dashboard.FolderId,
}
dto := &dashboards.SaveDashboardDTO{
OrgId: cmd.OrgId,
Dashboard: saveCmd.GetDashboardModel(),
Overwrite: saveCmd.Overwrite,
User: cmd.User,
}
savedDash, err := dashboards.NewService().ImportDashboard(dto)
if err != nil {
return err
}
cmd.Result = &PluginDashboardInfoDTO{
PluginId: cmd.PluginId,
Title: savedDash.Title,
Path: cmd.Path,
Revision: savedDash.Data.Get("revision").MustInt64(1),
ImportedUri: "db/" + savedDash.Slug,
ImportedUrl: savedDash.GetUrl(),
ImportedRevision: dashboard.Data.Get("revision").MustInt64(1),
Imported: true,
}
return nil
}
type DashTemplateEvaluator struct {
template *simplejson.Json
inputs []ImportDashboardInput
variables map[string]string
result *simplejson.Json
varRegex *regexp.Regexp
}
func (this *DashTemplateEvaluator) findInput(varName string, varType string) *ImportDashboardInput {
for _, input := range this.inputs {
if varType == input.Type && (input.Name == varName || input.Name == "*") {
return &input
}
}
return nil
}
func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
this.result = simplejson.New()
this.variables = make(map[string]string)
this.varRegex, _ = regexp.Compile(`(\$\{.+\})`)
// check that we have all inputs we need
for _, inputDef := range this.template.Get("__inputs").MustArray() {
inputDefJson := simplejson.NewFromAny(inputDef)
inputName := inputDefJson.Get("name").MustString()
inputType := inputDefJson.Get("type").MustString()
input := this.findInput(inputName, inputType)
if input == nil {
return nil, &DashboardInputMissingError{VariableName: inputName}
}
this.variables["${"+inputName+"}"] = input.Value
}
return simplejson.NewFromAny(this.evalObject(this.template)), nil
}
func (this *DashTemplateEvaluator) evalValue(source *simplejson.Json) interface{} {
sourceValue := source.Interface()
switch v := sourceValue.(type) {
case string:
interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
if replacement, exists := this.variables[match]; exists {
return replacement
} else {
return match
}
})
return interpolated
case bool:
return v
case json.Number:
return v
case map[string]interface{}:
return this.evalObject(source)
case []interface{}:
array := make([]interface{}, 0)
for _, item := range v {
array = append(array, this.evalValue(simplejson.NewFromAny(item)))
}
return array
}
return nil
}
func (this *DashTemplateEvaluator) evalObject(source *simplejson.Json) interface{} {
result := make(map[string]interface{})
for key, value := range source.MustMap() {
if key == "__inputs" {
continue
}
result[key] = this.evalValue(simplejson.NewFromAny(value))
}
return result
}