Merge branch 'dashboard-install'

This commit is contained in:
Torkel Ödegaard 2016-03-12 10:13:59 +01:00
commit 53dc145ce7
64 changed files with 4434 additions and 382 deletions

18
Godeps/Godeps.json generated
View File

@ -64,6 +64,11 @@
"Comment": "v1.0.0", "Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f" "Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
}, },
{
"ImportPath": "github.com/bmizerany/assert",
"Comment": "release.r60-6-ge17e998",
"Rev": "e17e99893cb6509f428e1728281c2ad60a6b31e3"
},
{ {
"ImportPath": "github.com/bradfitz/gomemcache/memcache", "ImportPath": "github.com/bradfitz/gomemcache/memcache",
"Comment": "release.r60-40-g72a6864", "Comment": "release.r60-40-g72a6864",
@ -123,10 +128,6 @@
"Comment": "v0.4.4-44-gf561133", "Comment": "v0.4.4-44-gf561133",
"Rev": "f56113384f2c63dfe4cd8e768e349f1c35122b58" "Rev": "f56113384f2c63dfe4cd8e768e349f1c35122b58"
}, },
{
"ImportPath": "github.com/gopherjs/gopherjs/js",
"Rev": "14d893dca2e4adb93a5ccc9494040acc0821cd8d"
},
{ {
"ImportPath": "github.com/gosimple/slug", "ImportPath": "github.com/gosimple/slug",
"Rev": "8d258463b4459f161f51d6a357edacd3eef9d663" "Rev": "8d258463b4459f161f51d6a357edacd3eef9d663"
@ -160,6 +161,15 @@
"ImportPath": "github.com/klauspost/crc32", "ImportPath": "github.com/klauspost/crc32",
"Rev": "6834731faf32e62a2dd809d99fb24d1e4ae5a92d" "Rev": "6834731faf32e62a2dd809d99fb24d1e4ae5a92d"
}, },
{
"ImportPath": "github.com/kr/pretty",
"Comment": "go.weekly.2011-12-22-27-ge6ac2fc",
"Rev": "e6ac2fc51e89a3249e82157fa0bb7a18ef9dd5bb"
},
{
"ImportPath": "github.com/kr/text",
"Rev": "bb797dc4fb8320488f47bf11de07a733d7233e1f"
},
{ {
"ImportPath": "github.com/lib/pq", "ImportPath": "github.com/lib/pq",
"Comment": "go1.0-cutoff-13-g19eeca3", "Comment": "go1.0-cutoff-13-g19eeca3",

View File

@ -0,0 +1,7 @@
_go_.*
_gotest_.*
_obj
_test
_testmain.go
*.out
*.[568]

View File

@ -0,0 +1,45 @@
# Assert (c) Blake Mizerany and Keith Rarick -- MIT LICENCE
## Assertions for Go tests
## Install
$ go get github.com/bmizerany/assert
## Use
**point.go**
package point
type Point struct {
x, y int
}
**point_test.go**
package point
import (
"testing"
"github.com/bmizerany/assert"
)
func TestAsserts(t *testing.T) {
p1 := Point{1, 1}
p2 := Point{2, 1}
assert.Equal(t, p1, p2)
}
**output**
$ go test
--- FAIL: TestAsserts (0.00 seconds)
assert.go:15: /Users/flavio.barbosa/dev/stewie/src/point_test.go:12
assert.go:24: ! X: 1 != 2
FAIL
## Docs
http://github.com/bmizerany/assert

View File

@ -0,0 +1,76 @@
package assert
// Testing helpers for doozer.
import (
"github.com/kr/pretty"
"reflect"
"testing"
"runtime"
"fmt"
)
func assert(t *testing.T, result bool, f func(), cd int) {
if !result {
_, file, line, _ := runtime.Caller(cd + 1)
t.Errorf("%s:%d", file, line)
f()
t.FailNow()
}
}
func equal(t *testing.T, exp, got interface{}, cd int, args ...interface{}) {
fn := func() {
for _, desc := range pretty.Diff(exp, got) {
t.Error("!", desc)
}
if len(args) > 0 {
t.Error("!", " -", fmt.Sprint(args...))
}
}
result := reflect.DeepEqual(exp, got)
assert(t, result, fn, cd+1)
}
func tt(t *testing.T, result bool, cd int, args ...interface{}) {
fn := func() {
t.Errorf("! Failure")
if len(args) > 0 {
t.Error("!", " -", fmt.Sprint(args...))
}
}
assert(t, result, fn, cd+1)
}
func T(t *testing.T, result bool, args ...interface{}) {
tt(t, result, 1, args...)
}
func Tf(t *testing.T, result bool, format string, args ...interface{}) {
tt(t, result, 1, fmt.Sprintf(format, args...))
}
func Equal(t *testing.T, exp, got interface{}, args ...interface{}) {
equal(t, exp, got, 1, args...)
}
func Equalf(t *testing.T, exp, got interface{}, format string, args ...interface{}) {
equal(t, exp, got, 1, fmt.Sprintf(format, args...))
}
func NotEqual(t *testing.T, exp, got interface{}, args ...interface{}) {
fn := func() {
t.Errorf("! Unexpected: <%#v>", exp)
if len(args) > 0 {
t.Error("!", " -", fmt.Sprint(args...))
}
}
result := !reflect.DeepEqual(exp, got)
assert(t, result, fn, 1)
}
func Panic(t *testing.T, err interface{}, fn func()) {
defer func() {
equal(t, err, recover(), 3)
}()
fn()
}

View File

@ -0,0 +1,15 @@
package assert
import (
"testing"
)
func TestLineNumbers(t *testing.T) {
Equal(t, "foo", "foo", "msg!")
//Equal(t, "foo", "bar", "this should blow up")
}
func TestNotEqual(t *testing.T) {
NotEqual(t, "foo", "bar", "msg!")
//NotEqual(t, "foo", "foo", "this should blow up")
}

View File

@ -0,0 +1,5 @@
package point
type Point struct {
X, Y int
}

View File

@ -0,0 +1,13 @@
package point
import (
"testing"
"assert"
)
func TestAsserts(t *testing.T) {
p1 := Point{1, 1}
p2 := Point{2, 1}
assert.Equal(t, p1, p2)
}

View File

@ -1,168 +0,0 @@
// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
//
// Use MakeWrapper to expose methods to JavaScript. When passing values directly, the following type conversions are performed:
//
// | Go type | JavaScript type | Conversions back to interface{} |
// | --------------------- | --------------------- | ------------------------------- |
// | bool | Boolean | bool |
// | integers and floats | Number | float64 |
// | string | String | string |
// | []int8 | Int8Array | []int8 |
// | []int16 | Int16Array | []int16 |
// | []int32, []int | Int32Array | []int |
// | []uint8 | Uint8Array | []uint8 |
// | []uint16 | Uint16Array | []uint16 |
// | []uint32, []uint | Uint32Array | []uint |
// | []float32 | Float32Array | []float32 |
// | []float64 | Float64Array | []float64 |
// | all other slices | Array | []interface{} |
// | arrays | see slice type | see slice type |
// | functions | Function | func(...interface{}) *js.Object |
// | time.Time | Date | time.Time |
// | - | instanceof Node | *js.Object |
// | maps, structs | instanceof Object | map[string]interface{} |
//
// Additionally, for a struct containing a *js.Object field, only the content of the field will be passed to JavaScript and vice versa.
package js
// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. A nil pointer to Object is equal to JavaScript's "null". Object can not be used as a map key.
type Object struct{ object *Object }
// Get returns the object's property with the given key.
func (o *Object) Get(key string) *Object { return o.object.Get(key) }
// Set assigns the value to the object's property with the given key.
func (o *Object) Set(key string, value interface{}) { o.object.Set(key, value) }
// Delete removes the object's property with the given key.
func (o *Object) Delete(key string) { o.object.Delete(key) }
// Length returns the object's "length" property, converted to int.
func (o *Object) Length() int { return o.object.Length() }
// Index returns the i'th element of an array.
func (o *Object) Index(i int) *Object { return o.object.Index(i) }
// SetIndex sets the i'th element of an array.
func (o *Object) SetIndex(i int, value interface{}) { o.object.SetIndex(i, value) }
// Call calls the object's method with the given name.
func (o *Object) Call(name string, args ...interface{}) *Object { return o.object.Call(name, args...) }
// Invoke calls the object itself. This will fail if it is not a function.
func (o *Object) Invoke(args ...interface{}) *Object { return o.object.Invoke(args...) }
// New creates a new instance of this type object. This will fail if it not a function (constructor).
func (o *Object) New(args ...interface{}) *Object { return o.object.New(args...) }
// Bool returns the object converted to bool according to JavaScript type conversions.
func (o *Object) Bool() bool { return o.object.Bool() }
// String returns the object converted to string according to JavaScript type conversions.
func (o *Object) String() string { return o.object.String() }
// Int returns the object converted to int according to JavaScript type conversions (parseInt).
func (o *Object) Int() int { return o.object.Int() }
// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
func (o *Object) Int64() int64 { return o.object.Int64() }
// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
func (o *Object) Uint64() uint64 { return o.object.Uint64() }
// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
func (o *Object) Float() float64 { return o.object.Float() }
// Interface returns the object converted to interface{}. See GopherJS' README for details.
func (o *Object) Interface() interface{} { return o.object.Interface() }
// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
func (o *Object) Unsafe() uintptr { return o.object.Unsafe() }
// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be recovered, giving an *Error that holds the JavaScript error object.
type Error struct {
*Object
}
// Error returns the message of the encapsulated JavaScript error object.
func (err *Error) Error() string {
return "JavaScript error: " + err.Get("message").String()
}
// Stack returns the stack property of the encapsulated JavaScript error object.
func (err *Error) Stack() string {
return err.Get("stack").String()
}
// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js).
var Global *Object
// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
var Module *Object
// Undefined gives the JavaScript value "undefined".
var Undefined *Object
// Debugger gets compiled to JavaScript's "debugger;" statement.
func Debugger() {}
// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
func InternalObject(i interface{}) *Object {
return nil
}
// MakeFunc wraps a function and gives access to the values of JavaScript's "this" and "arguments" keywords.
func MakeFunc(func(this *Object, arguments []*Object) interface{}) *Object {
return nil
}
// Keys returns the keys of the given JavaScript object.
func Keys(o *Object) []string {
if o == nil || o == Undefined {
return nil
}
a := Global.Get("Object").Call("keys", o)
s := make([]string, a.Length())
for i := 0; i < a.Length(); i++ {
s[i] = a.Index(i).String()
}
return s
}
// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. Use explicit getter and setter methods to expose struct fields to JavaScript.
func MakeWrapper(i interface{}) *Object {
v := InternalObject(i)
o := Global.Get("Object").New()
o.Set("__internal_object__", v)
methods := v.Get("constructor").Get("methods")
for i := 0; i < methods.Length(); i++ {
m := methods.Index(i)
if m.Get("pkg").String() != "" { // not exported
continue
}
o.Set(m.Get("name").String(), func(args ...*Object) *Object {
return Global.Call("$externalizeFunction", v.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", v, args)
})
}
return o
}
// NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice.
func NewArrayBuffer(b []byte) *Object {
slice := InternalObject(b)
offset := slice.Get("$offset").Int()
length := slice.Get("$length").Int()
return slice.Get("$array").Get("buffer").Call("slice", offset, offset+length)
}
// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
type M map[string]interface{}
// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
type S []interface{}
func init() {
// avoid dead code elimination
e := Error{}
_ = e
}

View File

@ -0,0 +1,4 @@
[568].out
_go*
_test*
_obj

21
Godeps/_workspace/src/github.com/kr/pretty/License generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2012 Keith Rarick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

9
Godeps/_workspace/src/github.com/kr/pretty/Readme generated vendored Normal file
View File

@ -0,0 +1,9 @@
package pretty
import "github.com/kr/pretty"
Package pretty provides pretty-printing for Go values.
Documentation
http://godoc.org/github.com/kr/pretty

158
Godeps/_workspace/src/github.com/kr/pretty/diff.go generated vendored Normal file
View File

@ -0,0 +1,158 @@
package pretty
import (
"fmt"
"io"
"reflect"
)
type sbuf []string
func (s *sbuf) Write(b []byte) (int, error) {
*s = append(*s, string(b))
return len(b), nil
}
// Diff returns a slice where each element describes
// a difference between a and b.
func Diff(a, b interface{}) (desc []string) {
Fdiff((*sbuf)(&desc), a, b)
return desc
}
// Fdiff writes to w a description of the differences between a and b.
func Fdiff(w io.Writer, a, b interface{}) {
diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
}
type diffWriter struct {
w io.Writer
l string // label
}
func (w diffWriter) printf(f string, a ...interface{}) {
var l string
if w.l != "" {
l = w.l + ": "
}
fmt.Fprintf(w.w, l+f, a...)
}
func (w diffWriter) diff(av, bv reflect.Value) {
if !av.IsValid() && bv.IsValid() {
w.printf("nil != %#v", bv.Interface())
return
}
if av.IsValid() && !bv.IsValid() {
w.printf("%#v != nil", av.Interface())
return
}
if !av.IsValid() && !bv.IsValid() {
return
}
at := av.Type()
bt := bv.Type()
if at != bt {
w.printf("%v != %v", at, bt)
return
}
// numeric types, including bool
if at.Kind() < reflect.Array {
a, b := av.Interface(), bv.Interface()
if a != b {
w.printf("%#v != %#v", a, b)
}
return
}
switch at.Kind() {
case reflect.String:
a, b := av.Interface(), bv.Interface()
if a != b {
w.printf("%q != %q", a, b)
}
case reflect.Ptr:
switch {
case av.IsNil() && !bv.IsNil():
w.printf("nil != %v", bv.Interface())
case !av.IsNil() && bv.IsNil():
w.printf("%v != nil", av.Interface())
case !av.IsNil() && !bv.IsNil():
w.diff(av.Elem(), bv.Elem())
}
case reflect.Struct:
for i := 0; i < av.NumField(); i++ {
w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
}
case reflect.Slice:
lenA := av.Len()
lenB := bv.Len()
if lenA != lenB {
w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB)
break
}
for i := 0; i < lenA; i++ {
w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i))
}
case reflect.Map:
ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys())
for _, k := range ak {
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
w.printf("%q != (missing)", av.MapIndex(k))
}
for _, k := range both {
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
w.diff(av.MapIndex(k), bv.MapIndex(k))
}
for _, k := range bk {
w := w.relabel(fmt.Sprintf("[%#v]", k.Interface()))
w.printf("(missing) != %q", bv.MapIndex(k))
}
case reflect.Interface:
w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
default:
if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface()))
}
}
}
func (d diffWriter) relabel(name string) (d1 diffWriter) {
d1 = d
if d.l != "" && name[0] != '[' {
d1.l += "."
}
d1.l += name
return d1
}
func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) {
for _, av := range a {
inBoth := false
for _, bv := range b {
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
inBoth = true
both = append(both, av)
break
}
}
if !inBoth {
ak = append(ak, av)
}
}
for _, bv := range b {
inBoth := false
for _, av := range a {
if reflect.DeepEqual(av.Interface(), bv.Interface()) {
inBoth = true
break
}
}
if !inBoth {
bk = append(bk, bv)
}
}
return
}

