-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabricks.go
More file actions
93 lines (87 loc) · 2.79 KB
/
Copy pathdatabricks.go
File metadata and controls
93 lines (87 loc) · 2.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Package datadatabricks is a togo data backend that queries Databricks SQL via
// the Statement Execution API. Select with `togo provider:use data databricks`.
//
// DATABRICKS_HOST workspace URL
// DATABRICKS_TOKEN PAT (secret)
// DATABRICKS_WAREHOUSE_ID SQL warehouse id
package datadatabricks
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/togo-framework/data"
"github.com/togo-framework/providers"
"github.com/togo-framework/togo"
)
func init() {
togo.RegisterProviderFunc("data-databricks", togo.PriorityService+1, func(k *togo.Kernel) error {
providers.Use(k, providers.CapData, "databricks", newDBX(k), false)
if k.Log != nil {
k.Log.Info("plugin active", "plugin", "data-databricks")
}
return nil
})
}
type dbx struct {
host string
token string
warehouse string
hc *http.Client
}
func newDBX(k *togo.Kernel) *dbx {
return &dbx{
host: strings.TrimRight(providers.Value(k, providers.CapData, "databricks", "host", "", false), "/"),
token: providers.Value(k, providers.CapData, "databricks", "token", "", true),
warehouse: providers.Value(k, providers.CapData, "databricks", "warehouse_id", "", false),
hc: &http.Client{Timeout: 60 * time.Second},
}
}
func (d *dbx) Query(ctx context.Context, query string, _ ...any) ([]data.Row, error) {
if d.host == "" || d.token == "" || d.warehouse == "" {
return nil, fmt.Errorf("data-databricks: set DATABRICKS_HOST, DATABRICKS_TOKEN, DATABRICKS_WAREHOUSE_ID")
}
body, _ := json.Marshal(map[string]any{"statement": query, "warehouse_id": d.warehouse, "wait_timeout": "30s"})
req, _ := http.NewRequestWithContext(ctx, "POST", d.host+"/api/2.0/sql/statements", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+d.token)
req.Header.Set("Content-Type", "application/json")
resp, err := d.hc.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var out struct {
Status struct{ State string `json:"state"` } `json:"status"`
Manifest struct {
Schema struct {
Columns []struct {
Name string `json:"name"`
} `json:"columns"`
} `json:"schema"`
} `json:"manifest"`
Result struct {
DataArray [][]any `json:"data_array"`
} `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
if resp.StatusCode >= 300 || (out.Status.State != "" && out.Status.State != "SUCCEEDED") {
return nil, fmt.Errorf("databricks: %s (%s)", resp.Status, out.Status.State)
}
cols := out.Manifest.Schema.Columns
rows := make([]data.Row, 0, len(out.Result.DataArray))
for _, r := range out.Result.DataArray {
row := make(data.Row, len(cols))
for i, c := range cols {
if i < len(r) {
row[c.Name] = r[i]
}
}
rows = append(rows, row)
}
return rows, nil
}