Skip to content
This repository was archived by the owner on Jan 15, 2024. It is now read-only.

Commit e083a12

Browse files
Merge branch 'master' into folder_by_uid
2 parents 40dd1d3 + 7fcf1d4 commit e083a12

File tree

5 files changed

+150
-14
lines changed

5 files changed

+150
-14
lines changed

client.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,16 @@ func New(baseURL string, cfg Config) (*Client, error) {
6262
}
6363

6464
func (c *Client) request(method, requestPath string, query url.Values, body io.Reader, responseStruct interface{}) error {
65-
var resp *http.Response
66-
var err error
67-
var bodyContents []byte
65+
var (
66+
req *http.Request
67+
resp *http.Response
68+
err error
69+
bodyContents []byte
70+
)
6871

6972
// retry logic
7073
for n := 0; n <= c.config.NumRetries; n++ {
71-
r, err := c.newRequest(method, requestPath, query, body)
74+
req, err = c.newRequest(method, requestPath, query, body)
7275
if err != nil {
7376
return err
7477
}
@@ -78,7 +81,7 @@ func (c *Client) request(method, requestPath string, query url.Values, body io.R
7881
time.Sleep(time.Second * 5)
7982
}
8083

81-
resp, err = c.client.Do(r)
84+
resp, err = c.client.Do(req)
8285

8386
// If err is not nil, retry again
8487
// That's either caused by client policy, or failure to speak HTTP (such as network connectivity problem). A

client_test.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func TestNew_invalidURL(t *testing.T) {
5858

5959
expected := "parse \"://my-grafana.com\": missing protocol scheme"
6060
if err.Error() != expected {
61-
t.Errorf("expected error: %v; got: %s", expected, err.Error())
61+
t.Errorf("expected error: %v; got: %s", expected, err)
6262
}
6363
}
6464

@@ -68,7 +68,7 @@ func TestRequest_200(t *testing.T) {
6868

6969
err := client.request("GET", "/foo", url.Values{}, nil, nil)
7070
if err != nil {
71-
t.Errorf(err.Error())
71+
t.Error(err)
7272
}
7373
}
7474

@@ -78,7 +78,7 @@ func TestRequest_201(t *testing.T) {
7878

7979
err := client.request("GET", "/foo", url.Values{}, nil, nil)
8080
if err != nil {
81-
t.Errorf(err.Error())
81+
t.Error(err)
8282
}
8383
}
8484

@@ -89,7 +89,7 @@ func TestRequest_400(t *testing.T) {
8989
expected := `status: 400, body: {"foo":"bar"}`
9090
err := client.request("GET", "/foo", url.Values{}, nil, nil)
9191
if err.Error() != expected {
92-
t.Errorf("expected error: %v; got: %s", expected, err.Error())
92+
t.Errorf("expected error: %v; got: %s", expected, err)
9393
}
9494
}
9595

@@ -100,7 +100,23 @@ func TestRequest_500(t *testing.T) {
100100
expected := `status: 500, body: {"foo":"bar"}`
101101
err := client.request("GET", "/foo", url.Values{}, nil, nil)
102102
if err.Error() != expected {
103-
t.Errorf("expected error: %v; got: %s", expected, err.Error())
103+
t.Errorf("expected error: %v; got: %s", expected, err)
104+
}
105+
}
106+
107+
func TestRequest_badURL(t *testing.T) {
108+
server, client := gapiTestTools(t, 200, `{"foo":"bar"}`)
109+
baseURL, err := url.Parse("bad-url")
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
client.baseURL = *baseURL
114+
defer server.Close()
115+
116+
expected := `Get "bad-url/foo": unsupported protocol scheme ""`
117+
err = client.request("GET", "/foo", url.Values{}, nil, nil)
118+
if err.Error() != expected {
119+
t.Errorf("expected error: %v; got: %s", expected, err)
104120
}
105121
}
106122

@@ -113,7 +129,7 @@ func TestRequest_200Unmarshal(t *testing.T) {
113129
}{}
114130
err := client.request("GET", "/foo", url.Values{}, nil, &result)
115131
if err != nil {
116-
t.Fatal(err.Error())
132+
t.Fatal(err)
117133
}
118134

119135
if result.Foo != "bar" {
@@ -130,7 +146,7 @@ func TestRequest_200UnmarshalPut(t *testing.T) {
130146
}
131147
data, err := json.Marshal(u)
132148
if err != nil {
133-
t.Fatal(err.Error())
149+
t.Fatal(err)
134150
}
135151

136152
result := struct {
@@ -140,7 +156,7 @@ func TestRequest_200UnmarshalPut(t *testing.T) {
140156
q.Add("a", "b")
141157
err = client.request("PUT", "/foo", q, bytes.NewBuffer(data), &result)
142158
if err != nil {
143-
t.Errorf(err.Error())
159+
t.Error(err)
144160
}
145161

146162
if result.Name != "mike" {

dashboard.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ type Dashboard struct {
2828
Model map[string]interface{} `json:"dashboard"`
2929
Folder int64 `json:"folderId"`
3030
Overwrite bool `json:"overwrite"`
31+
32+
// This is only used when creating a new dashboard, it will always be empty when getting a dashboard.
33+
Message string `json:"message"`
3134
}
3235

3336
// SaveDashboard is a deprecated method for saving a Grafana dashboard. Use NewDashboard.

datasource.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7+
"regexp"
8+
"strconv"
79
)
810

11+
var headerNameRegex = regexp.MustCompile(`^httpHeaderName(\d+)$`)
12+
913
// DataSource represents a Grafana data source.
1014
type DataSource struct {
1115
ID int64 `json:"id,omitempty"`
16+
UID string `json:"uid,omitempty"`
1217
Name string `json:"name"`
1318
Type string `json:"type"`
1419
URL string `json:"url"`
@@ -27,16 +32,46 @@ type DataSource struct {
2732
// Deprecated: Use secureJsonData.basicAuthPassword instead.
2833
BasicAuthPassword string `json:"basicAuthPassword,omitempty"`
2934

35+
// Helper to read/write http headers
36+
HTTPHeaders map[string]string `json:"-"`
37+
3038
JSONData JSONData `json:"jsonData,omitempty"`
3139
SecureJSONData SecureJSONData `json:"secureJsonData,omitempty"`
3240
}
3341

42+
// Required to avoid recursion during (un)marshal
43+
type _DataSource DataSource
44+
45+
// Marshal DataSource
46+
func (ds *DataSource) MarshalJSON() ([]byte, error) {
47+
dataSource := _DataSource(*ds)
48+
for name, value := range ds.HTTPHeaders {
49+
dataSource.JSONData.httpHeaderNames = append(dataSource.JSONData.httpHeaderNames, name)
50+
dataSource.SecureJSONData.httpHeaderValues = append(dataSource.SecureJSONData.httpHeaderValues, value)
51+
}
52+
return json.Marshal(dataSource)
53+
}
54+
55+
// Unmarshal DataSource
56+
func (ds *DataSource) UnmarshalJSON(b []byte) (err error) {
57+
dataSource := _DataSource(*ds)
58+
if err = json.Unmarshal(b, &dataSource); err == nil {
59+
*ds = DataSource(dataSource)
60+
}
61+
ds.HTTPHeaders = make(map[string]string)
62+
for _, value := range ds.JSONData.httpHeaderNames {
63+
ds.HTTPHeaders[value] = "true" // HTTP Headers are not returned by the API
64+
}
65+
return err
66+
}
67+
3468
// JSONData is a representation of the datasource `jsonData` property
3569
type JSONData struct {
3670
// Used by all datasources
3771
TLSAuth bool `json:"tlsAuth,omitempty"`
3872
TLSAuthWithCACert bool `json:"tlsAuthWithCACert,omitempty"`
3973
TLSSkipVerify bool `json:"tlsSkipVerify,omitempty"`
74+
httpHeaderNames []string
4075

4176
// Used by Graphite
4277
GraphiteVersion string `json:"graphiteVersion,omitempty"`
@@ -94,6 +129,64 @@ type JSONData struct {
94129
SigV4ExternalID string `json:"sigV4ExternalID,omitempty"`
95130
SigV4Profile string `json:"sigV4Profile,omitempty"`
96131
SigV4Region string `json:"sigV4Region,omitempty"`
132+
133+
// Used by Prometheus and Loki
134+
ManageAlerts bool `json:"manageAlerts,omitempty"`
135+
AlertmanagerUID string `json:"alertmanagerUid,omitempty"`
136+
137+
// Used by Alertmanager
138+
Implementation string `json:"implementation,omitempty"`
139+
}
140+
141+
// Required to avoid recursion during (un)marshal
142+
type _JSONData JSONData
143+
144+
// Marshal JSONData
145+
func (jd JSONData) MarshalJSON() ([]byte, error) {
146+
jsonData := _JSONData(jd)
147+
b, err := json.Marshal(jsonData)
148+
if err != nil {
149+
return nil, err
150+
}
151+
fields := make(map[string]interface{})
152+
if err = json.Unmarshal(b, &fields); err != nil {
153+
return nil, err
154+
}
155+
for index, name := range jd.httpHeaderNames {
156+
fields[fmt.Sprintf("httpHeaderName%d", index+1)] = name
157+
}
158+
return json.Marshal(fields)
159+
}
160+
161+
// Unmarshal JSONData
162+
func (jd *JSONData) UnmarshalJSON(b []byte) (err error) {
163+
jsonData := _JSONData(*jd)
164+
if err = json.Unmarshal(b, &jsonData); err == nil {
165+
*jd = JSONData(jsonData)
166+
}
167+
fields := make(map[string]interface{})
168+
if err = json.Unmarshal(b, &fields); err == nil {
169+
headerCount := 0
170+
for name := range fields {
171+
match := headerNameRegex.FindStringSubmatch(name)
172+
if len(match) > 0 {
173+
headerCount++
174+
}
175+
}
176+
177+
jd.httpHeaderNames = make([]string, headerCount)
178+
for name, value := range fields {
179+
match := headerNameRegex.FindStringSubmatch(name)
180+
if len(match) == 2 {
181+
index, err := strconv.ParseInt(match[1], 10, 64)
182+
if err != nil {
183+
return err
184+
}
185+
jd.httpHeaderNames[index-1] = value.(string)
186+
}
187+
}
188+
}
189+
return err
97190
}
98191

99192
// SecureJSONData is a representation of the datasource `secureJsonData` property
@@ -104,6 +197,7 @@ type SecureJSONData struct {
104197
TLSClientKey string `json:"tlsClientKey,omitempty"`
105198
Password string `json:"password,omitempty"`
106199
BasicAuthPassword string `json:"basicAuthPassword,omitempty"`
200+
httpHeaderValues []string
107201

108202
// Used by Cloudwatch
109203
AccessKey string `json:"accessKey,omitempty"`
@@ -117,6 +211,26 @@ type SecureJSONData struct {
117211
SigV4SecretKey string `json:"sigV4SecretKey,omitempty"`
118212
}
119213

214+
// Required to avoid recursion during unmarshal
215+
type _SecureJSONData SecureJSONData
216+
217+
// Marshal SecureJSONData
218+
func (sjd SecureJSONData) MarshalJSON() ([]byte, error) {
219+
secureJSONData := _SecureJSONData(sjd)
220+
b, err := json.Marshal(secureJSONData)
221+
if err != nil {
222+
return nil, err
223+
}
224+
fields := make(map[string]interface{})
225+
if err = json.Unmarshal(b, &fields); err != nil {
226+
return nil, err
227+
}
228+
for index, value := range sjd.httpHeaderValues {
229+
fields[fmt.Sprintf("httpHeaderValue%d", index+1)] = value
230+
}
231+
return json.Marshal(fields)
232+
}
233+
120234
// NewDataSource creates a new Grafana data source.
121235
func (c *Client) NewDataSource(s *DataSource) (int64, error) {
122236
data, err := json.Marshal(s)

datasource_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
const (
10-
createdDataSourceJSON = `{"id":1,"message":"Datasource added", "name": "test_datasource"}`
10+
createdDataSourceJSON = `{"id":1,"uid":"myuid0001","message":"Datasource added", "name": "test_datasource"}`
1111
)
1212

1313
func TestNewDataSource(t *testing.T) {

0 commit comments

Comments
 (0)