View File

@ -0,0 +1,74 @@
package pretty
import (
"testing"
)
type difftest struct {
a interface{}
b interface{}
exp []string
}
type S struct {
A int
S *S
I interface{}
C []int
}
var diffs = []difftest{
{a: nil, b: nil},
{a: S{A: 1}, b: S{A: 1}},
{0, "", []string{`int != string`}},
{0, 1, []string{`0 != 1`}},
{S{}, new(S), []string{`pretty.S != *pretty.S`}},
{"a", "b", []string{`"a" != "b"`}},
{S{}, S{A: 1}, []string{`A: 0 != 1`}},
{new(S), &S{A: 1}, []string{`A: 0 != 1`}},
{S{S: new(S)}, S{S: &S{A: 1}}, []string{`S.A: 0 != 1`}},
{S{}, S{I: 0}, []string{`I: nil != 0`}},
{S{I: 1}, S{I: "x"}, []string{`I: int != string`}},
{S{}, S{C: []int{1}}, []string{`C: []int[0] != []int[1]`}},
{S{C: []int{}}, S{C: []int{1}}, []string{`C: []int[0] != []int[1]`}},
{S{C: []int{1, 2, 3}}, S{C: []int{1, 2, 4}}, []string{`C[2]: 3 != 4`}},
{S{}, S{A: 1, S: new(S)}, []string{`A: 0 != 1`, `S: nil != &{0 <nil> <nil> []}`}},
}
func TestDiff(t *testing.T) {
for _, tt := range diffs {
got := Diff(tt.a, tt.b)
eq := len(got) == len(tt.exp)
if eq {
for i := range got {
eq = eq && got[i] == tt.exp[i]
}
}
if !eq {
t.Errorf("diffing % #v", tt.a)
t.Errorf("with % #v", tt.b)
diffdiff(t, got, tt.exp)
continue
}
}
}
func diffdiff(t *testing.T, got, exp []string) {
minus(t, "unexpected:", got, exp)
minus(t, "missing:", exp, got)
}
func minus(t *testing.T, s string, a, b []string) {
var i, j int
for i = 0; i < len(a); i++ {
for j = 0; j < len(b); j++ {
if a[i] == b[j] {
break
}
}
if j == len(b) {
t.Error(s, a[i])
}
}
}

View File

@ -0,0 +1,20 @@
package pretty_test
import (
"fmt"
"github.com/kr/pretty"
)
func Example() {
type myType struct {
a, b int
}
var x = []myType{{1, 2}, {3, 4}, {5, 6}}
fmt.Printf("%# v", pretty.Formatter(x))
// output:
// []pretty_test.myType{
// {a:1, b:2},
// {a:3, b:4},
// {a:5, b:6},
// }
}

337
Godeps/_workspace/src/github.com/kr/pretty/formatter.go generated vendored Normal file
View File

@ -0,0 +1,337 @@
package pretty
import (
"fmt"
"io"
"reflect"
"strconv"
"text/tabwriter"
"github.com/kr/text"
)
const (
limit = 50
)
type formatter struct {
x interface{}
force bool
quote bool
}
// Formatter makes a wrapper, f, that will format x as go source with line
// breaks and tabs. Object f responds to the "%v" formatting verb when both the
// "#" and " " (space) flags are set, for example:
//
// fmt.Sprintf("%# v", Formatter(x))
//
// If one of these two flags is not set, or any other verb is used, f will
// format x according to the usual rules of package fmt.
// In particular, if x satisfies fmt.Formatter, then x.Format will be called.
func Formatter(x interface{}) (f fmt.Formatter) {
return formatter{x: x, quote: true}
}
func (fo formatter) String() string {
return fmt.Sprint(fo.x) // unwrap it
}
func (fo formatter) passThrough(f fmt.State, c rune) {
s := "%"
for i := 0; i < 128; i++ {
if f.Flag(i) {
s += string(i)
}
}
if w, ok := f.Width(); ok {
s += fmt.Sprintf("%d", w)
}
if p, ok := f.Precision(); ok {
s += fmt.Sprintf(".%d", p)
}
s += string(c)
fmt.Fprintf(f, s, fo.x)
}
func (fo formatter) Format(f fmt.State, c rune) {
if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
p := &printer{tw: w, Writer: w, visited: make(map[visit]int)}
p.printValue(reflect.ValueOf(fo.x), true, fo.quote)
w.Flush()
return
}
fo.passThrough(f, c)
}
type printer struct {
io.Writer
tw *tabwriter.Writer
visited map[visit]int
depth int
}
func (p *printer) indent() *printer {
q := *p
q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'})
return &q
}
func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) {
if showType {
io.WriteString(p, v.Type().String())
fmt.Fprintf(p, "(%#v)", x)
} else {
fmt.Fprintf(p, "%#v", x)
}
}
// printValue must keep track of already-printed pointer values to avoid
// infinite recursion.
type visit struct {
v uintptr
typ reflect.Type
}
func (p *printer) printValue(v reflect.Value, showType, quote bool) {
if p.depth > 10 {
io.WriteString(p, "!%v(DEPTH EXCEEDED)")
return
}
switch v.Kind() {
case reflect.Bool:
p.printInline(v, v.Bool(), showType)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
p.printInline(v, v.Int(), showType)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
p.printInline(v, v.Uint(), showType)
case reflect.Float32, reflect.Float64:
p.printInline(v, v.Float(), showType)
case reflect.Complex64, reflect.Complex128:
fmt.Fprintf(p, "%#v", v.Complex())
case reflect.String:
p.fmtString(v.String(), quote)
case reflect.Map:
t := v.Type()
if showType {
io.WriteString(p, t.String())
}
writeByte(p, '{')
if nonzero(v) {
expand := !canInline(v.Type())
pp := p
if expand {
writeByte(p, '\n')
pp = p.indent()
}
keys := v.MapKeys()
for i := 0; i < v.Len(); i++ {
showTypeInStruct := true
k := keys[i]
mv := v.MapIndex(k)
pp.printValue(k, false, true)
writeByte(pp, ':')
if expand {
writeByte(pp, '\t')
}
showTypeInStruct = t.Elem().Kind() == reflect.Interface
pp.printValue(mv, showTypeInStruct, true)
if expand {
io.WriteString(pp, ",\n")
} else if i < v.Len()-1 {
io.WriteString(pp, ", ")
}
}
if expand {
pp.tw.Flush()
}
}
writeByte(p, '}')
case reflect.Struct:
t := v.Type()
if v.CanAddr() {
addr := v.UnsafeAddr()
vis := visit{addr, t}
if vd, ok := p.visited[vis]; ok && vd < p.depth {
p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
break // don't print v again
}
p.visited[vis] = p.depth
}
if showType {
io.WriteString(p, t.String())
}
writeByte(p, '{')
if nonzero(v) {
expand := !canInline(v.Type())
pp := p
if expand {
writeByte(p, '\n')
pp = p.indent()
}
for i := 0; i < v.NumField(); i++ {
showTypeInStruct := true
if f := t.Field(i); f.Name != "" {
io.WriteString(pp, f.Name)
writeByte(pp, ':')
if expand {
writeByte(pp, '\t')
}
showTypeInStruct = labelType(f.Type)
}
pp.printValue(getField(v, i), showTypeInStruct, true)
if expand {
io.WriteString(pp, ",\n")
} else if i < v.NumField()-1 {
io.WriteString(pp, ", ")
}
}
if expand {
pp.tw.Flush()
}
}
writeByte(p, '}')
case reflect.Interface:
switch e := v.Elem(); {
case e.Kind() == reflect.Invalid:
io.WriteString(p, "nil")
case e.IsValid():
pp := *p
pp.depth++
pp.printValue(e, showType, true)
default:
io.WriteString(p, v.Type().String())
io.WriteString(p, "(nil)")
}
case reflect.Array, reflect.Slice:
t := v.Type()
if showType {
io.WriteString(p, t.String())
}
if v.Kind() == reflect.Slice && v.IsNil() && showType {
io.WriteString(p, "(nil)")
break
}
if v.Kind() == reflect.Slice && v.IsNil() {
io.WriteString(p, "nil")
break
}
writeByte(p, '{')
expand := !canInline(v.Type())
pp := p
if expand {
writeByte(p, '\n')
pp = p.indent()
}
for i := 0; i < v.Len(); i++ {
showTypeInSlice := t.Elem().Kind() == reflect.Interface
pp.printValue(v.Index(i), showTypeInSlice, true)
if expand {
io.WriteString(pp, ",\n")
} else if i < v.Len()-1 {
io.WriteString(pp, ", ")
}
}
if expand {
pp.tw.Flush()
}
writeByte(p, '}')
case reflect.Ptr:
e := v.Elem()
if !e.IsValid() {
writeByte(p, '(')
io.WriteString(p, v.Type().String())
io.WriteString(p, ")(nil)")
} else {
pp := *p
pp.depth++
writeByte(pp, '&')
pp.printValue(e, true, true)
}
case reflect.Chan:
x := v.Pointer()
if showType {
writeByte(p, '(')
io.WriteString(p, v.Type().String())
fmt.Fprintf(p, ")(%#v)", x)
} else {
fmt.Fprintf(p, "%#v", x)
}
case reflect.Func:
io.WriteString(p, v.Type().String())
io.WriteString(p, " {...}")
case reflect.UnsafePointer:
p.printInline(v, v.Pointer(), showType)
case reflect.Invalid:
io.WriteString(p, "nil")
}
}
func canInline(t reflect.Type) bool {
switch t.Kind() {
case reflect.Map:
return !canExpand(t.Elem())
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
if canExpand(t.Field(i).Type) {
return false
}
}
return true
case reflect.Interface:
return false
case reflect.Array, reflect.Slice:
return !canExpand(t.Elem())
case reflect.Ptr:
return false
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
return false
}
return true
}
func canExpand(t reflect.Type) bool {
switch t.Kind() {
case reflect.Map, reflect.Struct,
reflect.Interface, reflect.Array, reflect.Slice,
reflect.Ptr:
return true
}
return false
}
func labelType(t reflect.Type) bool {
switch t.Kind() {
case reflect.Interface, reflect.Struct:
return true
}
return false
}
func (p *printer) fmtString(s string, quote bool) {
if quote {
s = strconv.Quote(s)
}
io.WriteString(p, s)
}
func tryDeepEqual(a, b interface{}) bool {
defer func() { recover() }()
return reflect.DeepEqual(a, b)
}
func writeByte(w io.Writer, b byte) {
w.Write([]byte{b})
}
func getField(v reflect.Value, i int) reflect.Value {
val := v.Field(i)
if val.Kind() == reflect.Interface && !val.IsNil() {
val = val.Elem()
}
return val
}

