Skip to content

Commit 87b5b48

Browse files
GiedriusSslok
andauthored
board: use a more correct type for "Text" (#128)
* Use reliable conversion of int to string Signed-off-by: Xabier Larrakoetxea <[email protected]> * Create a new custom type that can handle an string or slice of strings Signed-off-by: Xabier Larrakoetxea <[email protected]> * Use StringSliceString for current.Text fields Signed-off-by: Xabier Larrakoetxea <[email protected]> * Add use case on integration marshal templates tests Signed-off-by: Xabier Larrakoetxea <[email protected]> Co-authored-by: Xabier Larrakoetxea <[email protected]>
1 parent d230043 commit 87b5b48

File tree

4 files changed

+180
-4
lines changed

4 files changed

+180
-4
lines changed

board.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ type (
106106
}
107107
// for templateVar
108108
Current struct {
109-
Tags []*string `json:"tags,omitempty"`
110-
Text interface{} `json:"text"`
111-
Value interface{} `json:"value"` // TODO select more precise type
109+
Tags []*string `json:"tags,omitempty"`
110+
Text *StringSliceString `json:"text"`
111+
Value interface{} `json:"value"` // TODO select more precise type
112112
}
113113
Annotation struct {
114114
Name string `json:"name"`

custom-types.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,45 @@ func (v *FloatString) MarshalJSON() ([]byte, error) {
191191

192192
return []byte(`"null"`), nil
193193
}
194+
195+
// StringSliceString represents special type for json values that could be
196+
// strings or slice of strings: "something" or ["something"].
197+
type StringSliceString struct {
198+
Value []string
199+
Valid bool
200+
}
201+
202+
// UnmarshalJSON implements custom unmarshalling for StringSliceString type.
203+
func (v *StringSliceString) UnmarshalJSON(raw []byte) error {
204+
if raw == nil || bytes.Equal(raw, []byte(`"null"`)) {
205+
return nil
206+
}
207+
208+
// First try with string.
209+
var str string
210+
if err := json.Unmarshal(raw, &str); err == nil {
211+
v.Value = []string{str}
212+
v.Valid = true
213+
return nil
214+
}
215+
216+
// Lastly try with string slice.
217+
var strSlice []string
218+
err := json.Unmarshal(raw, &strSlice)
219+
if err != nil {
220+
return err
221+
}
222+
223+
v.Value = strSlice
224+
v.Valid = true
225+
return nil
226+
}
227+
228+
// MarshalJSON implements custom marshalling for StringSliceString type.
229+
func (v *StringSliceString) MarshalJSON() ([]byte, error) {
230+
if !v.Valid {
231+
return []byte(`"null"`), nil
232+
}
233+
234+
return json.Marshal(v.Value)
235+
}

custom-types_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package sdk_test
2222
import (
2323
"bytes"
2424
"encoding/json"
25+
"fmt"
2526
"testing"
2627

2728
"github.com/grafana-tools/sdk"
@@ -57,3 +58,136 @@ func TestIntString_Marshal(t *testing.T) {
5758
t.Error("Marshalled IntString is not valid: expected '100', got:", string(body))
5859
}
5960
}
61+
62+
func TestStringSliceString_Unmarshal(t *testing.T) {
63+
tests := map[string]struct {
64+
raw string
65+
exp sdk.StringSliceString
66+
expErr bool
67+
}{
68+
"Having an nil should unmarshall correctly as invalid.": {
69+
raw: `"null"`,
70+
exp: sdk.StringSliceString{
71+
Valid: false,
72+
},
73+
},
74+
75+
"Having a string should unmarshall correctly.": {
76+
raw: `"this is a test"`,
77+
exp: sdk.StringSliceString{
78+
Value: []string{"this is a test"},
79+
Valid: true,
80+
},
81+
},
82+
83+
"Having a empty array should unmarshall correctly.": {
84+
raw: `[]`,
85+
exp: sdk.StringSliceString{
86+
Value: []string{},
87+
Valid: true,
88+
},
89+
},
90+
91+
"Having a single value array should unmarshall correctly.": {
92+
raw: `["this is a test"]`,
93+
exp: sdk.StringSliceString{
94+
Value: []string{"this is a test"},
95+
Valid: true,
96+
},
97+
},
98+
99+
"Having a multiple value array should unmarshall correctly.": {
100+
raw: `["this", "is", "a", "test"]`,
101+
exp: sdk.StringSliceString{
102+
Value: []string{"this", "is", "a", "test"},
103+
Valid: true,
104+
},
105+
},
106+
107+
"Having a wrong value should fail.": {
108+
raw: `[`,
109+
expErr: true,
110+
},
111+
}
112+
for name, test := range tests {
113+
t.Run(name, func(t *testing.T) {
114+
var got sdk.StringSliceString
115+
116+
err := json.Unmarshal([]byte(test.raw), &got)
117+
118+
if test.expErr {
119+
if err == nil {
120+
t.Error("Expected error, got nil")
121+
}
122+
} else {
123+
if err != nil {
124+
t.Errorf("Got unexpected error: %s", err)
125+
}
126+
127+
if test.exp.Valid != got.Valid {
128+
t.Errorf("Valid field is not valid, expected %t; got: %t", test.exp.Valid, got.Valid)
129+
}
130+
131+
if fmt.Sprintf("%#v", test.exp.Value) != fmt.Sprintf("%#v", got.Value) {
132+
t.Errorf("Value field is not valid, expected %#v; got: %#v", test.exp.Value, got.Value)
133+
}
134+
}
135+
})
136+
}
137+
}
138+
139+
func TestStringSliceString_Marshall(t *testing.T) {
140+
tests := map[string]struct {
141+
value *sdk.StringSliceString
142+
exp string
143+
expErr bool
144+
}{
145+
"Having a single value should unmarshall correctly.": {
146+
value: &sdk.StringSliceString{
147+
Value: []string{"this is a test"},
148+
Valid: true,
149+
},
150+
exp: `["this is a test"]`,
151+
},
152+
153+
"Having an invalid value should return null.": {
154+
value: &sdk.StringSliceString{},
155+
exp: `"null"`,
156+
},
157+
158+
"Having a empty array should unmarshall correctly.": {
159+
value: &sdk.StringSliceString{
160+
Value: []string{},
161+
Valid: true,
162+
},
163+
exp: `[]`,
164+
},
165+
166+
"Having a multiple value array should unmarshall correctly.": {
167+
value: &sdk.StringSliceString{
168+
Value: []string{"this", "is", "a", "test"},
169+
Valid: true,
170+
},
171+
exp: `["this","is","a","test"]`,
172+
},
173+
}
174+
for name, test := range tests {
175+
t.Run(name, func(t *testing.T) {
176+
got, err := json.Marshal(test.value)
177+
178+
if test.expErr {
179+
if err == nil {
180+
t.Error("Expected error, got nil")
181+
}
182+
} else {
183+
if err != nil {
184+
t.Errorf("Got unexpected error: %s", err)
185+
}
186+
187+
if test.exp != string(got) {
188+
t.Errorf("Marshaled value is invalid, expected %s; got: %s", test.exp, string(got))
189+
}
190+
}
191+
})
192+
}
193+
}

testdata/empty-dashboard-with-templating-4.0.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"allValue": null,
2929
"current": {
3030
"tags": [],
31-
"text": "one",
31+
"text": ["one"],
3232
"value": "one"
3333
},
3434
"datasource": null,

0 commit comments

Comments
 (0)