Skip to content

Commit 11b1efc

Browse files
authored
Adding custom serialization for Request object. (#153)
* Adding custom serialization for Request object. ChangeLog: - Removed panic and replace panic in tests with asserts * Cleaner custom serialization and code review comments
1 parent f35c5af commit 11b1efc

File tree

4 files changed

+138
-31
lines changed

4 files changed

+138
-31
lines changed

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ module github.com/grafana-tools/sdk
33
go 1.13
44

55
require (
6-
github.com/chromedp/cdproto v0.0.0-20210706234513-2bc298e8be7f
6+
github.com/chromedp/cdproto v0.0.0-20210706234513-2bc298e8be7f // indirect
77
github.com/chromedp/chromedp v0.7.3
88
github.com/gosimple/slug v1.1.1
99
github.com/pkg/errors v0.9.1
1010
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
11+
github.com/stretchr/testify v1.7.0
1112
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
1213
)

go.sum

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,38 @@
1-
github.com/chromedp/cdproto v0.0.0-20210122124816-7a656c010d57 h1:htpyTFarq7OHx9SpkQ+7x20thTQA6JAsgnuMGoPbH4E=
2-
github.com/chromedp/cdproto v0.0.0-20210122124816-7a656c010d57/go.mod h1:55pim6Ht4LJKdVLlyFJV/g++HsEA1hQxPbB5JyNdZC0=
31
github.com/chromedp/cdproto v0.0.0-20210526005521-9e51b9051fd0/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
42
github.com/chromedp/cdproto v0.0.0-20210706234513-2bc298e8be7f h1:lg5k1KAxmknil6Z19LaaeiEs5Pje7hPzRfyWSSnWLP0=
53
github.com/chromedp/cdproto v0.0.0-20210706234513-2bc298e8be7f/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
6-
github.com/chromedp/chromedp v0.6.5 h1:hPaDYBpvD2WFicln0ByzV+XRhSOtLgAgsu39O455iWY=
7-
github.com/chromedp/chromedp v0.6.5/go.mod h1:/Q6h52DkrFuvOgmCuR6O3xT5g0bZYoPqjANKBEvQGEY=
84
github.com/chromedp/chromedp v0.7.3 h1:FvgJICfjvXtDX+miuMUY0NHuY8zQvjS/TcEQEG6Ldzs=
95
github.com/chromedp/chromedp v0.7.3/go.mod h1:9gC521Yzgrk078Ulv6KIgG7hJ2x9aWrxMBBobTFk30A=
106
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
117
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
8+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
9+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1210
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
1311
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
1412
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
1513
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
16-
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
17-
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
1814
github.com/gobwas/ws v1.1.0-rc.5 h1:QOAag7FoBaBYYHRqzqkhhd8fq5RTubvI4v3Ft/gDVVQ=
1915
github.com/gobwas/ws v1.1.0-rc.5/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
2016
github.com/gosimple/slug v1.1.1 h1:fRu/digW+NMwBIP+RmviTK97Ho/bEj/C9swrCspN3D4=
2117
github.com/gosimple/slug v1.1.1/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
2218
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
2319
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
24-
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
25-
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
2620
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
2721
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
2822
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
2923
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
24+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
25+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3026
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
3127
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
28+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
29+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
30+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3231
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
33-
golang.org/x/sys v0.0.0-20210122093101-04d7465088b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
3432
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3533
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
3634
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
35+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
36+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
37+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
38+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

rest-dashboard.go

+68-21
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,45 @@ type BoardProperties struct {
5757
FolderURL string `json:"folderUrl"`
5858
}
5959

60+
//RawBoardRequest struct that wraps Board and parameters being sent
61+
type RawBoardRequest struct {
62+
Dashboard []byte
63+
Parameters SetDashboardParams
64+
}
65+
66+
//MarshalJSON serializes the request to match the expectations of the grafana API.
67+
// Additionally, if preseveID is false, then the dashboard id is set to 0
68+
func (d RawBoardRequest) MarshalJSON() ([]byte, error) {
69+
var raw []byte
70+
71+
if d.Parameters.PreserveId {
72+
raw = d.Dashboard
73+
} else {
74+
var plain map[string]interface{} = make(map[string]interface{})
75+
if err := json.Unmarshal(d.Dashboard, &plain); err != nil {
76+
return nil, err
77+
}
78+
plain["id"] = 0
79+
raw, _ = json.Marshal(plain)
80+
}
81+
82+
var output struct {
83+
Dashboard map[string]interface{} `json:"dashboard"`
84+
SetDashboardParams
85+
}
86+
err := json.Unmarshal(raw, &output.Dashboard)
87+
if err != nil {
88+
return nil, err
89+
}
90+
output.SetDashboardParams = d.Parameters
91+
result, err := json.Marshal(&output)
92+
if err != nil {
93+
return nil, err
94+
}
95+
96+
return result, nil
97+
}
98+
6099
// GetDashboardByUID loads a dashboard and its metadata from Grafana by dashboard uid.
61100
//
62101
// Reflects GET /api/dashboards/uid/:uid API call.
@@ -219,11 +258,12 @@ func (r *Client) Search(ctx context.Context, params ...SearchParam) ([]FoundBoar
219258
return boards, err
220259
}
221260

222-
// SetDashboardParams contains the extra parameteres
261+
// SetDashboardParams contains the extra parameters
223262
// that affects where and how the dashboard will be stored
224263
type SetDashboardParams struct {
225-
FolderID int
226-
Overwrite bool
264+
FolderID int
265+
Overwrite bool
266+
PreserveId bool `json:"-"`
227267
}
228268

229269
// SetDashboard updates existing dashboard or creates a new one.
@@ -271,31 +311,20 @@ func (r *Client) SetDashboard(ctx context.Context, board Board, params SetDashbo
271311
return resp, nil
272312
}
273313

274-
// SetRawDashboard updates existing dashboard or creates a new one.
275-
// Contrary to SetDashboard() it accepts raw JSON instead of Board structure.
276-
// Grafana only can create or update a dashboard in a database. File dashboards
277-
// may be only loaded with HTTP API but not created or updated.
278-
//
279-
// Reflects POST /api/dashboards/db API call.
280-
func (r *Client) SetRawDashboard(ctx context.Context, raw []byte) (StatusMessage, error) {
314+
//SetRawDashboardWithParam sends the serialized along with request parameters
315+
func (r *Client) SetRawDashboardWithParam(ctx context.Context, request RawBoardRequest) (StatusMessage, error) {
281316
var (
282317
rawResp []byte
283318
resp StatusMessage
284319
code int
285320
err error
286-
buf bytes.Buffer
287-
plain = make(map[string]interface{})
288321
)
289-
if err = json.Unmarshal(raw, &plain); err != nil {
290-
return StatusMessage{}, err
322+
raw, err := json.Marshal(request)
323+
324+
if err != nil {
325+
return StatusMessage{}, errors.New(err.Error())
291326
}
292-
// TODO(axel) fragile place, refactor it
293-
plain["id"] = 0
294-
raw, _ = json.Marshal(plain)
295-
buf.WriteString(`{"dashboard":`)
296-
buf.Write(raw)
297-
buf.WriteString(`, "overwrite": true}`)
298-
if rawResp, code, err = r.post(ctx, "api/dashboards/db", nil, buf.Bytes()); err != nil {
327+
if rawResp, code, err = r.post(ctx, "api/dashboards/db", nil, raw); err != nil {
299328
return StatusMessage{}, err
300329
}
301330
if err = json.Unmarshal(rawResp, &resp); err != nil {
@@ -307,6 +336,24 @@ func (r *Client) SetRawDashboard(ctx context.Context, raw []byte) (StatusMessage
307336
return resp, nil
308337
}
309338

339+
// SetRawDashboard updates existing dashboard or creates a new one.
340+
// Contrary to SetDashboard() it accepts raw JSON instead of Board structure.
341+
// Grafana only can create or update a dashboard in a database. File dashboards
342+
// may be only loaded with HTTP API but not created or updated.
343+
//
344+
// Reflects POST /api/dashboards/db API call.
345+
func (r *Client) SetRawDashboard(ctx context.Context, raw []byte) (StatusMessage, error) {
346+
defaultParams := SetDashboardParams{
347+
Overwrite: true,
348+
FolderID: DefaultFolderId,
349+
}
350+
request := RawBoardRequest{
351+
Parameters: defaultParams,
352+
Dashboard: raw,
353+
}
354+
return r.SetRawDashboardWithParam(ctx, request)
355+
}
356+
310357
// DeleteDashboard deletes dashboard that selected by slug string.
311358
// Grafana only can delete a dashboard in a database. File dashboards
312359
// may be only loaded with HTTP API but not deteled.

rest-dashboard_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sdk_test
22

33
import (
44
"context"
5+
"encoding/json"
56
"net/http"
67
"net/http/httptest"
78
"net/url"
@@ -10,6 +11,7 @@ import (
1011
"testing"
1112

1213
"github.com/grafana-tools/sdk"
14+
"github.com/stretchr/testify/assert"
1315
)
1416

1517
func TestClient_SearchDashboards(t *testing.T) {
@@ -283,3 +285,58 @@ func testUIntSearchParam(t *testing.T, sp func(uint) sdk.SearchParam, key string
283285
}
284286
}
285287
}
288+
289+
func Test_DecodeRawBoardPreserveId(t *testing.T) {
290+
r := getRawBoardRequest(t, true)
291+
//Serialize object
292+
data, err := json.Marshal(r)
293+
assert.Nil(t, err)
294+
//Deserialize data
295+
var rawData map[string]interface{} = make(map[string]interface{})
296+
assert.Nil(t, json.Unmarshal(data, &rawData))
297+
folderId := rawData["FolderID"]
298+
299+
assert.Equal(t, folderId.(float64), float64(27))
300+
assert.Equal(t, rawData["Overwrite"].(bool), true)
301+
rawData = rawData["dashboard"].(map[string]interface{})
302+
assert.Equal(t, rawData["id"].(float64), float64(25))
303+
assert.Equal(t, rawData["title"].(string), "woot")
304+
305+
}
306+
307+
func Test_DecodeRawBoardWipeId(t *testing.T) {
308+
r := getRawBoardRequest(t, false)
309+
//Serialize object
310+
data, err := json.Marshal(r)
311+
assert.Nil(t, err)
312+
//Deserialize data
313+
var rawData map[string]interface{} = make(map[string]interface{})
314+
err = json.Unmarshal(data, &rawData)
315+
assert.Nil(t, err)
316+
folderId := rawData["FolderID"]
317+
318+
assert.Equal(t, folderId.(float64), float64(27))
319+
assert.Equal(t, rawData["Overwrite"].(bool), true)
320+
rawData = rawData["dashboard"].(map[string]interface{})
321+
assert.Equal(t, rawData["id"].(float64), float64(0))
322+
assert.Equal(t, rawData["title"].(string), "woot")
323+
}
324+
325+
func getRawBoardRequest(t *testing.T, preserveId bool) sdk.RawBoardRequest {
326+
r := sdk.RawBoardRequest{}
327+
board := sdk.Board{}
328+
board.ID = 25
329+
board.Title = "woot"
330+
rawBoard, err := json.Marshal(board)
331+
assert.Nil(t, err)
332+
333+
params := sdk.SetDashboardParams{
334+
FolderID: 27,
335+
Overwrite: true,
336+
PreserveId: preserveId,
337+
}
338+
r.Dashboard = rawBoard
339+
r.Parameters = params
340+
341+
return r
342+
}

0 commit comments

Comments
 (0)