View File

@ -0,0 +1,261 @@
package pretty
import (
"fmt"
"io"
"strings"
"testing"
"unsafe"
)
type test struct {
v interface{}
s string
}
type LongStructTypeName struct {
longFieldName interface{}
otherLongFieldName interface{}
}
type SA struct {
t *T
v T
}
type T struct {
x, y int
}
type F int
func (f F) Format(s fmt.State, c rune) {
fmt.Fprintf(s, "F(%d)", int(f))
}
var long = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var gosyntax = []test{
{nil, `nil`},
{"", `""`},
{"a", `"a"`},
{1, "int(1)"},
{1.0, "float64(1)"},
{[]int(nil), "[]int(nil)"},
{[0]int{}, "[0]int{}"},
{complex(1, 0), "(1+0i)"},
//{make(chan int), "(chan int)(0x1234)"},
{unsafe.Pointer(uintptr(unsafe.Pointer(&long))), fmt.Sprintf("unsafe.Pointer(0x%02x)", uintptr(unsafe.Pointer(&long)))},
{func(int) {}, "func(int) {...}"},
{map[int]int{1: 1}, "map[int]int{1:1}"},
{int32(1), "int32(1)"},
{io.EOF, `&errors.errorString{s:"EOF"}`},
{[]string{"a"}, `[]string{"a"}`},
{
[]string{long},
`[]string{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}`,
},
{F(5), "pretty.F(5)"},
{
SA{&T{1, 2}, T{3, 4}},
`pretty.SA{
t: &pretty.T{x:1, y:2},
v: pretty.T{x:3, y:4},
}`,
},
{
map[int][]byte{1: {}},
`map[int][]uint8{
1: {},
}`,
},
{
map[int]T{1: {}},
`map[int]pretty.T{
1: {},
}`,
},
{
long,
`"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"`,
},
{
LongStructTypeName{
longFieldName: LongStructTypeName{},
otherLongFieldName: long,
},
`pretty.LongStructTypeName{
longFieldName: pretty.LongStructTypeName{},
otherLongFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
}`,
},
{
&LongStructTypeName{
longFieldName: &LongStructTypeName{},
otherLongFieldName: (*LongStructTypeName)(nil),
},
`&pretty.LongStructTypeName{
longFieldName: &pretty.LongStructTypeName{},
otherLongFieldName: (*pretty.LongStructTypeName)(nil),
}`,
},
{
[]LongStructTypeName{
{nil, nil},
{3, 3},
{long, nil},
},
`[]pretty.LongStructTypeName{
{},
{
longFieldName: int(3),
otherLongFieldName: int(3),
},
{
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
otherLongFieldName: nil,
},
}`,
},
{
[]interface{}{
LongStructTypeName{nil, nil},
[]byte{1, 2, 3},
T{3, 4},
LongStructTypeName{long, nil},
},
`[]interface {}{
pretty.LongStructTypeName{},
[]uint8{0x1, 0x2, 0x3},
pretty.T{x:3, y:4},
pretty.LongStructTypeName{
longFieldName: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
otherLongFieldName: nil,
},
}`,
},
}
func TestGoSyntax(t *testing.T) {
for _, tt := range gosyntax {
s := fmt.Sprintf("%# v", Formatter(tt.v))
if tt.s != s {
t.Errorf("expected %q", tt.s)
t.Errorf("got %q", s)
t.Errorf("expraw\n%s", tt.s)
t.Errorf("gotraw\n%s", s)
}
}
}
type I struct {
i int
R interface{}
}
func (i *I) I() *I { return i.R.(*I) }
func TestCycle(t *testing.T) {
type A struct{ *A }
v := &A{}
v.A = v
// panics from stack overflow without cycle detection
t.Logf("Example cycle:\n%# v", Formatter(v))
p := &A{}
s := fmt.Sprintf("%# v", Formatter([]*A{p, p}))
if strings.Contains(s, "CYCLIC") {
t.Errorf("Repeated address detected as cyclic reference:\n%s", s)
}
type R struct {
i int
*R
}
r := &R{
i: 1,
R: &R{
i: 2,
R: &R{
i: 3,
},
},
}
r.R.R.R = r
t.Logf("Example longer cycle:\n%# v", Formatter(r))
r = &R{
i: 1,
R: &R{
i: 2,
R: &R{
i: 3,
R: &R{
i: 4,
R: &R{
i: 5,
R: &R{
i: 6,
R: &R{
i: 7,
R: &R{
i: 8,
R: &R{
i: 9,
R: &R{
i: 10,
R: &R{
i: 11,
},
},
},
},
},
},
},
},
},
},
}
// here be pirates
r.R.R.R.R.R.R.R.R.R.R.R = r
t.Logf("Example very long cycle:\n%# v", Formatter(r))
i := &I{
i: 1,
R: &I{
i: 2,
R: &I{
i: 3,
R: &I{
i: 4,
R: &I{
i: 5,
R: &I{
i: 6,
R: &I{
i: 7,
R: &I{
i: 8,
R: &I{
i: 9,
R: &I{
i: 10,
R: &I{
i: 11,
},
},
},
},
},
},
},
},
},
},
}
iv := i.I().I().I().I().I().I().I().I().I().I()
*iv = *i
t.Logf("Example long interface cycle:\n%# v", Formatter(i))
}

98
Godeps/_workspace/src/github.com/kr/pretty/pretty.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
// Package pretty provides pretty-printing for Go values. This is
// useful during debugging, to avoid wrapping long output lines in
// the terminal.
//
// It provides a function, Formatter, that can be used with any
// function that accepts a format string. It also provides
// convenience wrappers for functions in packages fmt and log.
package pretty
import (
"fmt"
"io"
"log"
)
// Errorf is a convenience wrapper for fmt.Errorf.
//
// Calling Errorf(f, x, y) is equivalent to
// fmt.Errorf(f, Formatter(x), Formatter(y)).
func Errorf(format string, a ...interface{}) error {
return fmt.Errorf(format, wrap(a, false)...)
}
// Fprintf is a convenience wrapper for fmt.Fprintf.
//
// Calling Fprintf(w, f, x, y) is equivalent to
// fmt.Fprintf(w, f, Formatter(x), Formatter(y)).
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) {
return fmt.Fprintf(w, format, wrap(a, false)...)
}
// Log is a convenience wrapper for log.Printf.
//
// Calling Log(x, y) is equivalent to
// log.Print(Formatter(x), Formatter(y)), but each operand is
// formatted with "%# v".
func Log(a ...interface{}) {
log.Print(wrap(a, true)...)
}
// Logf is a convenience wrapper for log.Printf.
//
// Calling Logf(f, x, y) is equivalent to
// log.Printf(f, Formatter(x), Formatter(y)).
func Logf(format string, a ...interface{}) {
log.Printf(format, wrap(a, false)...)
}
// Logln is a convenience wrapper for log.Printf.
//
// Calling Logln(x, y) is equivalent to
// log.Println(Formatter(x), Formatter(y)), but each operand is
// formatted with "%# v".
func Logln(a ...interface{}) {
log.Println(wrap(a, true)...)
}
// Print pretty-prints its operands and writes to standard output.
//
// Calling Print(x, y) is equivalent to
// fmt.Print(Formatter(x), Formatter(y)), but each operand is
// formatted with "%# v".
func Print(a ...interface{}) (n int, errno error) {
return fmt.Print(wrap(a, true)...)
}
// Printf is a convenience wrapper for fmt.Printf.
//
// Calling Printf(f, x, y) is equivalent to
// fmt.Printf(f, Formatter(x), Formatter(y)).
func Printf(format string, a ...interface{}) (n int, errno error) {
return fmt.Printf(format, wrap(a, false)...)
}
// Println pretty-prints its operands and writes to standard output.
//
// Calling Print(x, y) is equivalent to
// fmt.Println(Formatter(x), Formatter(y)), but each operand is
// formatted with "%# v".
func Println(a ...interface{}) (n int, errno error) {
return fmt.Println(wrap(a, true)...)
}
// Sprintf is a convenience wrapper for fmt.Sprintf.
//
// Calling Sprintf(f, x, y) is equivalent to
// fmt.Sprintf(f, Formatter(x), Formatter(y)).
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, wrap(a, false)...)
}
func wrap(a []interface{}, force bool) []interface{} {
w := make([]interface{}, len(a))
for i, x := range a {
w[i] = formatter{x: x, force: force}
}
return w
}

