Skip to content

Commit 96f4f60

Browse files
pieternnfx
authored andcommitted
Resources for SQL Analytics queries and visualizations
New resources are: * `databricks_sql_query` * `databricks_sql_visualization`
1 parent fc595ac commit 96f4f60

File tree

8 files changed

+1395
-1
lines changed

8 files changed

+1395
-1
lines changed

provider/provider.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ func DatabricksProvider() *schema.Provider {
7474
"databricks_azure_blob_mount": storage.ResourceAzureBlobMount(),
7575
"databricks_dbfs_file": storage.ResourceDBFSFile(),
7676

77-
"databricks_sql_endpoint": sqlanalytics.ResourceSQLEndpoint(),
77+
"databricks_sql_endpoint": sqlanalytics.ResourceSQLEndpoint(),
78+
"databricks_sql_query": sqlanalytics.ResourceQuery(),
79+
"databricks_sql_visualization": sqlanalytics.ResourceVisualization(),
7880

7981
"databricks_global_init_script": workspace.ResourceGlobalInitScript(),
8082
"databricks_notebook": workspace.ResourceNotebook(),

sqlanalytics/api/query.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// Query ...
8+
type Query struct {
9+
ID string `json:"id,omitempty"`
10+
DataSourceID string `json:"data_source_id"`
11+
Name string `json:"name"`
12+
Description string `json:"description,omitempty"`
13+
Query string `json:"query"`
14+
Schedule *QuerySchedule `json:"schedule,omitempty"`
15+
Options *QueryOptions `json:"options,omitempty"`
16+
Tags []string `json:"tags,omitempty"`
17+
Visualizations []json.RawMessage `json:"visualizations,omitempty"`
18+
}
19+
20+
// QuerySchedule ...
21+
type QuerySchedule struct {
22+
Interval int `json:"interval"`
23+
}
24+
25+
// QueryOptions ...
26+
type QueryOptions struct {
27+
Parameters []interface{} `json:"-"`
28+
RawParameters []json.RawMessage `json:"parameters,omitempty"`
29+
}
30+
31+
// MarshalJSON ...
32+
func (o *QueryOptions) MarshalJSON() ([]byte, error) {
33+
if o.Parameters != nil {
34+
o.RawParameters = []json.RawMessage{}
35+
for _, p := range o.Parameters {
36+
b, err := json.Marshal(p)
37+
if err != nil {
38+
return nil, err
39+
}
40+
o.RawParameters = append(o.RawParameters, b)
41+
}
42+
}
43+
44+
type localQueryOptions QueryOptions
45+
return json.Marshal((*localQueryOptions)(o))
46+
}
47+
48+
// UnmarshalJSON ...
49+
func (o *QueryOptions) UnmarshalJSON(b []byte) error {
50+
type localQueryOptions QueryOptions
51+
err := json.Unmarshal(b, (*localQueryOptions)(o))
52+
if err != nil {
53+
return err
54+
}
55+
56+
o.Parameters = []interface{}{}
57+
for _, rp := range o.RawParameters {
58+
var qp QueryParameter
59+
60+
// Unmarshal into base parameter type to figure out the right type.
61+
err = json.Unmarshal(rp, &qp)
62+
if err != nil {
63+
return err
64+
}
65+
66+
// Acquire pointer to the correct parameter type.
67+
var i interface{}
68+
switch qp.Type {
69+
case "text":
70+
i = &QueryParameterText{}
71+
case "number":
72+
i = &QueryParameterNumber{}
73+
case "enum":
74+
i = &QueryParameterEnum{}
75+
case "query":
76+
i = &QueryParameterQuery{}
77+
default:
78+
panic("don't know what to do...")
79+
}
80+
81+
// Unmarshal into correct parameter type.
82+
err = json.Unmarshal(rp, &i)
83+
if err != nil {
84+
return err
85+
}
86+
87+
o.Parameters = append(o.Parameters, i)
88+
}
89+
90+
return nil
91+
}
92+
93+
// QueryParameter ...
94+
type QueryParameter struct {
95+
Name string `json:"name"`
96+
Title string `json:"title,omitempty"`
97+
Type string `json:"type"`
98+
Value string `json:"value"`
99+
}
100+
101+
// QueryParameterText ...
102+
type QueryParameterText struct {
103+
QueryParameter
104+
}
105+
106+
// MarshalJSON sets the type before marshaling.
107+
func (p QueryParameterText) MarshalJSON() ([]byte, error) {
108+
p.QueryParameter.Type = "text"
109+
type localQueryParameter QueryParameterText
110+
return json.Marshal((localQueryParameter)(p))
111+
}
112+
113+
// QueryParameterNumber ...
114+
type QueryParameterNumber struct {
115+
QueryParameter
116+
}
117+
118+
// MarshalJSON sets the type before marshaling.
119+
func (p QueryParameterNumber) MarshalJSON() ([]byte, error) {
120+
p.QueryParameter.Type = "number"
121+
type localQueryParameter QueryParameterNumber
122+
return json.Marshal((localQueryParameter)(p))
123+
}
124+
125+
// QueryParameterMultipleValuesOptions ...
126+
type QueryParameterMultipleValuesOptions struct {
127+
Prefix string `json:"prefix"`
128+
Suffix string `json:"suffix"`
129+
Separator string `json:"separator"`
130+
}
131+
132+
// QueryParameterEnum ...
133+
type QueryParameterEnum struct {
134+
QueryParameter
135+
Options string `json:"enumOptions"`
136+
Multi *QueryParameterMultipleValuesOptions `json:"multiValuesOptions,omitempty"`
137+
}
138+
139+
// MarshalJSON sets the type before marshaling.
140+
func (p QueryParameterEnum) MarshalJSON() ([]byte, error) {
141+
p.QueryParameter.Type = "enum"
142+
type localQueryParameter QueryParameterEnum
143+
return json.Marshal((localQueryParameter)(p))
144+
}
145+
146+
// QueryParameterQuery ...
147+
type QueryParameterQuery struct {
148+
QueryParameter
149+
QueryID string `json:"queryId"`
150+
Multi *QueryParameterMultipleValuesOptions `json:"multiValuesOptions,omitempty"`
151+
}
152+
153+
// MarshalJSON sets the type before marshaling.
154+
func (p QueryParameterQuery) MarshalJSON() ([]byte, error) {
155+
p.QueryParameter.Type = "query"
156+
type localQueryParameter QueryParameterQuery
157+
return json.Marshal((localQueryParameter)(p))
158+
}

sqlanalytics/api/visualization.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package api
2+
3+
import "encoding/json"
4+
5+
// Visualization ...
6+
type Visualization struct {
7+
ID int `json:"id,omitempty"`
8+
QueryID string `json:"query_id,omitempty"`
9+
Type string `json:"type"`
10+
Name string `json:"name"`
11+
Description string `json:"description,omitempty"`
12+
Options json.RawMessage `json:"options,omitempty"`
13+
}

sqlanalytics/api/wrapper.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/databrickslabs/terraform-provider-databricks/common"
9+
"github.com/pkg/errors"
10+
)
11+
12+
// Wrapper is an API wrapper for the SQL Analytics queries, visualizations, and dashboards API.
13+
type Wrapper struct {
14+
client *common.DatabricksClient
15+
context context.Context
16+
}
17+
18+
// NewWrapper creates and returns an API wrapper.
19+
func NewWrapper(ctx context.Context, m interface{}) Wrapper {
20+
return Wrapper{m.(*common.DatabricksClient), ctx}
21+
}
22+
23+
const sqlaBasePath string = "/preview/sql"
24+
25+
// CreateVisualization ...
26+
func (a Wrapper) CreateVisualization(v *Visualization) (*Visualization, error) {
27+
var vp Visualization
28+
err := a.client.Post(a.context, fmt.Sprintf("%s/visualizations", sqlaBasePath), v, &vp)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
// Set query ID on returned object.
34+
// It's not included in the POST response.
35+
vp.QueryID = v.QueryID
36+
37+
return &vp, err
38+
}
39+
40+
// ReadVisualization ...
41+
func (a Wrapper) ReadVisualization(v *Visualization) (*Visualization, error) {
42+
if v.QueryID == "" {
43+
return nil, errors.Errorf("Cannot read visualization without query ID")
44+
}
45+
46+
var q Query
47+
err := a.client.Get(a.context, fmt.Sprintf("%s/queries/%s", sqlaBasePath, v.QueryID), nil, &q)
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
// Look for matching visualization ID.
53+
for _, vp := range q.Visualizations {
54+
var vnew Visualization
55+
err = json.Unmarshal(vp, &vnew)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
if vnew.ID == v.ID {
61+
// Include query ID in returned object.
62+
// It's not part of the API response.
63+
vnew.QueryID = v.QueryID
64+
return &vnew, nil
65+
}
66+
}
67+
68+
return nil, errors.Errorf("Cannot find visualization %d attached to query %s", v.ID, v.QueryID)
69+
}
70+
71+
// UpdateVisualization ...
72+
func (a Wrapper) UpdateVisualization(v *Visualization) (*Visualization, error) {
73+
var vp Visualization
74+
err := a.client.Post(a.context, fmt.Sprintf("%s/visualizations/%d", sqlaBasePath, v.ID), v, &vp)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
// Set query ID on returned object.
80+
// It's not included in the POST response.
81+
vp.QueryID = v.QueryID
82+
83+
return &vp, nil
84+
}
85+
86+
// DeleteVisualization ...
87+
func (a Wrapper) DeleteVisualization(v *Visualization) error {
88+
return a.client.Delete(a.context, fmt.Sprintf("%s/visualizations/%d", sqlaBasePath, v.ID), nil)
89+
}
90+
91+
// CreateQuery ...
92+
func (a Wrapper) CreateQuery(q *Query) (*Query, error) {
93+
var qp Query
94+
err := a.client.Post(a.context, fmt.Sprintf("%s/queries", sqlaBasePath), q, &qp)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
return &qp, err
100+
}
101+
102+
// ReadQuery ...
103+
func (a Wrapper) ReadQuery(q *Query) (*Query, error) {
104+
var qp Query
105+
err := a.client.Get(a.context, fmt.Sprintf("%s/queries/%s", sqlaBasePath, q.ID), nil, &qp)
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
return &qp, nil
111+
}
112+
113+
// UpdateQuery ...
114+
func (a Wrapper) UpdateQuery(q *Query) (*Query, error) {
115+
var qp Query
116+
err := a.client.Post(a.context, fmt.Sprintf("%s/queries/%s", sqlaBasePath, q.ID), q, &qp)
117+
if err != nil {
118+
return nil, err
119+
}
120+
121+
return &qp, nil
122+
}
123+
124+
// DeleteQuery ...
125+
func (a Wrapper) DeleteQuery(q *Query) error {
126+
return a.client.Delete(a.context, fmt.Sprintf("%s/queries/%s", sqlaBasePath, q.ID), nil)
127+
}

0 commit comments

Comments
 (0)