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:
@@ -0,0 +1,116 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
)
|
||||
|
||||
var varRegex = regexp.MustCompile(`(\$\{.+?\})`)
|
||||
|
||||
type DashboardInputMissingError struct {
|
||||
VariableName string
|
||||
}
|
||||
|
||||
func (e DashboardInputMissingError) Error() string {
|
||||
return fmt.Sprintf("Dashboard input variable: %v missing from import command", e.VariableName)
|
||||
}
|
||||
|
||||
type DashTemplateEvaluator struct {
|
||||
template *simplejson.Json
|
||||
inputs []dashboardimport.ImportDashboardInput
|
||||
variables map[string]string
|
||||
result *simplejson.Json
|
||||
}
|
||||
|
||||
func NewDashTemplateEvaluator(template *simplejson.Json, inputs []dashboardimport.ImportDashboardInput) *DashTemplateEvaluator {
|
||||
return &DashTemplateEvaluator{
|
||||
template: template,
|
||||
inputs: inputs,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DashTemplateEvaluator) findInput(varName string, varType string) *dashboardimport.ImportDashboardInput {
|
||||
for _, input := range e.inputs {
|
||||
if varType == input.Type && (input.Name == varName || input.Name == "*") {
|
||||
return &input
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
|
||||
e.result = simplejson.New()
|
||||
e.variables = make(map[string]string)
|
||||
|
||||
// check that we have all inputs we need
|
||||
for _, inputDef := range e.template.Get("__inputs").MustArray() {
|
||||
inputDefJson := simplejson.NewFromAny(inputDef)
|
||||
inputName := inputDefJson.Get("name").MustString()
|
||||
inputType := inputDefJson.Get("type").MustString()
|
||||
input := e.findInput(inputName, inputType)
|
||||
|
||||
// force expressions value to `__expr__`
|
||||
if inputDefJson.Get("pluginId").MustString() == expr.DatasourceType {
|
||||
input = &dashboardimport.ImportDashboardInput{
|
||||
Value: expr.DatasourceType,
|
||||
}
|
||||
}
|
||||
|
||||
if input == nil {
|
||||
return nil, &DashboardInputMissingError{VariableName: inputName}
|
||||
}
|
||||
|
||||
e.variables["${"+inputName+"}"] = input.Value
|
||||
}
|
||||
|
||||
return simplejson.NewFromAny(e.evalObject(e.template)), nil
|
||||
}
|
||||
|
||||
func (e *DashTemplateEvaluator) evalValue(source *simplejson.Json) interface{} {
|
||||
sourceValue := source.Interface()
|
||||
|
||||
switch v := sourceValue.(type) {
|
||||
case string:
|
||||
interpolated := varRegex.ReplaceAllStringFunc(v, func(match string) string {
|
||||
replacement, exists := e.variables[match]
|
||||
if exists {
|
||||
return replacement
|
||||
}
|
||||
return match
|
||||
})
|
||||
return interpolated
|
||||
case bool:
|
||||
return v
|
||||
case json.Number:
|
||||
return v
|
||||
case map[string]interface{}:
|
||||
return e.evalObject(source)
|
||||
case []interface{}:
|
||||
array := make([]interface{}, 0)
|
||||
for _, item := range v {
|
||||
array = append(array, e.evalValue(simplejson.NewFromAny(item)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DashTemplateEvaluator) evalObject(source *simplejson.Json) interface{} {
|
||||
result := make(map[string]interface{})
|
||||
|
||||
for key, value := range source.MustMap() {
|
||||
if key == "__inputs" {
|
||||
continue
|
||||
}
|
||||
result[key] = e.evalValue(simplejson.NewFromAny(value))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDashTemplateEvaluator(t *testing.T) {
|
||||
template, err := simplejson.NewJson([]byte(`{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_NAME",
|
||||
"type": "datasource"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"prop": "${DS_NAME}_${DS_NAME}"
|
||||
}
|
||||
}`))
|
||||
require.NoError(t, err)
|
||||
|
||||
evaluator := &DashTemplateEvaluator{
|
||||
template: template,
|
||||
inputs: []dashboardimport.ImportDashboardInput{
|
||||
{Name: "*", Type: "datasource", Value: "my-server"},
|
||||
},
|
||||
}
|
||||
|
||||
res, err := evaluator.Eval()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "my-server_my-server", res.GetPath("test", "prop").MustString())
|
||||
|
||||
inputs := res.Get("__inputs")
|
||||
require.Nil(t, inputs.Interface())
|
||||
}
|
||||
Reference in New Issue
Block a user