41
Godeps/_workspace/src/github.com/kr/pretty/zero.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package pretty
import (
"reflect"
)
func nonzero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() != 0
case reflect.Float32, reflect.Float64:
return v.Float() != 0
case reflect.Complex64, reflect.Complex128:
return v.Complex() != complex(0, 0)
case reflect.String:
return v.String() != ""
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if nonzero(getField(v, i)) {
return true
}
}
return false
case reflect.Array:
for i := 0; i < v.Len(); i++ {
if nonzero(v.Index(i)) {
return true
}
}
return false
case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func:
return !v.IsNil()
case reflect.UnsafePointer:
return v.Pointer() != 0
}
return true
}

19
Godeps/_workspace/src/github.com/kr/text/License generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright 2012 Keith Rarick
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

3
Godeps/_workspace/src/github.com/kr/text/Readme generated vendored Normal file
View File

@ -0,0 +1,3 @@
This is a Go package for manipulating paragraphs of text.
See http://go.pkgdoc.org/github.com/kr/text for full documentation.

View File

@ -0,0 +1,5 @@
Package colwriter provides a write filter that formats
input lines in multiple columns.
The package is a straightforward translation from
/src/cmd/draw/mc.c in Plan 9 from User Space.

View File

@ -0,0 +1,147 @@
// Package colwriter provides a write filter that formats
// input lines in multiple columns.
//
// The package is a straightforward translation from
// /src/cmd/draw/mc.c in Plan 9 from User Space.
package colwriter
import (
"bytes"
"io"
"unicode/utf8"
)
const (
tab = 4
)
const (
// Print each input line ending in a colon ':' separately.
BreakOnColon uint = 1 << iota
)
// A Writer is a filter that arranges input lines in as many columns as will
// fit in its width. Tab '\t' chars in the input are translated to sequences
// of spaces ending at multiples of 4 positions.
//
// If BreakOnColon is set, each input line ending in a colon ':' is written
// separately.
//
// The Writer assumes that all Unicode code points have the same width; this
// may not be true in some fonts.
type Writer struct {
w io.Writer
buf []byte
width int
flag uint
}
// NewWriter allocates and initializes a new Writer writing to w.
// Parameter width controls the total number of characters on each line
// across all columns.
func NewWriter(w io.Writer, width int, flag uint) *Writer {
return &Writer{
w: w,
width: width,
flag: flag,
}
}
// Write writes p to the writer w. The only errors returned are ones
// encountered while writing to the underlying output stream.
func (w *Writer) Write(p []byte) (n int, err error) {
var linelen int
var lastWasColon bool
for i, c := range p {
w.buf = append(w.buf, c)
linelen++
if c == '\t' {
w.buf[len(w.buf)-1] = ' '
for linelen%tab != 0 {
w.buf = append(w.buf, ' ')
linelen++
}
}
if w.flag&BreakOnColon != 0 && c == ':' {
lastWasColon = true
} else if lastWasColon {
if c == '\n' {
pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'})
if pos < 0 {
pos = 0
}
line := w.buf[pos:]
w.buf = w.buf[:pos]
if err = w.columnate(); err != nil {
if len(line) < i {
return i - len(line), err
}
return 0, err
}
if n, err := w.w.Write(line); err != nil {
if r := len(line) - n; r < i {
return i - r, err
}
return 0, err
}
}
lastWasColon = false
}
if c == '\n' {
linelen = 0
}
}
return len(p), nil
}
// Flush should be called after the last call to Write to ensure that any data
// buffered in the Writer is written to output.
func (w *Writer) Flush() error {
return w.columnate()
}
func (w *Writer) columnate() error {
words := bytes.Split(w.buf, []byte{'\n'})
w.buf = nil
if len(words[len(words)-1]) == 0 {
words = words[:len(words)-1]
}
maxwidth := 0
for _, wd := range words {
if n := utf8.RuneCount(wd); n > maxwidth {
maxwidth = n
}
}
maxwidth++ // space char
wordsPerLine := w.width / maxwidth
if wordsPerLine <= 0 {
wordsPerLine = 1
}
nlines := (len(words) + wordsPerLine - 1) / wordsPerLine
for i := 0; i < nlines; i++ {
col := 0
endcol := 0
for j := i; j < len(words); j += nlines {
endcol += maxwidth
_, err := w.w.Write(words[j])
if err != nil {
return err
}
col += utf8.RuneCount(words[j])
if j+nlines < len(words) {
for col < endcol {
_, err := w.w.Write([]byte{' '})
if err != nil {
return err
}
col++
}
}
}
_, err := w.w.Write([]byte{'\n'})
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,90 @@
package colwriter
import (
"bytes"
"testing"
)
var src = `
.git
.gitignore
.godir
Procfile:
README.md
api.go
apps.go
auth.go
darwin.go
data.go
dyno.go:
env.go
git.go
help.go
hkdist
linux.go
ls.go
main.go
plugin.go
run.go
scale.go
ssh.go
tail.go
term
unix.go
update.go
version.go
windows.go
`[1:]
var tests = []struct {
wid int
flag uint
src string
want string
}{
{80, 0, "", ""},
{80, 0, src, `
.git README.md darwin.go git.go ls.go scale.go unix.go
.gitignore api.go data.go help.go main.go ssh.go update.go
.godir apps.go dyno.go: hkdist plugin.go tail.go version.go
Procfile: auth.go env.go linux.go run.go term windows.go
`[1:]},
{80, BreakOnColon, src, `
.git .gitignore .godir
Procfile:
README.md api.go apps.go auth.go darwin.go data.go
dyno.go:
env.go hkdist main.go scale.go term version.go
git.go linux.go plugin.go ssh.go unix.go windows.go
help.go ls.go run.go tail.go update.go
`[1:]},
{20, 0, `
Hello
Γειά σου
안녕
今日は
`[1:], `
Hello 안녕
Γειά σου 今日は
`[1:]},
}
func TestWriter(t *testing.T) {
for _, test := range tests {
b := new(bytes.Buffer)
w := NewWriter(b, test.wid, test.flag)
if _, err := w.Write([]byte(test.src)); err != nil {
t.Error(err)
}
if err := w.Flush(); err != nil {
t.Error(err)
}
if g := b.String(); test.want != g {
t.Log("\n" + test.want)
t.Log("\n" + g)
t.Errorf("%q != %q", test.want, g)
}
}
}

3
Godeps/_workspace/src/github.com/kr/text/doc.go generated vendored Normal file
View File

@ -0,0 +1,3 @@
// Package text provides rudimentary functions for manipulating text in
// paragraphs.
package text

74
Godeps/_workspace/src/github.com/kr/text/indent.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
package text
import (
"io"
)
// Indent inserts prefix at the beginning of each non-empty line of s. The
// end-of-line marker is NL.
func Indent(s, prefix string) string {
return string(IndentBytes([]byte(s), []byte(prefix)))
}
// IndentBytes inserts prefix at the beginning of each non-empty line of b.
// The end-of-line marker is NL.
func IndentBytes(b, prefix []byte) []byte {
var res []byte
bol := true
for _, c := range b {
if bol && c != '\n' {
res = append(res, prefix...)
}
res = append(res, c)
bol = c == '\n'
}
return res
}
// Writer indents each line of its input.
type indentWriter struct {
w io.Writer
bol bool
pre [][]byte
sel int
off int
}
// NewIndentWriter makes a new write filter that indents the input
// lines. Each line is prefixed in order with the corresponding
// element of pre. If there are more lines than elements, the last
// element of pre is repeated for each subsequent line.
func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
return &indentWriter{
w: w,
pre: pre,
bol: true,
}
}
// The only errors returned are from the underlying indentWriter.
func (w *indentWriter) Write(p []byte) (n int, err error) {
for _, c := range p {
if w.bol {
var i int
i, err = w.w.Write(w.pre[w.sel][w.off:])
w.off += i
if err != nil {
return n, err
}
}
_, err = w.w.Write([]byte{c})
if err != nil {
return n, err
}
n++
w.bol = c == '\n'
if w.bol {
w.off = 0
if w.sel < len(w.pre)-1 {
w.sel++
}
}
}
return n, nil
}

119
Godeps/_workspace/src/github.com/kr/text/indent_test.go generated vendored Normal file
View File

@ -0,0 +1,119 @@
package text
import (
"bytes"
"testing"
)
type T struct {
inp, exp, pre string
}
var tests = []T{
{
"The quick brown fox\njumps over the lazy\ndog.\nBut not quickly.\n",
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\nxxxBut not quickly.\n",
"xxx",
},
{
"The quick brown fox\njumps over the lazy\ndog.\n\nBut not quickly.",
"xxxThe quick brown fox\nxxxjumps over the lazy\nxxxdog.\n\nxxxBut not quickly.",
"xxx",
},
}
func TestIndent(t *testing.T) {
for _, test := range tests {
got := Indent(test.inp, test.pre)
if got != test.exp {
t.Errorf("mismatch %q != %q", got, test.exp)
}
}
}
type IndentWriterTest struct {
inp, exp string
pre []string
}
var ts = []IndentWriterTest{
{
`
The quick brown fox
jumps over the lazy
dog.
But not quickly.
`[1:],
`
xxxThe quick brown fox
xxxjumps over the lazy
xxxdog.
xxxBut not quickly.
`[1:],
[]string{"xxx"},
},
{
`
The quick brown fox
jumps over the lazy
dog.
But not quickly.
`[1:],
`
xxaThe quick brown fox
xxxjumps over the lazy
xxxdog.
xxxBut not quickly.
`[1:],
[]string{"xxa", "xxx"},
},
{
`
The quick brown fox
jumps over the lazy
dog.
But not quickly.
`[1:],
`
xxaThe quick brown fox
xxbjumps over the lazy
xxcdog.
xxxBut not quickly.
`[1:],
[]string{"xxa", "xxb", "xxc", "xxx"},
},
{
`
The quick brown fox
jumps over the lazy
dog.
But not quickly.`[1:],
`
xxaThe quick brown fox
xxxjumps over the lazy
xxxdog.
xxx
xxxBut not quickly.`[1:],
[]string{"xxa", "xxx"},
},
}
func TestIndentWriter(t *testing.T) {
for _, test := range ts {
b := new(bytes.Buffer)
pre := make([][]byte, len(test.pre))
for i := range test.pre {
pre[i] = []byte(test.pre[i])
}
w := NewIndentWriter(b, pre...)
if _, err := w.Write([]byte(test.inp)); err != nil {
t.Error(err)
}
if got := b.String(); got != test.exp {
t.Errorf("mismatch %q != %q", got, test.exp)
t.Log(got)
t.Log(test.exp)
}
}
}

9
Godeps/_workspace/src/github.com/kr/text/mc/Readme generated vendored Normal file
View File

@ -0,0 +1,9 @@
Command mc prints in multiple columns.
Usage: mc [-] [-N] [file...]
Mc splits the input into as many columns as will fit in N
print positions. If the output is a tty, the default N is
the number of characters in a terminal line; otherwise the
default N is 80. Under option - each input line ending in
a colon ':' is printed separately.

62
Godeps/_workspace/src/github.com/kr/text/mc/mc.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
// Command mc prints in multiple columns.
//
// Usage: mc [-] [-N] [file...]
//
// Mc splits the input into as many columns as will fit in N
// print positions. If the output is a tty, the default N is
// the number of characters in a terminal line; otherwise the
// default N is 80. Under option - each input line ending in
// a colon ':' is printed separately.
package main
import (
"github.com/kr/pty"
"github.com/kr/text/colwriter"
"io"
"log"
"os"
"strconv"
)
func main() {
var width int
var flag uint
args := os.Args[1:]
for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' {
if len(args[0]) > 1 {
width, _ = strconv.Atoi(args[0][1:])
} else {
flag |= colwriter.BreakOnColon
}
args = args[1:]
}
if width < 1 {
_, width, _ = pty.Getsize(os.Stdout)
}
if width < 1 {
width = 80
}
w := colwriter.NewWriter(os.Stdout, width, flag)
if len(args) > 0 {
for _, s := range args {
if f, err := os.Open(s); err == nil {
copyin(w, f)
f.Close()
} else {
log.Println(err)
}
}
} else {
copyin(w, os.Stdin)
}
}
func copyin(w *colwriter.Writer, r io.Reader) {
if _, err := io.Copy(w, r); err != nil {
log.Println(err)
}
if err := w.Flush(); err != nil {
log.Println(err)
}
}

