mirror of https://github.com/grafana/grafana.git
lib(): added simplejson lib
This commit is contained in:
parent
c5a817194a
commit
1f9f439acb
|
|
@ -130,11 +130,14 @@ func handleGetNamespaces(req *cwRequest, c *middleware.Context) {
|
||||||
for key := range metricsMap {
|
for key := range metricsMap {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
}
|
}
|
||||||
if customMetricsNamespaces, ok := req.DataSource.JsonData["customMetricsNamespaces"].(string); ok {
|
|
||||||
for _, key := range strings.Split(customMetricsNamespaces, ",") {
|
customNamespaces := req.DataSource.JsonData.Get("customMetricsNamespaces").MustString()
|
||||||
|
if customNamespaces != "" {
|
||||||
|
for _, key := range strings.Split(customNamespaces, ",") {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(sort.StringSlice(keys))
|
sort.Sort(sort.StringSlice(keys))
|
||||||
|
|
||||||
result := []interface{}{}
|
result := []interface{}{}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
@ -57,21 +58,21 @@ type DashboardFullWithMeta struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataSource struct {
|
type DataSource struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
OrgId int64 `json:"orgId"`
|
OrgId int64 `json:"orgId"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Access m.DsAccess `json:"access"`
|
Access m.DsAccess `json:"access"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
BasicAuth bool `json:"basicAuth"`
|
BasicAuth bool `json:"basicAuth"`
|
||||||
BasicAuthUser string `json:"basicAuthUser"`
|
BasicAuthUser string `json:"basicAuthUser"`
|
||||||
BasicAuthPassword string `json:"basicAuthPassword"`
|
BasicAuthPassword string `json:"basicAuthPassword"`
|
||||||
WithCredentials bool `json:"withCredentials"`
|
WithCredentials bool `json:"withCredentials"`
|
||||||
IsDefault bool `json:"isDefault"`
|
IsDefault bool `json:"isDefault"`
|
||||||
JsonData map[string]interface{} `json:"jsonData,omitempty"`
|
JsonData *simplejson.Json `json:"jsonData,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetricQueryResultDto struct {
|
type MetricQueryResultDto struct {
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
||||||
defaultDatasource = ds.Name
|
defaultDatasource = ds.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ds.JsonData) > 0 {
|
if len(ds.JsonData.MustMap()) > 0 {
|
||||||
dsMap["jsonData"] = ds.JsonData
|
dsMap["jsonData"] = ds.JsonData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,10 @@ func (v *Value) Interface() interface{} {
|
||||||
return v.data
|
return v.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Value) StringMap() map[string]interface{} {
|
||||||
|
return v.data.(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
// Private Get
|
// Private Get
|
||||||
func (v *Value) get(key string) (*Value, error) {
|
func (v *Value) get(key string) (*Value, error) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,461 @@
|
||||||
|
package simplejson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// returns the current implementation version
|
||||||
|
func Version() string {
|
||||||
|
return "0.5.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Json struct {
|
||||||
|
data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Json) FromDB(data []byte) error {
|
||||||
|
j.data = make(map[string]interface{})
|
||||||
|
|
||||||
|
dec := json.NewDecoder(bytes.NewBuffer(data))
|
||||||
|
dec.UseNumber()
|
||||||
|
return dec.Decode(&j.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Json) ToDB() ([]byte, error) {
|
||||||
|
return j.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJson returns a pointer to a new `Json` object
|
||||||
|
// after unmarshaling `body` bytes
|
||||||
|
func NewJson(body []byte) (*Json, error) {
|
||||||
|
j := new(Json)
|
||||||
|
err := j.UnmarshalJSON(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a pointer to a new, empty `Json` object
|
||||||
|
func New() *Json {
|
||||||
|
return &Json{
|
||||||
|
data: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface returns the underlying data
|
||||||
|
func (j *Json) Interface() interface{} {
|
||||||
|
return j.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns its marshaled data as `[]byte`
|
||||||
|
func (j *Json) Encode() ([]byte, error) {
|
||||||
|
return j.MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodePretty returns its marshaled data as `[]byte` with indentation
|
||||||
|
func (j *Json) EncodePretty() ([]byte, error) {
|
||||||
|
return json.MarshalIndent(&j.data, "", " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the json.Marshaler interface.
|
||||||
|
func (j *Json) MarshalJSON() ([]byte, error) {
|
||||||
|
fmt.Printf("MarshalJSON")
|
||||||
|
return json.Marshal(&j.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set modifies `Json` map by `key` and `value`
|
||||||
|
// Useful for changing single key/value in a `Json` object easily.
|
||||||
|
func (j *Json) Set(key string, val interface{}) {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPath modifies `Json`, recursively checking/creating map keys for the supplied path,
|
||||||
|
// and then finally writing in the value
|
||||||
|
func (j *Json) SetPath(branch []string, val interface{}) {
|
||||||
|
if len(branch) == 0 {
|
||||||
|
j.data = val
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// in order to insert our branch, we need map[string]interface{}
|
||||||
|
if _, ok := (j.data).(map[string]interface{}); !ok {
|
||||||
|
// have to replace with something suitable
|
||||||
|
j.data = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
curr := j.data.(map[string]interface{})
|
||||||
|
|
||||||
|
for i := 0; i < len(branch)-1; i++ {
|
||||||
|
b := branch[i]
|
||||||
|
// key exists?
|
||||||
|
if _, ok := curr[b]; !ok {
|
||||||
|
n := make(map[string]interface{})
|
||||||
|
curr[b] = n
|
||||||
|
curr = n
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the value is the right sort of thing
|
||||||
|
if _, ok := curr[b].(map[string]interface{}); !ok {
|
||||||
|
// have to replace with something suitable
|
||||||
|
n := make(map[string]interface{})
|
||||||
|
curr[b] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
curr = curr[b].(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// add remaining k/v
|
||||||
|
curr[branch[len(branch)-1]] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del modifies `Json` map by deleting `key` if it is present.
|
||||||
|
func (j *Json) Del(key string) {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(m, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a pointer to a new `Json` object
|
||||||
|
// for `key` in its `map` representation
|
||||||
|
//
|
||||||
|
// useful for chaining operations (to traverse a nested JSON):
|
||||||
|
// js.Get("top_level").Get("dict").Get("value").Int()
|
||||||
|
func (j *Json) Get(key string) *Json {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err == nil {
|
||||||
|
if val, ok := m[key]; ok {
|
||||||
|
return &Json{val}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Json{nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPath searches for the item as specified by the branch
|
||||||
|
// without the need to deep dive using Get()'s.
|
||||||
|
//
|
||||||
|
// js.GetPath("top_level", "dict")
|
||||||
|
func (j *Json) GetPath(branch ...string) *Json {
|
||||||
|
jin := j
|
||||||
|
for _, p := range branch {
|
||||||
|
jin = jin.Get(p)
|
||||||
|
}
|
||||||
|
return jin
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndex returns a pointer to a new `Json` object
|
||||||
|
// for `index` in its `array` representation
|
||||||
|
//
|
||||||
|
// this is the analog to Get when accessing elements of
|
||||||
|
// a json array instead of a json object:
|
||||||
|
// js.Get("top_level").Get("array").GetIndex(1).Get("key").Int()
|
||||||
|
func (j *Json) GetIndex(index int) *Json {
|
||||||
|
a, err := j.Array()
|
||||||
|
if err == nil {
|
||||||
|
if len(a) > index {
|
||||||
|
return &Json{a[index]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Json{nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckGet returns a pointer to a new `Json` object and
|
||||||
|
// a `bool` identifying success or failure
|
||||||
|
//
|
||||||
|
// useful for chained operations when success is important:
|
||||||
|
// if data, ok := js.Get("top_level").CheckGet("inner"); ok {
|
||||||
|
// log.Println(data)
|
||||||
|
// }
|
||||||
|
func (j *Json) CheckGet(key string) (*Json, bool) {
|
||||||
|
m, err := j.Map()
|
||||||
|
if err == nil {
|
||||||
|
if val, ok := m[key]; ok {
|
||||||
|
return &Json{val}, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map type asserts to `map`
|
||||||
|
func (j *Json) Map() (map[string]interface{}, error) {
|
||||||
|
if m, ok := (j.data).(map[string]interface{}); ok {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("type assertion to map[string]interface{} failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array type asserts to an `array`
|
||||||
|
func (j *Json) Array() ([]interface{}, error) {
|
||||||
|
if a, ok := (j.data).([]interface{}); ok {
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("type assertion to []interface{} failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool type asserts to `bool`
|
||||||
|
func (j *Json) Bool() (bool, error) {
|
||||||
|
if s, ok := (j.data).(bool); ok {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return false, errors.New("type assertion to bool failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// String type asserts to `string`
|
||||||
|
func (j *Json) String() (string, error) {
|
||||||
|
if s, ok := (j.data).(string); ok {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return "", errors.New("type assertion to string failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes type asserts to `[]byte`
|
||||||
|
func (j *Json) Bytes() ([]byte, error) {
|
||||||
|
if s, ok := (j.data).(string); ok {
|
||||||
|
return []byte(s), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("type assertion to []byte failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringArray type asserts to an `array` of `string`
|
||||||
|
func (j *Json) StringArray() ([]string, error) {
|
||||||
|
arr, err := j.Array()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
retArr := make([]string, 0, len(arr))
|
||||||
|
for _, a := range arr {
|
||||||
|
if a == nil {
|
||||||
|
retArr = append(retArr, "")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s, ok := a.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
retArr = append(retArr, s)
|
||||||
|
}
|
||||||
|
return retArr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustArray guarantees the return of a `[]interface{}` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you want to interate over array values in a succinct manner:
|
||||||
|
// for i, v := range js.Get("results").MustArray() {
|
||||||
|
// fmt.Println(i, v)
|
||||||
|
// }
|
||||||
|
func (j *Json) MustArray(args ...[]interface{}) []interface{} {
|
||||||
|
var def []interface{}
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustArray() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := j.Array()
|
||||||
|
if err == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustMap guarantees the return of a `map[string]interface{}` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you want to interate over map values in a succinct manner:
|
||||||
|
// for k, v := range js.Get("dictionary").MustMap() {
|
||||||
|
// fmt.Println(k, v)
|
||||||
|
// }
|
||||||
|
func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} {
|
||||||
|
var def map[string]interface{}
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustMap() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := j.Map()
|
||||||
|
if err == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustString guarantees the return of a `string` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want a `string` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default"))
|
||||||
|
func (j *Json) MustString(args ...string) string {
|
||||||
|
var def string
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustString() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := j.String()
|
||||||
|
if err == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustStringArray guarantees the return of a `[]string` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you want to interate over array values in a succinct manner:
|
||||||
|
// for i, s := range js.Get("results").MustStringArray() {
|
||||||
|
// fmt.Println(i, s)
|
||||||
|
// }
|
||||||
|
func (j *Json) MustStringArray(args ...[]string) []string {
|
||||||
|
var def []string
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustStringArray() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := j.StringArray()
|
||||||
|
if err == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt guarantees the return of an `int` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want an `int` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150))
|
||||||
|
func (j *Json) MustInt(args ...int) int {
|
||||||
|
var def int
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustInt() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := j.Int()
|
||||||
|
if err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFloat64 guarantees the return of a `float64` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want a `float64` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150))
|
||||||
|
func (j *Json) MustFloat64(args ...float64) float64 {
|
||||||
|
var def float64
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustFloat64() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := j.Float64()
|
||||||
|
if err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustBool guarantees the return of a `bool` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want a `bool` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(true))
|
||||||
|
func (j *Json) MustBool(args ...bool) bool {
|
||||||
|
var def bool
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustBool() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := j.Bool()
|
||||||
|
if err == nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt64 guarantees the return of an `int64` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want an `int64` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustInt64(), js.Get("optional_param").MustInt64(5150))
|
||||||
|
func (j *Json) MustInt64(args ...int64) int64 {
|
||||||
|
var def int64
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustInt64() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := j.Int64()
|
||||||
|
if err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUInt64 guarantees the return of an `uint64` (with optional default)
|
||||||
|
//
|
||||||
|
// useful when you explicitly want an `uint64` in a single value return context:
|
||||||
|
// myFunc(js.Get("param1").MustUint64(), js.Get("optional_param").MustUint64(5150))
|
||||||
|
func (j *Json) MustUint64(args ...uint64) uint64 {
|
||||||
|
var def uint64
|
||||||
|
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
def = args[0]
|
||||||
|
default:
|
||||||
|
log.Panicf("MustUint64() received too many arguments %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := j.Uint64()
|
||||||
|
if err == nil {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
// +build go1.1
|
||||||
|
|
||||||
|
package simplejson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implements the json.Unmarshaler interface.
|
||||||
|
func (j *Json) UnmarshalJSON(p []byte) error {
|
||||||
|
fmt.Printf("UnmarshalJSON")
|
||||||
|
dec := json.NewDecoder(bytes.NewBuffer(p))
|
||||||
|
dec.UseNumber()
|
||||||
|
return dec.Decode(&j.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromReader returns a *Json by decoding from an io.Reader
|
||||||
|
func NewFromReader(r io.Reader) (*Json, error) {
|
||||||
|
j := new(Json)
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
dec.UseNumber()
|
||||||
|
err := dec.Decode(&j.data)
|
||||||
|
return j, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 coerces into a float64
|
||||||
|
func (j *Json) Float64() (float64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
return j.data.(json.Number).Float64()
|
||||||
|
case float32, float64:
|
||||||
|
return reflect.ValueOf(j.data).Float(), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return float64(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return float64(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int coerces into an int
|
||||||
|
func (j *Json) Int() (int, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
i, err := j.data.(json.Number).Int64()
|
||||||
|
return int(i), err
|
||||||
|
case float32, float64:
|
||||||
|
return int(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return int(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return int(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 coerces into an int64
|
||||||
|
func (j *Json) Int64() (int64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
return j.data.(json.Number).Int64()
|
||||||
|
case float32, float64:
|
||||||
|
return int64(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return reflect.ValueOf(j.data).Int(), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return int64(reflect.ValueOf(j.data).Uint()), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 coerces into an uint64
|
||||||
|
func (j *Json) Uint64() (uint64, error) {
|
||||||
|
switch j.data.(type) {
|
||||||
|
case json.Number:
|
||||||
|
return strconv.ParseUint(j.data.(json.Number).String(), 10, 64)
|
||||||
|
case float32, float64:
|
||||||
|
return uint64(reflect.ValueOf(j.data).Float()), nil
|
||||||
|
case int, int8, int16, int32, int64:
|
||||||
|
return uint64(reflect.ValueOf(j.data).Int()), nil
|
||||||
|
case uint, uint8, uint16, uint32, uint64:
|
||||||
|
return reflect.ValueOf(j.data).Uint(), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid value type")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,248 @@
|
||||||
|
package simplejson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/bmizerany/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimplejson(t *testing.T) {
|
||||||
|
var ok bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
js, err := NewJson([]byte(`{
|
||||||
|
"test": {
|
||||||
|
"string_array": ["asdf", "ghjk", "zxcv"],
|
||||||
|
"string_array_null": ["abc", null, "efg"],
|
||||||
|
"array": [1, "2", 3],
|
||||||
|
"arraywithsubs": [{"subkeyone": 1},
|
||||||
|
{"subkeytwo": 2, "subkeythree": 3}],
|
||||||
|
"int": 10,
|
||||||
|
"float": 5.150,
|
||||||
|
"string": "simplejson",
|
||||||
|
"bool": true,
|
||||||
|
"sub_obj": {"a": 1}
|
||||||
|
}
|
||||||
|
}`))
|
||||||
|
|
||||||
|
assert.NotEqual(t, nil, js)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
_, ok = js.CheckGet("test")
|
||||||
|
assert.Equal(t, true, ok)
|
||||||
|
|
||||||
|
_, ok = js.CheckGet("missing_key")
|
||||||
|
assert.Equal(t, false, ok)
|
||||||
|
|
||||||
|
aws := js.Get("test").Get("arraywithsubs")
|
||||||
|
assert.NotEqual(t, nil, aws)
|
||||||
|
var awsval int
|
||||||
|
awsval, _ = aws.GetIndex(0).Get("subkeyone").Int()
|
||||||
|
assert.Equal(t, 1, awsval)
|
||||||
|
awsval, _ = aws.GetIndex(1).Get("subkeytwo").Int()
|
||||||
|
assert.Equal(t, 2, awsval)
|
||||||
|
awsval, _ = aws.GetIndex(1).Get("subkeythree").Int()
|
||||||
|
assert.Equal(t, 3, awsval)
|
||||||
|
|
||||||
|
i, _ := js.Get("test").Get("int").Int()
|
||||||
|
assert.Equal(t, 10, i)
|
||||||
|
|
||||||
|
f, _ := js.Get("test").Get("float").Float64()
|
||||||
|
assert.Equal(t, 5.150, f)
|
||||||
|
|
||||||
|
s, _ := js.Get("test").Get("string").String()
|
||||||
|
assert.Equal(t, "simplejson", s)
|
||||||
|
|
||||||
|
b, _ := js.Get("test").Get("bool").Bool()
|
||||||
|
assert.Equal(t, true, b)
|
||||||
|
|
||||||
|
mi := js.Get("test").Get("int").MustInt()
|
||||||
|
assert.Equal(t, 10, mi)
|
||||||
|
|
||||||
|
mi2 := js.Get("test").Get("missing_int").MustInt(5150)
|
||||||
|
assert.Equal(t, 5150, mi2)
|
||||||
|
|
||||||
|
ms := js.Get("test").Get("string").MustString()
|
||||||
|
assert.Equal(t, "simplejson", ms)
|
||||||
|
|
||||||
|
ms2 := js.Get("test").Get("missing_string").MustString("fyea")
|
||||||
|
assert.Equal(t, "fyea", ms2)
|
||||||
|
|
||||||
|
ma2 := js.Get("test").Get("missing_array").MustArray([]interface{}{"1", 2, "3"})
|
||||||
|
assert.Equal(t, ma2, []interface{}{"1", 2, "3"})
|
||||||
|
|
||||||
|
msa := js.Get("test").Get("string_array").MustStringArray()
|
||||||
|
assert.Equal(t, msa[0], "asdf")
|
||||||
|
assert.Equal(t, msa[1], "ghjk")
|
||||||
|
assert.Equal(t, msa[2], "zxcv")
|
||||||
|
|
||||||
|
msa2 := js.Get("test").Get("string_array").MustStringArray([]string{"1", "2", "3"})
|
||||||
|
assert.Equal(t, msa2[0], "asdf")
|
||||||
|
assert.Equal(t, msa2[1], "ghjk")
|
||||||
|
assert.Equal(t, msa2[2], "zxcv")
|
||||||
|
|
||||||
|
msa3 := js.Get("test").Get("missing_array").MustStringArray([]string{"1", "2", "3"})
|
||||||
|
assert.Equal(t, msa3, []string{"1", "2", "3"})
|
||||||
|
|
||||||
|
mm2 := js.Get("test").Get("missing_map").MustMap(map[string]interface{}{"found": false})
|
||||||
|
assert.Equal(t, mm2, map[string]interface{}{"found": false})
|
||||||
|
|
||||||
|
strs, err := js.Get("test").Get("string_array").StringArray()
|
||||||
|
assert.Equal(t, err, nil)
|
||||||
|
assert.Equal(t, strs[0], "asdf")
|
||||||
|
assert.Equal(t, strs[1], "ghjk")
|
||||||
|
assert.Equal(t, strs[2], "zxcv")
|
||||||
|
|
||||||
|
strs2, err := js.Get("test").Get("string_array_null").StringArray()
|
||||||
|
assert.Equal(t, err, nil)
|
||||||
|
assert.Equal(t, strs2[0], "abc")
|
||||||
|
assert.Equal(t, strs2[1], "")
|
||||||
|
assert.Equal(t, strs2[2], "efg")
|
||||||
|
|
||||||
|
gp, _ := js.GetPath("test", "string").String()
|
||||||
|
assert.Equal(t, "simplejson", gp)
|
||||||
|
|
||||||
|
gp2, _ := js.GetPath("test", "int").Int()
|
||||||
|
assert.Equal(t, 10, gp2)
|
||||||
|
|
||||||
|
assert.Equal(t, js.Get("test").Get("bool").MustBool(), true)
|
||||||
|
|
||||||
|
js.Set("float2", 300.0)
|
||||||
|
assert.Equal(t, js.Get("float2").MustFloat64(), 300.0)
|
||||||
|
|
||||||
|
js.Set("test2", "setTest")
|
||||||
|
assert.Equal(t, "setTest", js.Get("test2").MustString())
|
||||||
|
|
||||||
|
js.Del("test2")
|
||||||
|
assert.NotEqual(t, "setTest", js.Get("test2").MustString())
|
||||||
|
|
||||||
|
js.Get("test").Get("sub_obj").Set("a", 2)
|
||||||
|
assert.Equal(t, 2, js.Get("test").Get("sub_obj").Get("a").MustInt())
|
||||||
|
|
||||||
|
js.GetPath("test", "sub_obj").Set("a", 3)
|
||||||
|
assert.Equal(t, 3, js.GetPath("test", "sub_obj", "a").MustInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdlibInterfaces(t *testing.T) {
|
||||||
|
val := new(struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Params *Json `json:"params"`
|
||||||
|
})
|
||||||
|
val2 := new(struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Params *Json `json:"params"`
|
||||||
|
})
|
||||||
|
|
||||||
|
raw := `{"name":"myobject","params":{"string":"simplejson"}}`
|
||||||
|
|
||||||
|
assert.Equal(t, nil, json.Unmarshal([]byte(raw), val))
|
||||||
|
|
||||||
|
assert.Equal(t, "myobject", val.Name)
|
||||||
|
assert.NotEqual(t, nil, val.Params.data)
|
||||||
|
s, _ := val.Params.Get("string").String()
|
||||||
|
assert.Equal(t, "simplejson", s)
|
||||||
|
|
||||||
|
p, err := json.Marshal(val)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, nil, json.Unmarshal(p, val2))
|
||||||
|
assert.Equal(t, val, val2) // stable
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet(t *testing.T) {
|
||||||
|
js, err := NewJson([]byte(`{}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
js.Set("baz", "bing")
|
||||||
|
|
||||||
|
s, err := js.GetPath("baz").String()
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "bing", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplace(t *testing.T) {
|
||||||
|
js, err := NewJson([]byte(`{}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
err = js.UnmarshalJSON([]byte(`{"baz":"bing"}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
s, err := js.GetPath("baz").String()
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "bing", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetPath(t *testing.T) {
|
||||||
|
js, err := NewJson([]byte(`{}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
js.SetPath([]string{"foo", "bar"}, "baz")
|
||||||
|
|
||||||
|
s, err := js.GetPath("foo", "bar").String()
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "baz", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetPathNoPath(t *testing.T) {
|
||||||
|
js, err := NewJson([]byte(`{"some":"data","some_number":1.0,"some_bool":false}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
f := js.GetPath("some_number").MustFloat64(99.0)
|
||||||
|
assert.Equal(t, f, 1.0)
|
||||||
|
|
||||||
|
js.SetPath([]string{}, map[string]interface{}{"foo": "bar"})
|
||||||
|
|
||||||
|
s, err := js.GetPath("foo").String()
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "bar", s)
|
||||||
|
|
||||||
|
f = js.GetPath("some_number").MustFloat64(99.0)
|
||||||
|
assert.Equal(t, f, 99.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathWillAugmentExisting(t *testing.T) {
|
||||||
|
js, err := NewJson([]byte(`{"this":{"a":"aa","b":"bb","c":"cc"}}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
js.SetPath([]string{"this", "d"}, "dd")
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
path []string
|
||||||
|
outcome string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
path: []string{"this", "a"},
|
||||||
|
outcome: "aa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: []string{"this", "b"},
|
||||||
|
outcome: "bb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: []string{"this", "c"},
|
||||||
|
outcome: "cc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: []string{"this", "d"},
|
||||||
|
outcome: "dd",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
s, err := js.GetPath(tc.path...).String()
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, tc.outcome, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathWillOverwriteExisting(t *testing.T) {
|
||||||
|
// notice how "a" is 0.1 - but then we'll try to set at path a, foo
|
||||||
|
js, err := NewJson([]byte(`{"this":{"a":0.1,"b":"bb","c":"cc"}}`))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
js.SetPath([]string{"this", "a", "foo"}, "bar")
|
||||||
|
|
||||||
|
s, err := js.GetPath("this", "a", "foo").String()
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "bar", s)
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,8 @@ package models
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -42,7 +44,7 @@ type DataSource struct {
|
||||||
BasicAuthPassword string
|
BasicAuthPassword string
|
||||||
WithCredentials bool
|
WithCredentials bool
|
||||||
IsDefault bool
|
IsDefault bool
|
||||||
JsonData map[string]interface{}
|
JsonData *simplejson.Json
|
||||||
|
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
|
|
@ -74,19 +76,19 @@ func IsKnownDataSourcePlugin(dsType string) bool {
|
||||||
|
|
||||||
// Also acts as api DTO
|
// Also acts as api DTO
|
||||||
type AddDataSourceCommand struct {
|
type AddDataSourceCommand struct {
|
||||||
Name string `json:"name" binding:"Required"`
|
Name string `json:"name" binding:"Required"`
|
||||||
Type string `json:"type" binding:"Required"`
|
Type string `json:"type" binding:"Required"`
|
||||||
Access DsAccess `json:"access" binding:"Required"`
|
Access DsAccess `json:"access" binding:"Required"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
BasicAuth bool `json:"basicAuth"`
|
BasicAuth bool `json:"basicAuth"`
|
||||||
BasicAuthUser string `json:"basicAuthUser"`
|
BasicAuthUser string `json:"basicAuthUser"`
|
||||||
BasicAuthPassword string `json:"basicAuthPassword"`
|
BasicAuthPassword string `json:"basicAuthPassword"`
|
||||||
WithCredentials bool `json:"withCredentials"`
|
WithCredentials bool `json:"withCredentials"`
|
||||||
IsDefault bool `json:"isDefault"`
|
IsDefault bool `json:"isDefault"`
|
||||||
JsonData map[string]interface{} `json:"jsonData"`
|
JsonData *simplejson.Json `json:"jsonData"`
|
||||||
|
|
||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
|
|
||||||
|
|
@ -95,19 +97,19 @@ type AddDataSourceCommand struct {
|
||||||
|
|
||||||
// Also acts as api DTO
|
// Also acts as api DTO
|
||||||
type UpdateDataSourceCommand struct {
|
type UpdateDataSourceCommand struct {
|
||||||
Name string `json:"name" binding:"Required"`
|
Name string `json:"name" binding:"Required"`
|
||||||
Type string `json:"type" binding:"Required"`
|
Type string `json:"type" binding:"Required"`
|
||||||
Access DsAccess `json:"access" binding:"Required"`
|
Access DsAccess `json:"access" binding:"Required"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
BasicAuth bool `json:"basicAuth"`
|
BasicAuth bool `json:"basicAuth"`
|
||||||
BasicAuthUser string `json:"basicAuthUser"`
|
BasicAuthUser string `json:"basicAuthUser"`
|
||||||
BasicAuthPassword string `json:"basicAuthPassword"`
|
BasicAuthPassword string `json:"basicAuthPassword"`
|
||||||
WithCredentials bool `json:"withCredentials"`
|
WithCredentials bool `json:"withCredentials"`
|
||||||
IsDefault bool `json:"isDefault"`
|
IsDefault bool `json:"isDefault"`
|
||||||
JsonData map[string]interface{} `json:"jsonData"`
|
JsonData *simplejson.Json `json:"jsonData"`
|
||||||
|
|
||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
Id int64 `json:"-"`
|
Id int64 `json:"-"`
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/dynmap"
|
"github.com/grafana/grafana/pkg/components/dynmap"
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
|
|
@ -24,6 +28,14 @@ type ImportDashboardInput struct {
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DashboardInputMissingError struct {
|
||||||
|
VariableName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DashboardInputMissingError) Error() string {
|
||||||
|
return fmt.Sprintf("Dashbord input variable: %v missing from import command", e.VariableName)
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bus.AddHandler("plugins", ImportDashboard)
|
bus.AddHandler("plugins", ImportDashboard)
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +54,19 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template := dynmap.NewFromMap(dashboard.Data)
|
||||||
|
evaluator := &DashTemplateEvaluator{
|
||||||
|
template: template,
|
||||||
|
inputs: cmd.Inputs,
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedDash, err := evaluator.Eval()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
saveCmd := m.SaveDashboardCommand{
|
saveCmd := m.SaveDashboardCommand{
|
||||||
Dashboard: dashboard.Data,
|
Dashboard: generatedDash.StringMap(),
|
||||||
OrgId: cmd.OrgId,
|
OrgId: cmd.OrgId,
|
||||||
UserId: cmd.UserId,
|
UserId: cmd.UserId,
|
||||||
}
|
}
|
||||||
|
|
@ -70,15 +93,9 @@ type DashTemplateEvaluator struct {
|
||||||
inputs []ImportDashboardInput
|
inputs []ImportDashboardInput
|
||||||
variables map[string]string
|
variables map[string]string
|
||||||
result *dynmap.Object
|
result *dynmap.Object
|
||||||
|
varRegex *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (this *DashTemplateEvaluator) getObject(path string) map[string]interface{} {
|
|
||||||
// if obj, exists := this.template[path]; exists {
|
|
||||||
// return obj.(map[string]interface{})
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (this *DashTemplateEvaluator) findInput(varName string, varDef *dynmap.Object) *ImportDashboardInput {
|
func (this *DashTemplateEvaluator) findInput(varName string, varDef *dynmap.Object) *ImportDashboardInput {
|
||||||
inputType, _ := varDef.GetString("type")
|
inputType, _ := varDef.GetString("type")
|
||||||
|
|
||||||
|
|
@ -94,14 +111,22 @@ func (this *DashTemplateEvaluator) findInput(varName string, varDef *dynmap.Obje
|
||||||
func (this *DashTemplateEvaluator) Eval() (*dynmap.Object, error) {
|
func (this *DashTemplateEvaluator) Eval() (*dynmap.Object, error) {
|
||||||
this.result = dynmap.NewObject()
|
this.result = dynmap.NewObject()
|
||||||
this.variables = make(map[string]string)
|
this.variables = make(map[string]string)
|
||||||
|
this.varRegex, _ = regexp.Compile("\\$__(\\w+)")
|
||||||
|
|
||||||
// check that we have all inputs we need
|
// check that we have all inputs we need
|
||||||
if requiredInputs, err := this.template.GetObject("__inputs"); err == nil {
|
if requiredInputs, err := this.template.GetObject("__inputs"); err == nil {
|
||||||
for varName, value := range requiredInputs.Map() {
|
for varName, value := range requiredInputs.Map() {
|
||||||
varDef, _ := value.Object()
|
varDef, _ := value.Object()
|
||||||
input := this.findInput(varName, varDef)
|
input := this.findInput(varName, varDef)
|
||||||
this.variables[varName] = input.Value
|
|
||||||
|
if input == nil {
|
||||||
|
return nil, &DashboardInputMissingError{VariableName: varName}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.variables["$__"+varName] = input.Value
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.Info("Import: dashboard has no __import section")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.EvalObject(this.template, this.result)
|
this.EvalObject(this.template, this.result)
|
||||||
|
|
@ -111,19 +136,24 @@ func (this *DashTemplateEvaluator) Eval() (*dynmap.Object, error) {
|
||||||
func (this *DashTemplateEvaluator) EvalObject(source *dynmap.Object, writer *dynmap.Object) {
|
func (this *DashTemplateEvaluator) EvalObject(source *dynmap.Object, writer *dynmap.Object) {
|
||||||
|
|
||||||
for key, value := range source.Map() {
|
for key, value := range source.Map() {
|
||||||
if key == "__input" {
|
if key == "__inputs" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
goValue := value.Interface()
|
goValue := value.Interface()
|
||||||
switch goValue.(type) {
|
|
||||||
|
switch v := goValue.(type) {
|
||||||
case string:
|
case string:
|
||||||
writer.SetValue(key, goValue)
|
interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
|
||||||
|
return this.variables[match]
|
||||||
|
})
|
||||||
|
writer.SetValue(key, interpolated)
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
childSource, _ := value.Object()
|
childSource, _ := value.Object()
|
||||||
childWriter, _ := writer.SetValue(key, map[string]interface{}{}).Object()
|
childWriter, _ := writer.SetValue(key, map[string]interface{}{}).Object()
|
||||||
this.EvalObject(childSource, childWriter)
|
this.EvalObject(childSource, childWriter)
|
||||||
default:
|
default:
|
||||||
|
log.Info("type: %v", reflect.TypeOf(goValue))
|
||||||
log.Error(3, "Unknown json type key: %v , type: %v", key, goValue)
|
log.Error(3, "Unknown json type key: %v , type: %v", key, goValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ func TestDashboardImport(t *testing.T) {
|
||||||
Path: "dashboards/connections.json",
|
Path: "dashboards/connections.json",
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
UserId: 1,
|
UserId: 1,
|
||||||
Inputs: []ImportDashboardInput{},
|
Inputs: []ImportDashboardInput{
|
||||||
|
{Name: "*", Type: "datasource"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ImportDashboard(&cmd)
|
err = ImportDashboard(&cmd)
|
||||||
|
|
@ -41,6 +43,10 @@ func TestDashboardImport(t *testing.T) {
|
||||||
|
|
||||||
Convey("should install dashboard", func() {
|
Convey("should install dashboard", func() {
|
||||||
So(importedDash, ShouldNotBeNil)
|
So(importedDash, ShouldNotBeNil)
|
||||||
|
|
||||||
|
dashData := dynmap.NewFromMap(importedDash.Data)
|
||||||
|
So(dashData.String(), ShouldEqual, "")
|
||||||
|
|
||||||
rows := importedDash.Data["rows"].([]interface{})
|
rows := importedDash.Data["rows"].([]interface{})
|
||||||
row1 := rows[0].(map[string]interface{})
|
row1 := rows[0].(map[string]interface{})
|
||||||
panels := row1["panels"].([]interface{})
|
panels := row1["panels"].([]interface{})
|
||||||
|
|
@ -53,7 +59,7 @@ func TestDashboardImport(t *testing.T) {
|
||||||
|
|
||||||
Convey("When evaling dashboard template", t, func() {
|
Convey("When evaling dashboard template", t, func() {
|
||||||
template, _ := dynmap.NewObjectFromBytes([]byte(`{
|
template, _ := dynmap.NewObjectFromBytes([]byte(`{
|
||||||
"__input": {
|
"__inputs": {
|
||||||
"graphite": {
|
"graphite": {
|
||||||
"type": "datasource"
|
"type": "datasource"
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +82,12 @@ func TestDashboardImport(t *testing.T) {
|
||||||
Convey("should render template", func() {
|
Convey("should render template", func() {
|
||||||
So(res.MustGetString("test.prop", ""), ShouldEqual, "my-server")
|
So(res.MustGetString("test.prop", ""), ShouldEqual, "my-server")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("should not include inputs in output", func() {
|
||||||
|
_, err := res.GetObject("__inputs")
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export class DataSourceEditCtrl {
|
||||||
datasourceMeta: any;
|
datasourceMeta: any;
|
||||||
tabIndex: number;
|
tabIndex: number;
|
||||||
hasDashboards: boolean;
|
hasDashboards: boolean;
|
||||||
|
editForm: any;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -114,7 +115,7 @@ export class DataSourceEditCtrl {
|
||||||
}
|
}
|
||||||
|
|
||||||
saveChanges(test) {
|
saveChanges(test) {
|
||||||
if (!this.$scope.editForm.$valid) {
|
if (!this.editForm.$valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<div ng-if="ctrl.tabIndex === 0" class="tab-content">
|
<div ng-if="ctrl.tabIndex === 0" class="tab-content">
|
||||||
|
|
||||||
<form name="editForm">
|
<form name="ctrl.editForm">
|
||||||
<div class="gf-form-group">
|
<div class="gf-form-group">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-7">Name</span>
|
<span class="gf-form-label width-7">Name</span>
|
||||||
|
|
@ -52,12 +52,12 @@
|
||||||
</plugin-component>
|
</plugin-component>
|
||||||
</rebuild-on-change>
|
</rebuild-on-change>
|
||||||
|
|
||||||
<div ng-if="testing" style="margin-top: 25px">
|
<div ng-if="ctrl.testing" style="margin-top: 25px">
|
||||||
<h5 ng-show="!testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
|
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
|
||||||
<h5 ng-show="testing.done">Test results</h5>
|
<h5 ng-show="ctrl.testing.done">Test results</h5>
|
||||||
<div class="alert-{{testing.status}} alert">
|
<div class="alert-{{ctrl.testing.status}} alert">
|
||||||
<div class="alert-title">{{testing.title}}</div>
|
<div class="alert-title">{{ctrl.testing.title}}</div>
|
||||||
<div ng-bind='testing.message'></div>
|
<div ng-bind='ctrl.testing.message'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
"panels": [
|
"panels": [
|
||||||
{
|
{
|
||||||
"type": "graph",
|
"type": "graph",
|
||||||
"datasource": "__$graphite"
|
"datasource": "$__graphite"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue