refactor(): refactoring json usage

This commit is contained in:
Torkel Ödegaard 2016-03-12 00:13:06 +01:00
parent 40b2f00dc5
commit 3fb0b71822
8 changed files with 68 additions and 86 deletions

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"log" "log"
) )
@ -51,6 +50,11 @@ func New() *Json {
} }
} }
// New returns a pointer to a new, empty `Json` object
func NewFromAny(data interface{}) *Json {
return &Json{data: data}
}
// Interface returns the underlying data // Interface returns the underlying data
func (j *Json) Interface() interface{} { func (j *Json) Interface() interface{} {
return j.data return j.data
@ -68,7 +72,6 @@ func (j *Json) EncodePretty() ([]byte, error) {
// Implements the json.Marshaler interface. // Implements the json.Marshaler interface.
func (j *Json) MarshalJSON() ([]byte, error) { func (j *Json) MarshalJSON() ([]byte, error) {
fmt.Printf("MarshalJSON")
return json.Marshal(&j.data) return json.Marshal(&j.data)
} }

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/gosimple/slug" "github.com/gosimple/slug"
"github.com/grafana/grafana/pkg/components/simplejson"
) )
// Typed errors // Typed errors
@ -37,14 +38,14 @@ type Dashboard struct {
CreatedBy int64 CreatedBy int64
Title string Title string
Data map[string]interface{} Data *simplejson.Json
} }
// NewDashboard creates a new dashboard // NewDashboard creates a new dashboard
func NewDashboard(title string) *Dashboard { func NewDashboard(title string) *Dashboard {
dash := &Dashboard{} dash := &Dashboard{}
dash.Data = make(map[string]interface{}) dash.Data = simplejson.New()
dash.Data["title"] = title dash.Data.Set("title", title)
dash.Title = title dash.Title = title
dash.Created = time.Now() dash.Created = time.Now()
dash.Updated = time.Now() dash.Updated = time.Now()
@ -54,34 +55,24 @@ func NewDashboard(title string) *Dashboard {
// GetTags turns the tags in data json into go string array // GetTags turns the tags in data json into go string array
func (dash *Dashboard) GetTags() []string { func (dash *Dashboard) GetTags() []string {
jsonTags := dash.Data["tags"] return dash.Data.Get("tags").MustStringArray()
if jsonTags == nil || jsonTags == "" {
return []string{}
}
arr := jsonTags.([]interface{})
b := make([]string, len(arr))
for i := range arr {
b[i] = arr[i].(string)
}
return b
} }
func NewDashboardFromJson(data map[string]interface{}) *Dashboard { func NewDashboardFromJson(data *simplejson.Json) *Dashboard {
dash := &Dashboard{} dash := &Dashboard{}
dash.Data = data dash.Data = data
dash.Title = dash.Data["title"].(string) dash.Title = dash.Data.Get("title").MustString()
dash.UpdateSlug() dash.UpdateSlug()
if dash.Data["id"] != nil { if id, err := dash.Data.Get("id").Float64(); err == nil {
dash.Id = int64(dash.Data["id"].(float64)) dash.Id = int64(id)
if dash.Data["version"] != nil { if version, err := dash.Data.Get("version").Float64(); err == nil {
dash.Version = int(dash.Data["version"].(float64)) dash.Version = int(version)
dash.Updated = time.Now() dash.Updated = time.Now()
} }
} else { } else {
dash.Data["version"] = 0 dash.Data.Set("version", 0)
dash.Created = time.Now() dash.Created = time.Now()
dash.Updated = time.Now() dash.Updated = time.Now()
} }
@ -92,9 +83,11 @@ func NewDashboardFromJson(data map[string]interface{}) *Dashboard {
// GetDashboardModel turns the command into the savable model // GetDashboardModel turns the command into the savable model
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard { func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
dash := NewDashboardFromJson(cmd.Dashboard) dash := NewDashboardFromJson(cmd.Dashboard)
if dash.Data["version"] == 0 {
if dash.Data.Get("version").MustInt(0) == 0 {
dash.CreatedBy = cmd.UserId dash.CreatedBy = cmd.UserId
} }
dash.UpdatedBy = cmd.UserId dash.UpdatedBy = cmd.UserId
dash.OrgId = cmd.OrgId dash.OrgId = cmd.OrgId
dash.UpdateSlug() dash.UpdateSlug()
@ -103,15 +96,12 @@ func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
// GetString a // GetString a
func (dash *Dashboard) GetString(prop string, defaultValue string) string { func (dash *Dashboard) GetString(prop string, defaultValue string) string {
if val, exists := dash.Data[prop]; exists { return dash.Data.Get(prop).MustString(defaultValue)
return val.(string)
}
return defaultValue
} }
// UpdateSlug updates the slug // UpdateSlug updates the slug
func (dash *Dashboard) UpdateSlug() { func (dash *Dashboard) UpdateSlug() {
title := strings.ToLower(dash.Data["title"].(string)) title := strings.ToLower(dash.Data.Get("title").MustString())
dash.Slug = slug.Make(title) dash.Slug = slug.Make(title)
} }
@ -120,10 +110,10 @@ func (dash *Dashboard) UpdateSlug() {
// //
type SaveDashboardCommand struct { type SaveDashboardCommand struct {
Dashboard map[string]interface{} `json:"dashboard" binding:"Required"` Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
UserId int64 `json:"userId"` UserId int64 `json:"userId"`
OrgId int64 `json:"-"` OrgId int64 `json:"-"`
Overwrite bool `json:"overwrite"` Overwrite bool `json:"overwrite"`
Result *Dashboard Result *Dashboard
} }

View File

@ -3,6 +3,7 @@ package models
import ( import (
"testing" "testing"
"github.com/grafana/grafana/pkg/components/simplejson"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
@ -16,12 +17,11 @@ func TestDashboardModel(t *testing.T) {
}) })
Convey("Given a dashboard json", t, func() { Convey("Given a dashboard json", t, func() {
json := map[string]interface{}{ json := simplejson.New()
"title": "test dash", json.Set("title", "test dash")
}
Convey("With tags as string value", func() { Convey("With tags as string value", func() {
json["tags"] = "" json.Set("tags", "")
dash := NewDashboardFromJson(json) dash := NewDashboardFromJson(json)
So(len(dash.GetTags()), ShouldEqual, 0) So(len(dash.GetTags()), ShouldEqual, 0)

View File

@ -6,7 +6,7 @@ import (
"regexp" "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/simplejson"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )
@ -54,9 +54,8 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
return err return err
} }
template := dynmap.NewFromMap(dashboard.Data)
evaluator := &DashTemplateEvaluator{ evaluator := &DashTemplateEvaluator{
template: template, template: dashboard.Data,
inputs: cmd.Inputs, inputs: cmd.Inputs,
} }
@ -66,7 +65,7 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
} }
saveCmd := m.SaveDashboardCommand{ saveCmd := m.SaveDashboardCommand{
Dashboard: generatedDash.StringMap(), Dashboard: generatedDash,
OrgId: cmd.OrgId, OrgId: cmd.OrgId,
UserId: cmd.UserId, UserId: cmd.UserId,
} }
@ -89,15 +88,15 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
} }
type DashTemplateEvaluator struct { type DashTemplateEvaluator struct {
template *dynmap.Object template *simplejson.Json
inputs []ImportDashboardInput inputs []ImportDashboardInput
variables map[string]string variables map[string]string
result *dynmap.Object result *simplejson.Json
varRegex *regexp.Regexp varRegex *regexp.Regexp
} }
func (this *DashTemplateEvaluator) findInput(varName string, varDef *dynmap.Object) *ImportDashboardInput { func (this *DashTemplateEvaluator) findInput(varName string, varDef *simplejson.Json) *ImportDashboardInput {
inputType, _ := varDef.GetString("type") inputType := varDef.Get("type").MustString()
for _, input := range this.inputs { for _, input := range this.inputs {
if inputType == input.Type && (input.Name == varName || input.Name == "*") { if inputType == input.Type && (input.Name == varName || input.Name == "*") {
@ -108,16 +107,15 @@ func (this *DashTemplateEvaluator) findInput(varName string, varDef *dynmap.Obje
return nil return nil
} }
func (this *DashTemplateEvaluator) Eval() (*dynmap.Object, error) { func (this *DashTemplateEvaluator) Eval() (*simplejson.Json, error) {
this.result = dynmap.NewObject() this.result = simplejson.New()
this.variables = make(map[string]string) this.variables = make(map[string]string)
this.varRegex, _ = regexp.Compile("\\$__(\\w+)") 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 inputDefs := this.template.Get("__inputs"); inputDefs != nil {
for varName, value := range requiredInputs.Map() { for varName, value := range inputDefs.MustMap() {
varDef, _ := value.Object() input := this.findInput(varName, simplejson.NewFromAny(value))
input := this.findInput(varName, varDef)
if input == nil { if input == nil {
return nil, &DashboardInputMissingError{VariableName: varName} return nil, &DashboardInputMissingError{VariableName: varName}
@ -133,28 +131,28 @@ func (this *DashTemplateEvaluator) Eval() (*dynmap.Object, error) {
return this.result, nil return this.result, nil
} }
func (this *DashTemplateEvaluator) EvalObject(source *dynmap.Object, writer *dynmap.Object) { func (this *DashTemplateEvaluator) EvalObject(source *simplejson.Json, writer *simplejson.Json) {
for key, value := range source.Map() { for key, value := range source.MustMap() {
if key == "__inputs" { if key == "__inputs" {
continue continue
} }
goValue := value.Interface() switch v := value.(type) {
switch v := goValue.(type) {
case string: case string:
interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string { interpolated := this.varRegex.ReplaceAllStringFunc(v, func(match string) string {
return this.variables[match] return this.variables[match]
}) })
writer.SetValue(key, interpolated) writer.Set(key, interpolated)
case map[string]interface{}: case map[string]interface{}:
childSource, _ := value.Object() childSource := simplejson.NewFromAny(value)
childWriter, _ := writer.SetValue(key, map[string]interface{}{}).Object() childWriter := simplejson.New()
writer.Set(key, childWriter.Interface())
this.EvalObject(childSource, childWriter) this.EvalObject(childSource, childWriter)
case []interface{}:
default: default:
log.Info("type: %v", reflect.TypeOf(goValue)) log.Info("type: %v", reflect.TypeOf(value))
log.Error(3, "Unknown json type key: %v , type: %v", key, goValue) log.Error(3, "Unknown json type key: %v , type: %v", key, value)
} }
} }
} }

View File

@ -4,7 +4,7 @@ import (
"testing" "testing"
"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/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"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
@ -44,21 +44,16 @@ 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) dashStr, _ := importedDash.Data.EncodePretty()
So(dashData.String(), ShouldEqual, "") So(string(dashStr), ShouldEqual, "")
rows := importedDash.Data["rows"].([]interface{}) // So(panel["datasource"], ShouldEqual, "graphite")
row1 := rows[0].(map[string]interface{}) // So(importedDash.Data["__inputs"], ShouldBeNil)
panels := row1["panels"].([]interface{})
panel := panels[0].(map[string]interface{})
So(panel["datasource"], ShouldEqual, "graphite")
So(importedDash.Data["__inputs"], ShouldBeNil)
}) })
}) })
Convey("When evaling dashboard template", t, func() { Convey("When evaling dashboard template", t, func() {
template, _ := dynmap.NewObjectFromBytes([]byte(`{ template, _ := simplejson.NewJson([]byte(`{
"__inputs": { "__inputs": {
"graphite": { "graphite": {
"type": "datasource" "type": "datasource"
@ -80,12 +75,12 @@ func TestDashboardImport(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
Convey("should render template", func() { Convey("should render template", func() {
So(res.MustGetString("test.prop", ""), ShouldEqual, "my-server") So(res.GetPath("test", "prop").MustString(), ShouldEqual, "my-server")
}) })
Convey("should not include inputs in output", func() { Convey("should not include inputs in output", func() {
_, err := res.GetObject("__inputs") inputs := res.Get("__inputs")
So(err, ShouldNotBeNil) So(inputs.Interface(), ShouldBeNil)
}) })
}) })

View File

@ -1,11 +1,11 @@
package plugins package plugins
import ( import (
"encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )
@ -52,10 +52,8 @@ func loadPluginDashboard(plugin *PluginBase, path string) (*m.Dashboard, error)
defer reader.Close() defer reader.Close()
jsonParser := json.NewDecoder(reader) data, err := simplejson.NewFromReader(reader)
var data map[string]interface{} if err != nil {
if err := jsonParser.Decode(&data); err != nil {
return nil, err return nil, err
} }

View File

@ -23,7 +23,7 @@ func TestPluginDashboards(t *testing.T) {
bus.AddHandler("test", func(query *m.GetDashboardQuery) error { bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
if query.Slug == "nginx-connections" { if query.Slug == "nginx-connections" {
dash := m.NewDashboard("Nginx Connections") dash := m.NewDashboard("Nginx Connections")
dash.Data["revision"] = "1.1" dash.Data.Set("revision", "1.1")
query.Result = dash query.Result = dash
return nil return nil
} }

View File

@ -1,12 +1,12 @@
package search package search
import ( import (
"encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )
@ -120,10 +120,8 @@ func loadDashboardFromFile(filename string) (*JsonDashIndexItem, error) {
} }
defer reader.Close() defer reader.Close()
jsonParser := json.NewDecoder(reader) data, err := simplejson.NewFromReader(reader)
var data map[string]interface{} if err != nil {
if err := jsonParser.Decode(&data); err != nil {
return nil, err return nil, err
} }