86
Godeps/_workspace/src/github.com/kr/text/wrap.go generated vendored Normal file
View File

@ -0,0 +1,86 @@
package text
import (
"bytes"
"math"
)
var (
nl = []byte{'\n'}
sp = []byte{' '}
)
const defaultPenalty = 1e5
// Wrap wraps s into a paragraph of lines of length lim, with minimal
// raggedness.
func Wrap(s string, lim int) string {
return string(WrapBytes([]byte(s), lim))
}
// WrapBytes wraps b into a paragraph of lines of length lim, with minimal
// raggedness.
func WrapBytes(b []byte, lim int) []byte {
words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp)
var lines [][]byte
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
lines = append(lines, bytes.Join(line, sp))
}
return bytes.Join(lines, nl)
}
// WrapWords is the low-level line-breaking algorithm, useful if you need more
// control over the details of the text wrapping process. For most uses, either
// Wrap or WrapBytes will be sufficient and more convenient.
//
// WrapWords splits a list of words into lines with minimal "raggedness",
// treating each byte as one unit, accounting for spc units between adjacent
// words on each line, and attempting to limit lines to lim units. Raggedness
// is the total error over all lines, where error is the square of the
// difference of the length of the line and lim. Too-long lines (which only
// happen when a single word is longer than lim units) have pen penalty units
// added to the error.
func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte {
n := len(words)
length := make([][]int, n)
for i := 0; i < n; i++ {
length[i] = make([]int, n)
length[i][i] = len(words[i])
for j := i + 1; j < n; j++ {
length[i][j] = length[i][j-1] + spc + len(words[j])
}
}
nbrk := make([]int, n)
cost := make([]int, n)
for i := range cost {
cost[i] = math.MaxInt32
}
for i := n - 1; i >= 0; i-- {
if length[i][n-1] <= lim || i == n-1 {
cost[i] = 0
nbrk[i] = n
} else {
for j := i + 1; j < n; j++ {
d := lim - length[i][j-1]
c := d*d + cost[j]
if length[i][j-1] > lim {
c += pen // too-long lines get a worse penalty
}
if c < cost[i] {
cost[i] = c
nbrk[i] = j
}
}
}
}
var lines [][][]byte
i := 0
for i < n {
lines = append(lines, words[i:nbrk[i]])
i = nbrk[i]
}
return lines
}

62
Godeps/_workspace/src/github.com/kr/text/wrap_test.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
package text
import (
"bytes"
"testing"
)
var text = "The quick brown fox jumps over the lazy dog."
func TestWrap(t *testing.T) {
exp := [][]string{
{"The", "quick", "brown", "fox"},
{"jumps", "over", "the", "lazy", "dog."},
}
words := bytes.Split([]byte(text), sp)
got := WrapWords(words, 1, 24, defaultPenalty)
if len(exp) != len(got) {
t.Fail()
}
for i := range exp {
if len(exp[i]) != len(got[i]) {
t.Fail()
}
for j := range exp[i] {
if exp[i][j] != string(got[i][j]) {
t.Fatal(i, exp[i][j], got[i][j])
}
}
}
}
func TestWrapNarrow(t *testing.T) {
exp := "The\nquick\nbrown\nfox\njumps\nover\nthe\nlazy\ndog."
if Wrap(text, 5) != exp {
t.Fail()
}
}
func TestWrapOneLine(t *testing.T) {
exp := "The quick brown fox jumps over the lazy dog."
if Wrap(text, 500) != exp {
t.Fail()
}
}
func TestWrapBug1(t *testing.T) {
cases := []struct {
limit int
text string
want string
}{
{4, "aaaaa", "aaaaa"},
{4, "a aaaaa", "a\naaaaa"},
}
for _, test := range cases {
got := Wrap(test.text, test.limit)
if got != test.want {
t.Errorf("Wrap(%q, %d) = %q want %q", test.text, test.limit, got, test.want)
}
}
}

View File

@ -176,7 +176,7 @@ func Register(r *macaron.Macaron) {
r.Get("/", wrap(GetPluginList)) r.Get("/", wrap(GetPluginList))
r.Get("/dashboards/:pluginId", wrap(GetPluginDashboards)) r.Get("/dashboards/:pluginId", wrap(GetPluginDashboards))
r.Post("/dashboards/install", bind(dtos.InstallPluginDashboardCmd{}), wrap(InstallPluginDashboard)) r.Post("/dashboards/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
r.Get("/:pluginId/settings", wrap(GetPluginSettingById)) r.Get("/:pluginId/settings", wrap(GetPluginSettingById))
r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting)) r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))

View File

@ -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{}{}

View File

@ -53,7 +53,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
} }
func GetDashboardSnapshot(c *middleware.Context) { func GetDashboardSnapshot(c *middleware.Context) {
key := c.Params(":key") key := c.Params(":key")
query := &m.GetDashboardSnapshotQuery{Key: key} query := &m.GetDashboardSnapshotQuery{Key: key}
@ -136,5 +135,4 @@ func SearchDashboardSnapshots(c *middleware.Context) Response {
} }
return Json(200, dtos) return Json(200, dtos)
//return Json(200, searchQuery.Result)
} }

View File

