Skip to content

Commit e764b8c

Browse files
Merge pull request #52 from NeedleInAJayStack/feat/httpclient-improvements
httpClient inherits Grafana settings
2 parents 1cdc19f + a13b84c commit e764b8c

3 files changed

Lines changed: 46 additions & 60 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
if: steps.check-for-backend.outputs.has-backend == 'true'
5757
uses: actions/setup-go@v3
5858
with:
59-
go-version: '1.21'
59+
go-version: '1.26'
6060

6161
- name: Test backend
6262
if: steps.check-for-backend.outputs.has-backend == 'true'

.go-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.25.6
1+
1.26.3

pkg/plugin/datasource.go

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,18 @@ package plugin
22

33
import (
44
"context"
5-
"crypto/tls"
65
"encoding/json"
76
"fmt"
8-
"net/http"
97
"sort"
108
"strconv"
119
"strings"
12-
"sync"
1310
"time"
1411

1512
"github.com/NeedleInAJayStack/haystack"
1613
"github.com/NeedleInAJayStack/haystack/client"
1714
"github.com/NeedleInAJayStack/haystack/io"
1815
"github.com/grafana/grafana-plugin-sdk-go/backend"
16+
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
1917
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
2018
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
2119
"github.com/grafana/grafana-plugin-sdk-go/data"
@@ -38,24 +36,29 @@ func NewDatasource(ctx context.Context, settings backend.DataSourceInstanceSetti
3836

3937
// settings contains normal inputs in the .JSONData field in JSON byte form
4038
var options Options
41-
jsonErr := json.Unmarshal(settings.JSONData, &options)
42-
if jsonErr != nil {
43-
return nil, jsonErr
39+
err := json.Unmarshal(settings.JSONData, &options)
40+
if err != nil {
41+
return nil, fmt.Errorf("datasource options: %w", err)
4442
}
4543
url := options.Url
4644
username := options.Username
4745

4846
// settings contains secure inputs in .DecryptedSecureJSONData in a string:string map
4947
password := settings.DecryptedSecureJSONData["password"]
5048

51-
client := client.NewClientFromHTTP(url, username, password, &http.Client{
52-
Transport: &http.Transport{
53-
TLSClientConfig: &tls.Config{InsecureSkipVerify: options.SkipTlsVerify},
54-
},
55-
})
56-
openErr := client.Open()
57-
if openErr != nil {
58-
return nil, openErr
49+
httpClientOptions, err := settings.HTTPClientOptions(ctx)
50+
if err != nil {
51+
return nil, fmt.Errorf("http client options: %w", err)
52+
}
53+
httpClientOptions.TLS = &httpclient.TLSOptions{InsecureSkipVerify: options.SkipTlsVerify}
54+
httpClient, err := httpclient.New(httpClientOptions)
55+
if err != nil {
56+
return nil, fmt.Errorf("new http client: %w", err)
57+
}
58+
client := client.NewClientFromHTTP(url, username, password, httpClient)
59+
err = client.Open()
60+
if err != nil {
61+
return nil, fmt.Errorf("haystack client opening: %w", err)
5962
}
6063
datasource := Datasource{client: client}
6164
return &datasource, nil
@@ -117,10 +120,10 @@ func (datasource *Datasource) query(ctx context.Context, pCtx backend.PluginCont
117120
// Unmarshal the JSON into our queryModel.
118121
var model QueryModel
119122

120-
jsonErr := json.Unmarshal(query.JSON, &model)
121-
if jsonErr != nil {
122-
log.DefaultLogger.Error(jsonErr.Error())
123-
return backend.ErrDataResponse(backend.StatusBadRequest, fmt.Sprintf("json unmarshal failure: %v", jsonErr.Error()))
123+
err := json.Unmarshal(query.JSON, &model)
124+
if err != nil {
125+
log.DefaultLogger.Error(err.Error())
126+
return backend.ErrDataResponse(backend.StatusBadRequest, fmt.Sprintf("json unmarshal failure: %v", err.Error()))
124127
}
125128

126129
variables := map[string]string{
@@ -201,37 +204,24 @@ func (datasource *Datasource) query(ctx context.Context, pCtx backend.PluginCont
201204
return backend.ErrDataResponse(backend.StatusBadRequest, errMsg)
202205
}
203206

204-
// Function to read a single point and send it to a channel.
205-
readPoint := func(point haystack.Row, hisReadChannel chan haystack.Grid, wg *sync.WaitGroup) {
206-
hisRead, err := datasource.hisRead(point, query.TimeRange)
207-
if err != nil {
208-
log.DefaultLogger.Error(err.Error())
209-
}
210-
hisReadChannel <- hisRead // hisRead is empty under error condition
211-
wg.Done()
212-
}
213-
214-
// Start a goroutine to collect all the grids into a slice.
215-
hisReadChannel := make(chan haystack.Grid)
216-
combinedChannel := make(chan []haystack.Grid)
217-
go func() {
218-
grids := []haystack.Grid{}
219-
for grid := range hisReadChannel {
220-
grids = append(grids, grid)
221-
}
222-
combinedChannel <- grids
223-
}()
224-
225207
// Read all the points in parallel using goroutines.
226-
var wg sync.WaitGroup
227-
wg.Add(len(points))
208+
hisReadChannel := make(chan haystack.Grid)
228209
for _, point := range points {
229-
go readPoint(point, hisReadChannel, &wg)
210+
go func() {
211+
hisRead, err := datasource.hisRead(point, query.TimeRange)
212+
if err != nil {
213+
log.DefaultLogger.Error(err.Error())
214+
}
215+
hisReadChannel <- hisRead // hisRead is empty under error condition
216+
}()
217+
}
218+
219+
grids := []haystack.Grid{}
220+
for _ = range len(points) {
221+
grid := <-hisReadChannel
222+
grids = append(grids, grid)
230223
}
231-
wg.Wait()
232-
close(hisReadChannel)
233224

234-
grids := <-combinedChannel
235225
response := responseFromGrids(grids)
236226
// Make the display name on the "val" fields the names of the points.
237227
for _, frame := range response.Frames {
@@ -260,11 +250,7 @@ func (datasource *Datasource) query(ctx context.Context, pCtx backend.PluginCont
260250
func responseFromGrids(grids []haystack.Grid) backend.DataResponse {
261251
frames := data.Frames{}
262252
for _, grid := range grids {
263-
frame, frameErr := dataFrameFromGrid(grid)
264-
if frameErr != nil {
265-
log.DefaultLogger.Error(frameErr.Error())
266-
return backend.ErrDataResponse(backend.StatusBadRequest, fmt.Sprintf("Frame conversion failure: %v", frameErr.Error()))
267-
}
253+
frame := dataFrameFromGrid(grid)
268254
frames = append(frames, frame)
269255
}
270256

@@ -333,13 +319,13 @@ func (datasource *Datasource) hisRead(point haystack.Row, timeRange backend.Time
333319

334320
// Must convert input date range to the point's timezone.
335321
// See https://github.com/skyfoundry/haystack-java/blob/30380dbbe4b5d9be8eb3f400195b0cdcdcc67b95/src/main/java/org/projecthaystack/server/HServer.java#L328
336-
start, startErr := haystack.NewDateTimeFromGo(timeRange.From).ToTz(tz.String())
337-
if startErr != nil {
338-
return haystack.EmptyGrid(), startErr
322+
start, err := haystack.NewDateTimeFromGo(timeRange.From).ToTz(tz.String())
323+
if err != nil {
324+
return haystack.EmptyGrid(), fmt.Errorf("start time: %w", err)
339325
}
340-
end, endErr := haystack.NewDateTimeFromGo(timeRange.To).ToTz(tz.String())
341-
if endErr != nil {
342-
return haystack.EmptyGrid(), endErr
326+
end, err := haystack.NewDateTimeFromGo(timeRange.To).ToTz(tz.String())
327+
if err != nil {
328+
return haystack.EmptyGrid(), fmt.Errorf("end time: %w", err)
343329
}
344330

345331
return datasource.withRetry(
@@ -415,7 +401,7 @@ func (datasource *Datasource) withRetry(
415401
}
416402

417403
// dataFrameFromGrid converts a haystack grid to a Grafana data frame
418-
func dataFrameFromGrid(grid haystack.Grid) (*data.Frame, error) {
404+
func dataFrameFromGrid(grid haystack.Grid) *data.Frame {
419405
fields := []*data.Field{}
420406

421407
for _, col := range grid.Cols() {
@@ -527,7 +513,7 @@ func dataFrameFromGrid(grid haystack.Grid) (*data.Frame, error) {
527513
frame := data.NewFrame("response", fields...)
528514
frameName := disFromMeta(grid.Meta(), "")
529515
frame.Name = frameName
530-
return frame, nil
516+
return frame
531517
}
532518

533519
// disFromMeta returns the display name using metadata. It falls back to the provided string if no other name can be found

0 commit comments

Comments
 (0)