Skip to content

Commit 9d6d185

Browse files
authored
Add query_plan attribute to visualization (#1752)
Fixes #1751.
1 parent d10b859 commit 9d6d185

File tree

5 files changed

+146
-15
lines changed

5 files changed

+146
-15
lines changed

sql/acceptance/sql_dashboard_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ func TestAccDashboard(t *testing.T) {
5858
5959
options = jsonencode({})
6060
}
61+
62+
resource "databricks_sql_visualization" "q1v2" {
63+
query_id = databricks_sql_query.q1.id
64+
type = "table"
65+
name = "My Table (1)"
66+
67+
options = jsonencode({})
68+
69+
# Note: this resource differs from the one above in that
70+
# the query plan is set. This tests that it can either
71+
# be unset or set and in both cases yield a consistent result.
72+
query_plan = jsonencode({
73+
# The value should be non-empty to check we don't have
74+
# plan changes due to whitespace changes in JSON serialization.
75+
groups = [
76+
]
77+
})
78+
}
6179
`,
6280
},
6381
})

sql/api/visualization.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ type Visualization struct {
1313
Name string `json:"name"`
1414
Description string `json:"description,omitempty"`
1515
Options json.RawMessage `json:"options,omitempty"`
16+
QueryPlan json.RawMessage `json:"query_plan,omitempty"`
1617
}

sql/api/visualization_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ func TestVisualizationMarshalUnmarshal(t *testing.T) {
1515
Name: "name",
1616
Description: "description",
1717
Options: json.RawMessage("{}"),
18+
QueryPlan: json.RawMessage(`{"foo":"bar"}`),
1819
}
1920

2021
out, err := json.Marshal(v)

sql/resource_sql_visualization.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type VisualizationEntity struct {
2323
Name string `json:"name"`
2424
Description string `json:"description,omitempty"`
2525
Options string `json:"options"`
26+
QueryPlan string `json:"query_plan,omitempty"`
2627
}
2728

2829
func (v *VisualizationEntity) toAPIObject(schema map[string]*schema.Schema, data *schema.ResourceData) (*api.Visualization, error) {
@@ -37,6 +38,9 @@ func (v *VisualizationEntity) toAPIObject(schema map[string]*schema.Schema, data
3738
av.Name = v.Name
3839
av.Description = v.Description
3940
av.Options = json.RawMessage(v.Options)
41+
if v.QueryPlan != "" {
42+
av.QueryPlan = json.RawMessage(v.QueryPlan)
43+
}
4044
return &av, nil
4145
}
4246

@@ -49,6 +53,11 @@ func (v *VisualizationEntity) fromAPIObject(av *api.Visualization, schema map[st
4953
v.Description = av.Description
5054
v.Options = string(av.Options)
5155

56+
// If the query plan attribute is not set; it may come back as `null` from the API.
57+
if av.QueryPlan != nil && !bytes.Equal(av.QueryPlan, []byte("null")) {
58+
v.QueryPlan = string(av.QueryPlan)
59+
}
60+
5261
// Transform to ResourceData.
5362
return common.StructToData(*v, schema, data)
5463
}
@@ -125,26 +134,28 @@ func jsonRemarshal(in []byte) ([]byte, error) {
125134
return out, nil
126135
}
127136

137+
func suppressWhitespaceChangesInJSON(_, old, new string, d *schema.ResourceData) bool {
138+
oldp, err := jsonRemarshal([]byte(old))
139+
if err != nil {
140+
log.Printf("[WARN] Unable to remarshal value %#v", old)
141+
return false
142+
}
143+
newp, err := jsonRemarshal([]byte(new))
144+
if err != nil {
145+
log.Printf("[WARN] Unable to remarshal value %#v", new)
146+
return false
147+
}
148+
return bytes.Equal(oldp, newp)
149+
}
150+
128151
func ResourceSqlVisualization() *schema.Resource {
129152
p := common.NewPairSeparatedID("query_id", "visualization_id", "/")
130153
s := common.StructToSchema(
131154
VisualizationEntity{},
132155
func(m map[string]*schema.Schema) map[string]*schema.Schema {
133-
// We care only about logical changes to the JSON payload in `options`.
134-
m["options"].DiffSuppressFunc = func(_, old, new string, d *schema.ResourceData) bool {
135-
oldp, err := jsonRemarshal([]byte(old))
136-
if err != nil {
137-
log.Printf("[WARN] Unable to remarshal value %#v", old)
138-
return false
139-
}
140-
newp, err := jsonRemarshal([]byte(new))
141-
if err != nil {
142-
log.Printf("[WARN] Unable to remarshal value %#v", new)
143-
return false
144-
}
145-
return bytes.Equal(oldp, newp)
146-
}
147-
156+
// We care only about logical changes to the JSON payload in `options` and `query_plan`.
157+
m["options"].DiffSuppressFunc = suppressWhitespaceChangesInJSON
158+
m["query_plan"].DiffSuppressFunc = suppressWhitespaceChangesInJSON
148159
return m
149160
})
150161

sql/resource_sql_visualization_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,106 @@ func TestVisualizationRead(t *testing.T) {
137137
assert.Less(t, 0, len(d.Get("options").(string)))
138138
}
139139

140+
func TestVisualizationReadWithQueryPlan(t *testing.T) {
141+
d, err := qa.ResourceFixture{
142+
Fixtures: []qa.HTTPFixture{
143+
{
144+
Method: "GET",
145+
Resource: "/api/2.0/preview/sql/queries/foo",
146+
Response: api.Query{
147+
ID: "foo",
148+
Visualizations: []json.RawMessage{
149+
json.RawMessage(`
150+
{
151+
"id": 12344
152+
}
153+
`),
154+
json.RawMessage(`
155+
{
156+
"id": 12345,
157+
"type": "CHART",
158+
"name": "My Chart",
159+
"description": "Some Description",
160+
"options": {},
161+
"query_plan": {"foo":"qux"}
162+
}
163+
`),
164+
json.RawMessage(`
165+
{
166+
"id": 12345
167+
}
168+
`),
169+
},
170+
},
171+
},
172+
},
173+
Resource: ResourceSqlVisualization(),
174+
Read: true,
175+
ID: "foo/12345",
176+
State: map[string]any{
177+
"query_id": "foo",
178+
"type": "chart",
179+
"name": "My Chart",
180+
"description": "Some Description",
181+
"options": "{}",
182+
"query_plan": `{"foo":"bar"}`,
183+
},
184+
}.Apply(t)
185+
186+
assert.NoError(t, err)
187+
assert.Equal(t, `{"foo":"qux"}`, d.Get("query_plan").(string))
188+
}
189+
190+
func TestVisualizationReadWithNullQueryPlan(t *testing.T) {
191+
d, err := qa.ResourceFixture{
192+
Fixtures: []qa.HTTPFixture{
193+
{
194+
Method: "GET",
195+
Resource: "/api/2.0/preview/sql/queries/foo",
196+
Response: api.Query{
197+
ID: "foo",
198+
Visualizations: []json.RawMessage{
199+
json.RawMessage(`
200+
{
201+
"id": 12344
202+
}
203+
`),
204+
json.RawMessage(`
205+
{
206+
"id": 12345,
207+
"type": "CHART",
208+
"name": "My Chart",
209+
"description": "Some Description",
210+
"options": {},
211+
"query_plan": null
212+
}
213+
`),
214+
json.RawMessage(`
215+
{
216+
"id": 12345
217+
}
218+
`),
219+
},
220+
},
221+
},
222+
},
223+
Resource: ResourceSqlVisualization(),
224+
Read: true,
225+
New: true,
226+
ID: "foo/12345",
227+
State: map[string]any{
228+
"query_id": "foo",
229+
"type": "chart",
230+
"name": "My Chart",
231+
"description": "Some Description",
232+
"options": "{}",
233+
},
234+
}.Apply(t)
235+
236+
assert.NoError(t, err)
237+
assert.Equal(t, "", d.Get("query_plan").(string))
238+
}
239+
140240
func TestVisualizationReadNotFound(t *testing.T) {
141241
d, err := qa.ResourceFixture{
142242
Fixtures: []qa.HTTPFixture{

0 commit comments

Comments
 (0)