@ -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"
) )
@ -53,7 +54,7 @@ type DashboardMeta struct {
type DashboardFullWithMeta struct { type DashboardFullWithMeta struct {
Meta DashboardMeta `json:"meta"` Meta DashboardMeta `json:"meta"`
Dashboard map[string]interface{} `json:"dashboard"` Dashboard *simplejson.Json `json:"dashboard"`
} }
type DataSource struct { type DataSource struct {
@ -71,7 +72,7 @@ type DataSource struct {
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 {

View File

@ -26,9 +26,9 @@ type PluginListItem struct {
Info *plugins.PluginInfo `json:"info"` Info *plugins.PluginInfo `json:"info"`
} }
type InstallPluginDashboardCmd struct { type ImportDashboardCommand struct {
PluginId string `json:"pluginId"` PluginId string `json:"pluginId"`
Path string `json:"path"` Path string `json:"path"`
Reinstall bool `json:"reinstall"` Reinstall bool `json:"reinstall"`
Inputs map[string]interface{} `json:"inputs"` Inputs []plugins.ImportDashboardInput `json:"inputs"`
} }

View File

@ -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
} }

View File

@ -122,9 +122,9 @@ func GetPluginDashboards(c *middleware.Context) Response {
} }
} }
func InstallPluginDashboard(c *middleware.Context, apiCmd dtos.InstallPluginDashboardCmd) Response { func ImportDashboard(c *middleware.Context, apiCmd dtos.ImportDashboardCommand) Response {
cmd := plugins.InstallPluginDashboardCommand{ cmd := plugins.ImportDashboardCommand{
OrgId: c.OrgId, OrgId: c.OrgId,
UserId: c.UserId, UserId: c.UserId,
PluginId: apiCmd.PluginId, PluginId: apiCmd.PluginId,

View File

@ -0,0 +1,817 @@
// uses code from https://github.com/antonholmquist/jason/blob/master/jason.go
// MIT Licence
package dynmap
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"strings"
)
// Error values returned when validation functions fail
var (
ErrNotNull = errors.New("is not null")
ErrNotArray = errors.New("Not an array")
ErrNotNumber = errors.New("not a number")
ErrNotBool = errors.New("no bool")
ErrNotObject = errors.New("not an object")
ErrNotObjectArray = errors.New("not an object array")
ErrNotString = errors.New("not a string")
)
type KeyNotFoundError struct {
Key string
}
func (k KeyNotFoundError) Error() string {
if k.Key != "" {
return fmt.Sprintf("key '%s' not found", k.Key)
}
return "key not found"
}
// Value represents an arbitrary JSON value.
// It may contain a bool, number, string, object, array or null.
type Value struct {
data interface{}
exists bool // Used to separate nil and non-existing values
}
// Object represents an object JSON object.
// It inherets from Value but with an additional method to access
// a map representation of it's content. It's useful when iterating.
type Object struct {
Value
m map[string]*Value
valid bool
}
// Returns the golang map.
// Needed when iterating through the values of the object.
func (v *Object) Map() map[string]*Value {
return v.m
}
func NewFromMap(data map[string]interface{}) *Object {
val := &Value{data: data, exists: true}
obj, _ := val.Object()
return obj
}
func NewObject() *Object {
val := &Value{data: make(map[string]interface{}), exists: true}
obj, _ := val.Object()
return obj
}
// Creates a new value from an io.reader.
// Returns an error if the reader does not contain valid json.
// Useful for parsing the body of a net/http response.
// Example: NewFromReader(res.Body)
func NewValueFromReader(reader io.Reader) (*Value, error) {
j := new(Value)
d := json.NewDecoder(reader)
d.UseNumber()
err := d.Decode(&j.data)
return j, err
}
// Creates a new value from bytes.
// Returns an error if the bytes are not valid json.
func NewValueFromBytes(b []byte) (*Value, error) {
r := bytes.NewReader(b)
return NewValueFromReader(r)
}
func objectFromValue(v *Value, err error) (*Object, error) {
if err != nil {
return nil, err
}
o, err := v.Object()
if err != nil {
return nil, err
}
return o, nil
}
func NewObjectFromBytes(b []byte) (*Object, error) {
return objectFromValue(NewValueFromBytes(b))
}
func NewObjectFromReader(reader io.Reader) (*Object, error) {
return objectFromValue(NewValueFromReader(reader))
}
// Marshal into bytes.
func (v *Value) Marshal() ([]byte, error) {
return json.Marshal(v.data)
}
// Get the interyling data as interface
func (v *Value) Interface() interface{} {
return v.data
}
func (v *Value) StringMap() map[string]interface{} {
return v.data.(map[string]interface{})
}
// Private Get
func (v *Value) get(key string) (*Value, error) {
// Assume this is an object
obj, err := v.Object()
if err == nil {
child, ok := obj.Map()[key]
if ok {
return child, nil
} else {
return nil, KeyNotFoundError{key}
}
}
return nil, err
}
// Private get path
func (v *Value) getPath(keys []string) (*Value, error) {
current := v
var err error
for _, key := range keys {
current, err = current.get(key)
if err != nil {
return nil, err
}
}
return current, nil
}
// Gets the value at key path.
// Returns error if the value does not exist.
// Consider using the more specific Get<Type>(..) methods instead.
// Example:
// value, err := GetValue("address", "street")
func (v *Object) GetValue(keys ...string) (*Value, error) {
return v.getPath(keys)
}
// Gets the value at key path and attempts to typecast the value into an object.
// Returns error if the value is not a json object.
// Example:
// object, err := GetObject("person", "address")
func (v *Object) GetObject(keys ...string) (*Object, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
obj, err := child.Object()
if err != nil {
return nil, err
} else {
return obj, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into a string.
// Returns error if the value is not a json string.
// Example:
// string, err := GetString("address", "street")
func (v *Object) GetString(keys ...string) (string, error) {
child, err := v.getPath(keys)
if err != nil {
return "", err
} else {
return child.String()
}
}
func (v *Object) MustGetString(path string, def string) string {
keys := strings.Split(path, ".")
if str, err := v.GetString(keys...); err != nil {
return def
} else {
return str
}
}
// Gets the value at key path and attempts to typecast the value into null.
// Returns error if the value is not json null.
// Example:
// err := GetNull("address", "street")
func (v *Object) GetNull(keys ...string) error {
child, err := v.getPath(keys)
if err != nil {
return err
}
return child.Null()
}
// Gets the value at key path and attempts to typecast the value into a number.
// Returns error if the value is not a json number.
// Example:
// n, err := GetNumber("address", "street_number")
func (v *Object) GetNumber(keys ...string) (json.Number, error) {
child, err := v.getPath(keys)
if err != nil {
return "", err
} else {
n, err := child.Number()
if err != nil {
return "", err
} else {
return n, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into a float64.
// Returns error if the value is not a json number.
// Example:
// n, err := GetNumber("address", "street_number")
func (v *Object) GetFloat64(keys ...string) (float64, error) {
child, err := v.getPath(keys)
if err != nil {
return 0, err
} else {
n, err := child.Float64()
if err != nil {
return 0, err
} else {
return n, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into a float64.
// Returns error if the value is not a json number.
// Example:
// n, err := GetNumber("address", "street_number")
func (v *Object) GetInt64(keys ...string) (int64, error) {
child, err := v.getPath(keys)
if err != nil {
return 0, err
} else {
n, err := child.Int64()
if err != nil {
return 0, err
} else {
return n, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into a float64.
// Returns error if the value is not a json number.
// Example:
// v, err := GetInterface("address", "anything")
func (v *Object) GetInterface(keys ...string) (interface{}, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
return child.Interface(), nil
}
}
// Gets the value at key path and attempts to typecast the value into a bool.
// Returns error if the value is not a json boolean.
// Example:
// married, err := GetBoolean("person", "married")
func (v *Object) GetBoolean(keys ...string) (bool, error) {
child, err := v.getPath(keys)
if err != nil {
return false, err
}
return child.Boolean()
}
// Gets the value at key path and attempts to typecast the value into an array.
// Returns error if the value is not a json array.
// Consider using the more specific Get<Type>Array() since it may reduce later type casts.
// Example:
// friends, err := GetValueArray("person", "friends")
// for i, friend := range friends {
// ... // friend will be of type Value here
// }
func (v *Object) GetValueArray(keys ...string) ([]*Value, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
return child.Array()
}
}
// Gets the value at key path and attempts to typecast the value into an array of objects.
// Returns error if the value is not a json array or if any of the contained objects are not objects.
// Example:
// friends, err := GetObjectArray("person", "friends")
// for i, friend := range friends {
// ... // friend will be of type Object here
// }
func (v *Object) GetObjectArray(keys ...string) ([]*Object, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]*Object, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.
Object()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into an array of string.
// Returns error if the value is not a json array or if any of the contained objects are not strings.
// Gets the value at key path and attempts to typecast the value into an array of objects.
// Returns error if the value is not a json array or if any of the contained objects are not objects.
// Example:
// friendNames, err := GetStringArray("person", "friend_names")
// for i, friendName := range friendNames {
// ... // friendName will be of type string here
// }
func (v *Object) GetStringArray(keys ...string) ([]string, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]string, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.String()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into an array of numbers.
// Returns error if the value is not a json array or if any of the contained objects are not numbers.
// Example:
// friendAges, err := GetNumberArray("person", "friend_ages")
// for i, friendAge := range friendAges {
// ... // friendAge will be of type float64 here
// }
func (v *Object) GetNumberArray(keys ...string) ([]json.Number, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]json.Number, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Number()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into an array of floats.
// Returns error if the value is not a json array or if any of the contained objects are not numbers.
func (v *Object) GetFloat64Array(keys ...string) ([]float64, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]float64, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Float64()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into an array of ints.
// Returns error if the value is not a json array or if any of the contained objects are not numbers.
func (v *Object) GetInt64Array(keys ...string) ([]int64, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]int64, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Int64()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into an array of bools.
// Returns error if the value is not a json array or if any of the contained objects are not booleans.
func (v *Object) GetBooleanArray(keys ...string) ([]bool, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]bool, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Boolean()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
}
// Gets the value at key path and attempts to typecast the value into an array of nulls.
// Returns length, or an error if the value is not a json array or if any of the contained objects are not nulls.
func (v *Object) GetNullArray(keys ...string) (int64, error) {
child, err := v.getPath(keys)
if err != nil {
return 0, err
} else {
array, err := child.Array()
if err != nil {
return 0, err
} else {
var length int64 = 0
for _, arrayItem := range array {
err := arrayItem.Null()
if err != nil {
return 0, err
} else {
length++
}
}
return length, nil
}
}
}
// Returns an error if the value is not actually null
func (v *Value) Null() error {
var valid bool
// Check the type of this data
switch v.data.(type) {
case nil:
valid = v.exists // Valid only if j also exists, since other values could possibly also be nil
break
}
if valid {
return nil
}
return ErrNotNull
}
// Attempts to typecast the current value into an array.
// Returns error if the current value is not a json array.
// Example:
// friendsArray, err := friendsValue.Array()
func (v *Value) Array() ([]*Value, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case []interface{}:
valid = true
break
}
// Unsure if this is a good way to use slices, it's probably not
var slice []*Value
if valid {
for _, element := range v.data.([]interface{}) {
child := Value{element, true}
slice = append(slice, &child)
}
return slice, nil
}
return slice, ErrNotArray
}
// Attempts to typecast the current value into a number.
// Returns error if the current value is not a json number.
// Example:
// ageNumber, err := ageValue.Number()
func (v *Value) Number() (json.Number, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case json.Number:
valid = true
break
}
if valid {
return v.data.(json.Number), nil
}
return "", ErrNotNumber
}
// Attempts to typecast the current value into a float64.
// Returns error if the current value is not a json number.
// Example:
// percentage, err := v.Float64()
func (v *Value) Float64() (float64, error) {
n, err := v.Number()
if err != nil {
return 0, err
}
return n.Float64()
}
// Attempts to typecast the current value into a int64.
// Returns error if the current value is not a json number.
// Example:
// id, err := v.Int64()
func (v *Value) Int64() (int64, error) {
n, err := v.Number()
if err != nil {
return 0, err
}
return n.Int64()
}
// Attempts to typecast the current value into a bool.
// Returns error if the current value is not a json boolean.
// Example:
// marriedBool, err := marriedValue.Boolean()
func (v *Value) Boolean() (bool, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case bool:
valid = true
break
}
if valid {
return v.data.(bool), nil
}
return false, ErrNotBool
}
// Attempts to typecast the current value into an object.
// Returns error if the current value is not a json object.
// Example:
// friendObject, err := friendValue.Object()
func (v *Value) Object() (*Object, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case map[string]interface{}:
valid = true
break
}
if valid {
obj := new(Object)
obj.valid = valid
m := make(map[string]*Value)
if valid {
for key, element := range v.data.(map[string]interface{}) {
m[key] = &Value{element, true}
}
}
obj.data = v.data
obj.m = m
return obj, nil
}
return nil, ErrNotObject
}
// Attempts to typecast the current value into an object arrau.
// Returns error if the current value is not an array of json objects
// Example:
// friendObjects, err := friendValues.ObjectArray()
func (v *Value) ObjectArray() ([]*Object, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case []interface{}:
valid = true
break
}
// Unsure if this is a good way to use slices, it's probably not
var slice []*Object
if valid {
for _, element := range v.data.([]interface{}) {
childValue := Value{element, true}
childObject, err := childValue.Object()
if err != nil {
return nil, ErrNotObjectArray
}
slice = append(slice, childObject)
}
return slice, nil
}
return nil, ErrNotObjectArray
}
// Attempts to typecast the current value into a string.
// Returns error if the current value is not a json string
// Example:
// nameObject, err := nameValue.String()
func (v *Value) String() (string, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case string:
valid = true
break
}
if valid {
return v.data.(string), nil
}
return "", ErrNotString
}
// Returns the value a json formatted string.
// Note: The method named String() is used by golang's log method for logging.
// Example:
func (v *Object) String() string {
f, err := json.Marshal(v.data)
if err != nil {
return err.Error()
}
return string(f)
}
func (v *Object) SetValue(key string, value interface{}) *Value {
data := v.Interface().(map[string]interface{})
data[key] = value
return &Value{
data: value,
exists: true,
}
}

View File

@ -0,0 +1,313 @@
// uses code from https://github.com/antonholmquist/jason/blob/master/jason.go
// MIT Licence
package dynmap
import (
"log"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
type Assert struct {
T *testing.T
}
func NewAssert(t *testing.T) *Assert {
return &Assert{
T: t,
}
}
func (assert *Assert) True(value bool, message string) {
if value == false {
log.Panicln("Assert: ", message)
}
}
func TestFirst(t *testing.T) {
assert := NewAssert(t)
testJSON := `{
"name": "anton",
"age": 29,
"nothing": null,
"true": true,
"false": false,
"list": [
"first",
"second"
],
"list2": [
{
"street": "Street 42",
"city": "Stockholm"
},
{
"street": "Street 42",
"city": "Stockholm"
}
],
"address": {
"street": "Street 42",
"city": "Stockholm"
},
"country": {
"name": "Sweden"
}
}`
j, err := NewObjectFromBytes([]byte(testJSON))
a, err := j.GetObject("address")
assert.True(a != nil && err == nil, "failed to create json from string")
assert.True(err == nil, "failed to create json from string")
s, err := j.GetString("name")
assert.True(s == "anton" && err == nil, "name should be a string")
s = j.MustGetString("name", "fallback")
assert.True(s == "anton", "must get string")
s = j.MustGetString("adsasdas", "fallback")
assert.True(s == "fallback", "must get string return fallback")
s, err = j.GetString("name")
assert.True(s == "anton" && err == nil, "name shoud match")
s, err = j.GetString("address", "street")
assert.True(s == "Street 42" && err == nil, "street shoud match")
//log.Println("s: ", s.String())
_, err = j.GetNumber("age")
assert.True(err == nil, "age should be a number")
n, err := j.GetInt64("age")
assert.True(n == 29 && err == nil, "age mismatch")
ageInterface, err := j.GetInterface("age")
assert.True(ageInterface != nil, "should be defined")
assert.True(err == nil, "age interface error")
invalidInterface, err := j.GetInterface("not_existing")
assert.True(invalidInterface == nil, "should not give error here")
assert.True(err != nil, "should give error here")
age, err := j.GetValue("age")
assert.True(age != nil && err == nil, "age should exist")
age2, err := j.GetValue("age2")
assert.True(age2 == nil && err != nil, "age2 should not exist")
address, err := j.GetObject("address")
assert.True(address != nil && err == nil, "address should be an object")
//log.Println("address: ", address)
s, err = address.GetString("street")
addressAsString, err := j.GetString("address")
assert.True(addressAsString == "" && err != nil, "address should not be an string")
s, err = j.GetString("address", "street")
assert.True(s == "Street 42" && err == nil, "street mismatching")
s, err = j.GetString("address", "name2")
assert.True(s == "" && err != nil, "nonexistent string fail")
b, err := j.GetBoolean("true")
assert.True(b == true && err == nil, "bool true test")
b, err = j.GetBoolean("false")
assert.True(b == false && err == nil, "bool false test")
b, err = j.GetBoolean("invalid_field")
assert.True(b == false && err != nil, "bool invalid test")
list, err := j.GetValueArray("list")
assert.True(list != nil && err == nil, "list should be an array")
list2, err := j.GetValueArray("list2")
assert.True(list2 != nil && err == nil, "list2 should be an array")
list2Array, err := j.GetValueArray("list2")
assert.True(err == nil, "List2 should not return error on AsArray")
assert.True(len(list2Array) == 2, "List2 should should have length 2")
list2Value, err := j.GetValue("list2")
assert.True(err == nil, "List2 should not return error on value")
list2ObjectArray, err := list2Value.ObjectArray()
assert.True(err == nil, "list2Value should not return error on ObjectArray")
assert.True(len(list2ObjectArray) == 2, "list2ObjectArray should should have length 2")
for _, elementValue := range list2Array {
//assert.True(element.IsObject() == true, "first fail")
element, err := elementValue.Object()
s, err = element.GetString("street")
assert.True(s == "Street 42" && err == nil, "second fail")
}
obj, err := j.GetObject("country")
assert.True(obj != nil && err == nil, "country should not return error on AsObject")
for key, value := range obj.Map() {
assert.True(key == "name", "country name key incorrect")
s, err = value.String()
assert.True(s == "Sweden" && err == nil, "country name should be Sweden")
}
}
func TestSecond(t *testing.T) {
json := `
{
"data": [
{
"id": "X999_Y999",
"from": {
"name": "Tom Brady", "id": "X12"
},
"message": "Looking forward to 2010!",
"actions": [
{
"name": "Comment",
"link": "http://www.facebook.com/X999/posts/Y999"
},
{
"name": "Like",
"link": "http://www.facebook.com/X999/posts/Y999"
}
],
"type": "status",
"created_time": "2010-08-02T21:27:44+0000",
"updated_time": "2010-08-02T21:27:44+0000"
},
{
"id": "X998_Y998",
"from": {
"name": "Peyton Manning", "id": "X18"
},
"message": "Where's my contract?",
"actions": [
{
"name": "Comment",
"link": "http://www.facebook.com/X998/posts/Y998"
},
{
"name": "Like",
"link": "http://www.facebook.com/X998/posts/Y998"
}
],
"type": "status",
"created_time": "2010-08-02T21:27:44+0000",
"updated_time": "2010-08-02T21:27:44+0000"
}
]
}`
assert := NewAssert(t)
j, err := NewObjectFromBytes([]byte(json))
assert.True(j != nil && err == nil, "failed to parse json")
dataObject, err := j.GetObject("data")
assert.True(dataObject == nil && err != nil, "data should not be an object")
dataArray, err := j.GetObjectArray("data")
assert.True(dataArray != nil && err == nil, "data should be an object array")
for index, dataItem := range dataArray {
if index == 0 {
id, err := dataItem.GetString("id")
assert.True(id == "X999_Y999" && err == nil, "item id mismatch")
fromName, err := dataItem.GetString("from", "name")
assert.True(fromName == "Tom Brady" && err == nil, "fromName mismatch")
actions, err := dataItem.GetObjectArray("actions")
for index, action := range actions {
if index == 1 {
name, err := action.GetString("name")
assert.True(name == "Like" && err == nil, "name mismatch")
link, err := action.GetString("link")
assert.True(link == "http://www.facebook.com/X999/posts/Y999" && err == nil, "Like mismatch")
}
}
} else if index == 1 {
id, err := dataItem.GetString("id")
assert.True(id == "X998_Y998" && err == nil, "item id mismatch")
}
}
}
func TestErrors(t *testing.T) {
json := `
{
"string": "hello",
"number": 1,
"array": [1,2,3]
}`
errstr := "expected an error getting %s, but got '%s'"
j, err := NewObjectFromBytes([]byte(json))
if err != nil {
t.Fatal("failed to parse json")
}
if _, err = j.GetObject("string"); err != ErrNotObject {
t.Errorf(errstr, "object", err)
}
if err = j.GetNull("string"); err != ErrNotNull {
t.Errorf(errstr, "null", err)
}
if _, err = j.GetStringArray("string"); err != ErrNotArray {
t.Errorf(errstr, "array", err)
}
if _, err = j.GetStringArray("array"); err != ErrNotString {
t.Errorf(errstr, "string array", err)
}
if _, err = j.GetNumber("array"); err != ErrNotNumber {
t.Errorf(errstr, "number", err)
}
if _, err = j.GetBoolean("array"); err != ErrNotBool {
t.Errorf(errstr, "boolean", err)
}
if _, err = j.GetString("number"); err != ErrNotString {
t.Errorf(errstr, "string", err)
}
_, err = j.GetString("not_found")
if e, ok := err.(KeyNotFoundError); !ok {
t.Errorf(errstr, "key not found error", e)
}
}
func TestWriting(t *testing.T) {
Convey("When writing", t, func() {
j, _ := NewObjectFromBytes([]byte(`{}`))
j.SetValue("prop", "value")
So(j.MustGetString("prop", ""), ShouldEqual, "value")
})
}

View File

@ -0,0 +1,468 @@
package simplejson
import (
"bytes"
"encoding/json"
"errors"
"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) {
if j == nil || j.data == nil {
return nil, nil
}
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{}),
}
}
// New returns a pointer to a new, empty `Json` object
func NewFromAny(data interface{}) *Json {
return &Json{data: data}
}
// 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) {
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
}

View File

@ -0,0 +1,89 @@
// +build go1.1
package simplejson
import (
"bytes"
"encoding/json"
"errors"
"io"
"reflect"
"strconv"
)
// Implements the json.Unmarshaler interface.
func (j *Json) UnmarshalJSON(p []byte) error {
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")
}

View File

@ -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)
}

View File

@ -1,6 +1,10 @@
package models package models
import "time" import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
)
// DashboardSnapshot model // DashboardSnapshot model
type DashboardSnapshot struct { type DashboardSnapshot struct {
@ -17,7 +21,7 @@ type DashboardSnapshot struct {
Created time.Time Created time.Time
Updated time.Time Updated time.Time
Dashboard map[string]interface{} Dashboard *simplejson.Json
} }
// DashboardSnapshotDTO without dashboard map // DashboardSnapshotDTO without dashboard map
@ -40,7 +44,7 @@ type DashboardSnapshotDTO struct {
// COMMANDS // COMMANDS
type CreateDashboardSnapshotCommand struct { type CreateDashboardSnapshotCommand struct {
Dashboard map[string]interface{} `json:"dashboard" binding:"Required"` Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
Name string `json:"name" binding:"Required"` Name string `json:"name" binding:"Required"`
Expires int64 `json:"expires"` Expires int64 `json:"expires"`

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{}) func NewDashboardFromJson(data *simplejson.Json) *Dashboard {
b := make([]string, len(arr))
for i := range arr {
b[i] = arr[i].(string)
}
return b
}
func NewDashboardFromJson(data map[string]interface{}) *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,7 +110,7 @@ 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"`

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

@ -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
@ -86,7 +88,7 @@ type AddDataSourceCommand struct {
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:"-"`
@ -107,7 +109,7 @@ type UpdateDataSourceCommand struct {
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:"-"`

View File

@ -0,0 +1,171 @@
package plugins
import (
"encoding/json"
"fmt"
"regexp"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
)
type ImportDashboardCommand struct {
Path string `json:"string"`
Inputs []ImportDashboardInput `json:"inputs"`
OrgId int64 `json:"-"`
UserId int64 `json:"-"`
PluginId string `json:"-"`
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("Dashbord input variable: %v missing from import command", e.VariableName)
}
func init() {
bus.AddHandler("plugins", ImportDashboard)
}
func ImportDashboard(cmd *ImportDashboardCommand) error {
plugin, exists := Plugins[cmd.PluginId]
if !exists {
return PluginNotFoundError{cmd.PluginId}
}
var dashboard *m.Dashboard
var err error
if dashboard, err = loadPluginDashboard(plugin, cmd.Path); err != nil {
return err
}
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.UserId,
}
if err := bus.Dispatch(&saveCmd); err != nil {
return err
}
cmd.Result = &PluginDashboardInfoDTO{
PluginId: cmd.PluginId,
Title: dashboard.Title,
Path: cmd.Path,
Revision: dashboard.GetString("revision", "1.0"),
InstalledUri: "db/" + saveCmd.Result.Slug,
InstalledRevision: dashboard.GetString("revision", "1.0"),
Installed: 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, varDef *simplejson.Json) *ImportDashboardInput {
inputType := varDef.Get("type").MustString()
for _, input := range this.inputs {
if inputType == 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("\\$__(\\w+)")
// check that we have all inputs we need
if inputDefs := this.template.Get("__inputs"); inputDefs != nil {
for varName, value := range inputDefs.MustMap() {
input := this.findInput(varName, simplejson.NewFromAny(value))
if input == nil {
return nil, &DashboardInputMissingError{VariableName: varName}
}
this.variables["$__"+varName] = input.Value
}
} else {
log.Info("Import: dashboard has no __import section")
}
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 {
return this.variables[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
}

View File

@ -0,0 +1,93 @@
package plugins
import (
"io/ioutil"
"testing"
"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/setting"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func TestDashboardImport(t *testing.T) {
Convey("When importing plugin dashboard", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.test-app")
sec.NewKey("path", "../../tests/test-app")
err := Init()
So(err, ShouldBeNil)
var importedDash *m.Dashboard
bus.AddHandler("test", func(cmd *m.SaveDashboardCommand) error {
importedDash = cmd.GetDashboardModel()
cmd.Result = importedDash
return nil
})
cmd := ImportDashboardCommand{
PluginId: "test-app",
Path: "dashboards/connections.json",
OrgId: 1,
UserId: 1,
Inputs: []ImportDashboardInput{
{Name: "*", Type: "datasource", Value: "graphite"},
},
}
err = ImportDashboard(&cmd)
So(err, ShouldBeNil)
Convey("should install dashboard", func() {
So(importedDash, ShouldNotBeNil)
resultStr, _ := importedDash.Data.EncodePretty()
expectedBytes, _ := ioutil.ReadFile("../../tests/test-app/dashboards/connections_result.json")
expectedJson, _ := simplejson.NewJson(expectedBytes)
expectedStr, _ := expectedJson.EncodePretty()
So(string(resultStr), ShouldEqual, string(expectedStr))
panel := importedDash.Data.Get("rows").GetIndex(0).Get("panels").GetIndex(0)
So(panel.Get("datasource").MustString(), ShouldEqual, "graphite")
})
})
Convey("When evaling dashboard template", t, func() {
template, _ := simplejson.NewJson([]byte(`{
"__inputs": {
"graphite": {
"type": "datasource"
}
},
"test": {
"prop": "$__graphite"
}
}`))
evaluator := &DashTemplateEvaluator{
template: template,
inputs: []ImportDashboardInput{
{Name: "*", Type: "datasource", Value: "my-server"},
},
}
res, err := evaluator.Eval()
So(err, ShouldBeNil)
Convey("should render template", func() {
So(res.GetPath("test", "prop").MustString(), ShouldEqual, "my-server")
})
Convey("should not include inputs in output", func() {
inputs := res.Get("__inputs")
So(inputs.Interface(), ShouldBeNil)
})
})
}

View File

@ -1,57 +0,0 @@
package plugins
import (
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
)
type InstallPluginDashboardCommand struct {
Path string `json:"string"`
Inputs map[string]interface{} `json:"inputs"`
OrgId int64 `json:"-"`
UserId int64 `json:"-"`
PluginId string `json:"-"`
Result *PluginDashboardInfoDTO
}
func init() {
bus.AddHandler("plugins", InstallPluginDashboard)
}
func InstallPluginDashboard(cmd *InstallPluginDashboardCommand) error {
plugin, exists := Plugins[cmd.PluginId]
if !exists {
return PluginNotFoundError{cmd.PluginId}
}
var dashboard *m.Dashboard
var err error
if dashboard, err = loadPluginDashboard(plugin, cmd.Path); err != nil {
return err
}
saveCmd := m.SaveDashboardCommand{
Dashboard: dashboard.Data,
OrgId: cmd.OrgId,
UserId: cmd.UserId,
}
if err := bus.Dispatch(&saveCmd); err != nil {
return err
}
cmd.Result = &PluginDashboardInfoDTO{
PluginId: cmd.PluginId,
Title: dashboard.Title,
Path: cmd.Path,
Revision: dashboard.GetString("revision", "1.0"),
InstalledUri: "db/" + saveCmd.Result.Slug,
InstalledRevision: dashboard.GetString("revision", "1.0"),
Installed: true,
}
return nil
}

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
} }

View File

@ -69,7 +69,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
affectedRows, err = sess.Insert(dash) affectedRows, err = sess.Insert(dash)
} else { } else {
dash.Version += 1 dash.Version += 1
dash.Data["version"] = dash.Version dash.Data.Set("version", dash.Version)
affectedRows, err = sess.Id(dash.Id).Update(dash) affectedRows, err = sess.Id(dash.Id).Update(dash)
} }
@ -108,7 +108,7 @@ func GetDashboard(query *m.GetDashboardQuery) error {
return m.ErrDashboardNotFound return m.ErrDashboardNotFound
} }
dashboard.Data["id"] = dashboard.Id dashboard.Data.Set("id", dashboard.Id)
query.Result = &dashboard query.Result = &dashboard
return nil return nil

View File

@ -5,6 +5,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/grafana/grafana/pkg/components/simplejson"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
) )
@ -16,9 +17,9 @@ func TestDashboardSnapshotDBAccess(t *testing.T) {
Convey("Given saved snaphot", func() { Convey("Given saved snaphot", func() {
cmd := m.CreateDashboardSnapshotCommand{ cmd := m.CreateDashboardSnapshotCommand{
Key: "hej", Key: "hej",
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"hello": "mupp", "hello": "mupp",
}, }),
} }
err := CreateDashboardSnapshot(&cmd) err := CreateDashboardSnapshot(&cmd)
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -29,7 +30,7 @@ func TestDashboardSnapshotDBAccess(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(query.Result, ShouldNotBeNil) So(query.Result, ShouldNotBeNil)
So(query.Result.Dashboard["hello"], ShouldEqual, "mupp") So(query.Result.Dashboard.Get("hello").MustString(), ShouldEqual, "mupp")
}) })
}) })

