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

Commit 1a99912

Browse files
Allow arbitrary json data for datasources (#107)
* Allow arbitrary json data for datasources This will allow us to close a bunch of issues in the provider Instead of supporting attributes one by one, users can just set a map The current JSONData objects will still be supported using the `Map()` function * Uppercase JSON * Clone the JSON Data maps * lint
1 parent f2dab71 commit 1a99912

File tree

3 files changed

+326
-335
lines changed

3 files changed

+326
-335
lines changed

datasource.go

Lines changed: 2 additions & 260 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7-
"regexp"
8-
"strconv"
97
)
108

11-
var headerNameRegex = regexp.MustCompile(`^httpHeaderName(\d+)$`)
12-
139
// DataSource represents a Grafana data source.
1410
type DataSource struct {
1511
ID int64 `json:"id,omitempty"`
@@ -35,262 +31,8 @@ type DataSource struct {
3531
// Deprecated: Use secureJsonData.basicAuthPassword instead.
3632
BasicAuthPassword string `json:"basicAuthPassword,omitempty"`
3733

38-
// Helper to read/write http headers
39-
HTTPHeaders map[string]string `json:"-"`
40-
41-
JSONData JSONData `json:"jsonData,omitempty"`
42-
SecureJSONData SecureJSONData `json:"secureJsonData,omitempty"`
43-
}
44-
45-
// Required to avoid recursion during (un)marshal
46-
type _DataSource DataSource
47-
48-
// Marshal DataSource
49-
func (ds *DataSource) MarshalJSON() ([]byte, error) {
50-
dataSource := _DataSource(*ds)
51-
for name, value := range ds.HTTPHeaders {
52-
dataSource.JSONData.httpHeaderNames = append(dataSource.JSONData.httpHeaderNames, name)
53-
dataSource.SecureJSONData.httpHeaderValues = append(dataSource.SecureJSONData.httpHeaderValues, value)
54-
}
55-
56-
// Sentry provider expects this value in the JSON data payload,
57-
// ignoring the url attribute. This hack allows passing the URL as
58-
// an attribute but then sends it in the payload.
59-
if ds.Type == "grafana-sentry-datasource" {
60-
dataSource.JSONData.URL = ds.URL
61-
}
62-
63-
return json.Marshal(dataSource)
64-
}
65-
66-
// Unmarshal DataSource
67-
func (ds *DataSource) UnmarshalJSON(b []byte) (err error) {
68-
dataSource := _DataSource(*ds)
69-
if err = json.Unmarshal(b, &dataSource); err == nil {
70-
*ds = DataSource(dataSource)
71-
}
72-
ds.HTTPHeaders = make(map[string]string)
73-
for _, value := range ds.JSONData.httpHeaderNames {
74-
ds.HTTPHeaders[value] = "true" // HTTP Headers are not returned by the API
75-
}
76-
return err
77-
}
78-
79-
type LokiDerivedField struct {
80-
Name string `json:"name"`
81-
MatcherRegex string `json:"matcherRegex"`
82-
URL string `json:"url"`
83-
DatasourceUID string `json:"datasourceUid,omitempty"`
84-
}
85-
86-
// JSONData is a representation of the datasource `jsonData` property
87-
type JSONData struct {
88-
// Used by all datasources
89-
TLSAuth bool `json:"tlsAuth,omitempty"`
90-
TLSAuthWithCACert bool `json:"tlsAuthWithCACert,omitempty"`
91-
TLSConfigurationMethod string `json:"tlsConfigurationMethod,omitempty"`
92-
TLSSkipVerify bool `json:"tlsSkipVerify,omitempty"`
93-
httpHeaderNames []string
94-
95-
// Used by Athena
96-
Catalog string `json:"catalog,omitempty"`
97-
Database string `json:"database,omitempty"`
98-
OutputLocation string `json:"outputLocation,omitempty"`
99-
Workgroup string `json:"workgroup,omitempty"`
100-
101-
// Used by Github
102-
GitHubURL string `json:"githubUrl,omitempty"`
103-
104-
// Used by Graphite
105-
GraphiteVersion string `json:"graphiteVersion,omitempty"`
106-
107-
// Used by Prometheus, Elasticsearch, InfluxDB, MySQL, PostgreSQL and MSSQL
108-
TimeInterval string `json:"timeInterval,omitempty"`
109-
110-
// Used by Elasticsearch
111-
// From Grafana 8.x esVersion is the semantic version of Elasticsearch.
112-
EsVersion string `json:"esVersion,omitempty"`
113-
TimeField string `json:"timeField,omitempty"`
114-
Interval string `json:"interval,omitempty"`
115-
LogMessageField string `json:"logMessageField,omitempty"`
116-
LogLevelField string `json:"logLevelField,omitempty"`
117-
MaxConcurrentShardRequests int64 `json:"maxConcurrentShardRequests,omitempty"`
118-
XpackEnabled bool `json:"xpack"`
119-
120-
// Used by Cloudwatch
121-
CustomMetricsNamespaces string `json:"customMetricsNamespaces,omitempty"`
122-
TracingDatasourceUID string `json:"tracingDatasourceUid,omitempty"`
123-
124-
// Used by Cloudwatch, Athena
125-
AuthType string `json:"authType,omitempty"`
126-
AssumeRoleArn string `json:"assumeRoleArn,omitempty"`
127-
DefaultRegion string `json:"defaultRegion,omitempty"`
128-
Endpoint string `json:"endpoint,omitempty"`
129-
ExternalID string `json:"externalId,omitempty"`
130-
Profile string `json:"profile,omitempty"`
131-
132-
// Used by Loki
133-
DerivedFields []LokiDerivedField `json:"derivedFields,omitempty"`
134-
MaxLines int `json:"maxLines,omitempty"`
135-
136-
// Used by OpenTSDB
137-
TsdbVersion int64 `json:"tsdbVersion,omitempty"`
138-
TsdbResolution int64 `json:"tsdbResolution,omitempty"`
139-
140-
// Used by MSSQL
141-
Encrypt string `json:"encrypt,omitempty"`
142-
143-
// Used by PostgreSQL
144-
Sslmode string `json:"sslmode,omitempty"`
145-
PostgresVersion int64 `json:"postgresVersion,omitempty"`
146-
Timescaledb bool `json:"timescaledb"`
147-
148-
// Used by MySQL, PostgreSQL and MSSQL
149-
MaxOpenConns int64 `json:"maxOpenConns,omitempty"`
150-
MaxIdleConns int64 `json:"maxIdleConns,omitempty"`
151-
ConnMaxLifetime int64 `json:"connMaxLifetime,omitempty"`
152-
153-
// Used by Prometheus
154-
HTTPMethod string `json:"httpMethod,omitempty"`
155-
QueryTimeout string `json:"queryTimeout,omitempty"`
156-
157-
// Used by Stackdriver
158-
AuthenticationType string `json:"authenticationType,omitempty"`
159-
ClientEmail string `json:"clientEmail,omitempty"`
160-
DefaultProject string `json:"defaultProject,omitempty"`
161-
TokenURI string `json:"tokenUri,omitempty"`
162-
163-
// Used by Prometheus and Elasticsearch
164-
SigV4AssumeRoleArn string `json:"sigV4AssumeRoleArn,omitempty"`
165-
SigV4Auth bool `json:"sigV4Auth"`
166-
SigV4AuthType string `json:"sigV4AuthType,omitempty"`
167-
SigV4ExternalID string `json:"sigV4ExternalID,omitempty"`
168-
SigV4Profile string `json:"sigV4Profile,omitempty"`
169-
SigV4Region string `json:"sigV4Region,omitempty"`
170-
171-
// Used by Prometheus and Loki
172-
ManageAlerts bool `json:"manageAlerts"`
173-
AlertmanagerUID string `json:"alertmanagerUid,omitempty"`
174-
175-
// Used by Alertmanager
176-
Implementation string `json:"implementation,omitempty"`
177-
178-
// Used by Sentry
179-
OrgSlug string `json:"orgSlug,omitempty"`
180-
URL string `json:"url,omitempty"` // Sentry is not using the datasource URL attribute
181-
182-
// Used by InfluxDB
183-
DefaultBucket string `json:"defaultBucket,omitempty"`
184-
Organization string `json:"organization,omitempty"`
185-
Version string `json:"version,omitempty"`
186-
187-
// Used by Azure Monitor
188-
ClientID string `json:"clientId,omitempty"`
189-
CloudName string `json:"cloudName,omitempty"`
190-
SubscriptionID string `json:"subscriptionId,omitempty"`
191-
TenantID string `json:"tenantId,omitempty"`
192-
}
193-
194-
// Required to avoid recursion during (un)marshal
195-
type _JSONData JSONData
196-
197-
// Marshal JSONData
198-
func (jd JSONData) MarshalJSON() ([]byte, error) {
199-
jsonData := _JSONData(jd)
200-
b, err := json.Marshal(jsonData)
201-
if err != nil {
202-
return nil, err
203-
}
204-
fields := make(map[string]interface{})
205-
if err = json.Unmarshal(b, &fields); err != nil {
206-
return nil, err
207-
}
208-
for index, name := range jd.httpHeaderNames {
209-
fields[fmt.Sprintf("httpHeaderName%d", index+1)] = name
210-
}
211-
return json.Marshal(fields)
212-
}
213-
214-
// Unmarshal JSONData
215-
func (jd *JSONData) UnmarshalJSON(b []byte) (err error) {
216-
jsonData := _JSONData(*jd)
217-
if err = json.Unmarshal(b, &jsonData); err == nil {
218-
*jd = JSONData(jsonData)
219-
}
220-
fields := make(map[string]interface{})
221-
if err = json.Unmarshal(b, &fields); err == nil {
222-
headerCount := 0
223-
for name := range fields {
224-
match := headerNameRegex.FindStringSubmatch(name)
225-
if len(match) > 0 {
226-
headerCount++
227-
}
228-
}
229-
230-
jd.httpHeaderNames = make([]string, headerCount)
231-
for name, value := range fields {
232-
match := headerNameRegex.FindStringSubmatch(name)
233-
if len(match) == 2 {
234-
index, err := strconv.ParseInt(match[1], 10, 64)
235-
if err != nil {
236-
return err
237-
}
238-
jd.httpHeaderNames[index-1] = value.(string)
239-
}
240-
}
241-
}
242-
return err
243-
}
244-
245-
// SecureJSONData is a representation of the datasource `secureJsonData` property
246-
type SecureJSONData struct {
247-
// Used by all datasources
248-
TLSCACert string `json:"tlsCACert,omitempty"`
249-
TLSClientCert string `json:"tlsClientCert,omitempty"`
250-
TLSClientKey string `json:"tlsClientKey,omitempty"`
251-
Password string `json:"password,omitempty"`
252-
BasicAuthPassword string `json:"basicAuthPassword,omitempty"`
253-
httpHeaderValues []string
254-
255-
// Used by Cloudwatch, Athena
256-
AccessKey string `json:"accessKey,omitempty"`
257-
SecretKey string `json:"secretKey,omitempty"`
258-
259-
// Used by Stackdriver
260-
PrivateKey string `json:"privateKey,omitempty"`
261-
262-
// Used by Prometheus and Elasticsearch
263-
SigV4AccessKey string `json:"sigV4AccessKey,omitempty"`
264-
SigV4SecretKey string `json:"sigV4SecretKey,omitempty"`
265-
266-
// Used by GitHub
267-
AccessToken string `json:"accessToken,omitempty"`
268-
269-
// Used by Sentry
270-
AuthToken string `json:"authToken,omitempty"`
271-
272-
// Used by Azure Monitor
273-
ClientSecret string `json:"clientSecret,omitempty"`
274-
}
275-
276-
// Required to avoid recursion during unmarshal
277-
type _SecureJSONData SecureJSONData
278-
279-
// Marshal SecureJSONData
280-
func (sjd SecureJSONData) MarshalJSON() ([]byte, error) {
281-
secureJSONData := _SecureJSONData(sjd)
282-
b, err := json.Marshal(secureJSONData)
283-
if err != nil {
284-
return nil, err
285-
}
286-
fields := make(map[string]interface{})
287-
if err = json.Unmarshal(b, &fields); err != nil {
288-
return nil, err
289-
}
290-
for index, value := range sjd.httpHeaderValues {
291-
fields[fmt.Sprintf("httpHeaderValue%d", index+1)] = value
292-
}
293-
return json.Marshal(fields)
34+
JSONData map[string]interface{} `json:"jsonData,omitempty"`
35+
SecureJSONData map[string]interface{} `json:"secureJsonData,omitempty"`
29436
}
29537

29638
// NewDataSource creates a new Grafana data source.

0 commit comments

Comments
 (0)