View File

@ -5,6 +5,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"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/services/search" "github.com/grafana/grafana/pkg/services/search"
) )
@ -12,11 +13,11 @@ import (
func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard { func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: orgId, OrgId: orgId,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil, "id": nil,
"title": title, "title": title,
"tags": tags, "tags": tags,
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
@ -58,11 +59,11 @@ func TestDashboardDataAccess(t *testing.T) {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: 1, OrgId: 1,
Overwrite: true, Overwrite: true,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": float64(123412321), "id": float64(123412321),
"title": "Expect error", "title": "Expect error",
"tags": []interface{}{}, "tags": []interface{}{},
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
@ -76,11 +77,11 @@ func TestDashboardDataAccess(t *testing.T) {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: 2, OrgId: 2,
Overwrite: true, Overwrite: true,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": float64(query.Result.Id), "id": float64(query.Result.Id),
"title": "Expect error", "title": "Expect error",
"tags": []interface{}{}, "tags": []interface{}{},
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)
@ -135,11 +136,11 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Should not be able to save dashboard with same name", func() { Convey("Should not be able to save dashboard with same name", func() {
cmd := m.SaveDashboardCommand{ cmd := m.SaveDashboardCommand{
OrgId: 1, OrgId: 1,
Dashboard: map[string]interface{}{ Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil, "id": nil,
"title": "test dash 23", "title": "test dash 23",
"tags": []interface{}{}, "tags": []interface{}{},
}, }),
} }
err := SaveDashboard(&cmd) err := SaveDashboard(&cmd)

View File

@ -137,8 +137,8 @@ export class DashNavCtrl {
$scope.deleteDashboard = function() { $scope.deleteDashboard = function() {
$scope.appEvent('confirm-modal', { $scope.appEvent('confirm-modal', {
title: 'Delete dashboard', title: 'Delete',
text: 'Do you want to delete dashboard?', text: 'Do you want to delete this dashboard?',
text2: $scope.dashboard.title, text2: $scope.dashboard.title,
icon: 'fa-trash', icon: 'fa-trash',
yesText: 'Delete', yesText: 'Delete',

View File

@ -21,13 +21,13 @@
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
<button class="btn btn-secondary" ng-click="ctrl.import(dash, false)" ng-show="!dash.installed"> <button class="btn btn-secondary" ng-click="ctrl.import(dash, false)" ng-show="!dash.installed">
Install Import
</button> </button>
<button class="btn btn-secondary" ng-click="ctrl.import(dash, true)" ng-show="dash.installed"> <button class="btn btn-secondary" ng-click="ctrl.import(dash, true)" ng-show="dash.installed">
Re-Install Re-Import
</button> </button>
<button class="btn btn-danger" ng-click="ctrl.remove(dash)" ng-show="dash.installed"> <button class="btn btn-danger" ng-click="ctrl.remove(dash)" ng-show="dash.installed">
Un-install Delete
</button> </button>
</td> </td>
</tr> </tr>

View File

@ -7,6 +7,7 @@ import coreModule from 'app/core/core_module';
export class DashImportListCtrl { export class DashImportListCtrl {
dashboards: any[]; dashboards: any[];
plugin: any; plugin: any;
datasource: any;
constructor(private $http, private backendSrv, private $rootScope) { constructor(private $http, private backendSrv, private $rootScope) {
this.dashboards = []; this.dashboards = [];
@ -21,10 +22,19 @@ export class DashImportListCtrl {
pluginId: this.plugin.id, pluginId: this.plugin.id,
path: dash.path, path: dash.path,
reinstall: reinstall, reinstall: reinstall,
inputs: {} inputs: []
}; };
this.backendSrv.post(`/api/plugins/dashboards/install`, installCmd).then(res => { if (this.datasource) {
installCmd.inputs.push({
name: '*',
type: 'datasource',
pluginId: this.datasource.type,
value: this.datasource.name
});
}
this.backendSrv.post(`/api/plugins/dashboards/import`, installCmd).then(res => {
this.$rootScope.appEvent('alert-success', ['Dashboard Installed', dash.title]); this.$rootScope.appEvent('alert-success', ['Dashboard Installed', dash.title]);
_.extend(dash, res); _.extend(dash, res);
}); });
@ -46,7 +56,8 @@ export function dashboardImportList() {
bindToController: true, bindToController: true,
controllerAs: 'ctrl', controllerAs: 'ctrl',
scope: { scope: {
plugin: "=" plugin: "=",
datasource: "="
} }
}; };
} }

View File

@ -74,7 +74,7 @@
</div> </div>
<div ng-if="ctrl.tabIndex === 1" class="tab-content"> <div ng-if="ctrl.tabIndex === 1" class="tab-content">
<dashboard-import-list plugin="ctrl.datasourceMeta"></dashboard-import-list> <dashboard-import-list plugin="ctrl.datasourceMeta" datasource="ctrl.current"></dashboard-import-list>
</div> </div>
</div> </div>

View File

@ -0,0 +1,176 @@
{
"__inputs": {
"graphite": {
"type": "datasource",
"pluginId": "graphite",
"description": "Graphite datasource"
}
},
"revision": "1.0",
"title": "Graphite Carbon Metrics",
"tags": ["graphite", "carbon"],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapsable": true,
"collapse": false,
"editable": true,
"height": "350px",
"notice": false,
"panels": [
{
"aliasColors": {},
"annotate": {
"enable": false
},
"bars": false,
"datasource": "$__graphite",
"editable": true,
"fill": 0,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"max": null,
"min": 0,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"loadingEditor": false,
"nullPointMode": "null as zero",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"resolution": 100,
"scale": 1,
"seriesOverrides": [
{
"alias": "Points Per Update",
"yaxis": 2
},
{
"alias": "CPU",
"yaxis": 2
}
],
"span": 12,
"stack": false,
"steppedLine": false,
"targets": [
{
"refId": "A",
"target": "alias(sumSeries(carbon.agents.*.updateOperations),\"Updates\") "
},
{
"refId": "B",
"target": "alias(sumSeries(carbon.agents.*.metricsReceived),'Metrics Received')"
},
{
"refId": "C",
"target": "alias(sumSeries(carbon.agents.*.committedPoints),'Committed Points')"
},
{
"refId": "D",
"target": "alias(sumSeries(carbon.agents.*.pointsPerUpdate),'Points Per Update')"
},
{
"refId": "E",
"target": "alias(averageSeries(carbon.agents.*.cpuUsage),'CPU')"
},
{
"refId": "F",
"target": "alias(sumSeries(carbon.agents.*.creates),'Creates')"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Graphite Carbon Metrics",
"tooltip": {
"query_as_alias": true,
"shared": false,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"short",
"short"
],
"zerofill": true
}
],
"title": "Row1"
}
],
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {
"collapse": false,
"enable": true,
"notice": false,
"now": true,
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"type": "timepicker"
},
"templating": {
"enable": false,
"list": []
},
"annotations": {
"enable": false,
"list": []
},
"refresh": false,
"schemaVersion": 8,
"version": 2,
"links": []
}

View File

@ -1,21 +0,0 @@
{
"__inputs": {
"graphite": {
"type": "datasource",
"description": "Graphite datasource"
}
},
"title": "Carbon Cache Stats",
"version": 1,
"rows": [
{
"panels": [
{
"type": "graph",
"datasource": "__$graphite"
}
]
}
]
}

View File

@ -4,7 +4,7 @@
"id": "graphite", "id": "graphite",
"includes": [ "includes": [
{"type": "dashboard", "name": "Carbon Cache Stats", "path": "dashboards/carbon_stats.json"} {"type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json"}
], ],
"metrics": true, "metrics": true,

View File

@ -1,5 +1,28 @@
{ {
"__inputs": {
"graphite": {
"type": "datasource",
"pluginId": "graphite",
"description": "Graphite datasource"
}
},
"title": "Nginx Connections", "title": "Nginx Connections",
"revision": "1.5", "revision": "1.5",
"schemaVersion": 11 "schemaVersion": 11,
"tags": ["tag1", "tag2"],
"number_array": [1,2,3,10.33],
"boolean_true": true,
"boolean_false": false,
"rows": [
{
"panels": [
{
"type": "graph",
"datasource": "$__graphite"
}
]
}
]
} }

View File

@ -0,0 +1,20 @@
{
"revision": "1.5",
"tags": ["tag1", "tag2"],
"boolean_false": false,
"boolean_true": true,
"number_array": [1,2,3,10.33],
"rows": [
{
"panels": [
{
"datasource": "graphite",
"type": "graph"
}
]
}
],
"schemaVersion": 11,
"title": "Nginx Connections",
"version": 0
}