Skip to content

Commit fd3969f

Browse files
pieternnfx
authored andcommitted
Allow specifying visualization by its Terraform resource ID
1 parent a78915f commit fd3969f

File tree

4 files changed

+125
-1
lines changed

4 files changed

+125
-1
lines changed

sqlanalytics/api/widget.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,26 @@ type WidgetParameterMapping struct {
5252
// This title overrides the title given to this parameter by the query, if specified.
5353
Title string `json:"title,omitempty"`
5454
}
55+
56+
// UnmarshalJSON ...
57+
func (w *Widget) UnmarshalJSON(b []byte) error {
58+
type localWidget Widget
59+
err := json.Unmarshal(b, (*localWidget)(w))
60+
if err != nil {
61+
return err
62+
}
63+
64+
// If a visualization is configured, set the corresponding visualization ID.
65+
// The visualization ID is only used when creating or updating a widget
66+
// and not set when retrieving an existing widget with visualization.
67+
if w.Visualization != nil {
68+
var v Visualization
69+
err = json.Unmarshal(w.Visualization, &v)
70+
if err != nil {
71+
return err
72+
}
73+
w.VisualizationID = &v.ID
74+
}
75+
76+
return nil
77+
}

sqlanalytics/api/widget_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,27 @@ func TestWidgetMarshalUnmarshal(t *testing.T) {
4949

5050
assert.Equal(t, w, wp)
5151
}
52+
53+
func TestWidgetUnmarshalWithVisualization(t *testing.T) {
54+
w := Widget{
55+
Visualization: json.RawMessage(`
56+
{
57+
"id": 12345
58+
}
59+
`),
60+
}
61+
62+
out, err := json.Marshal(w)
63+
if err != nil {
64+
t.Fatal(err)
65+
}
66+
67+
var wp Widget
68+
if err := json.Unmarshal(out, &wp); err != nil {
69+
t.Fatal(err)
70+
}
71+
72+
if assert.NotNil(t, wp.VisualizationID) {
73+
assert.Equal(t, 12345, *wp.VisualizationID)
74+
}
75+
}

sqlanalytics/resource_widget.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"sort"
88
"strconv"
9+
"strings"
910

1011
"github.com/databrickslabs/terraform-provider-databricks/common"
1112
"github.com/databrickslabs/terraform-provider-databricks/sqlanalytics/api"
@@ -58,6 +59,14 @@ func (a sortWidgetParameter) Less(i, j int) bool {
5859
return a[i].Name < a[j].Name
5960
}
6061

62+
// Use second part of ID if it's a composite ID, or the verbatim value.
63+
// This allows setting the visualization ID as the backend visualization ID
64+
// or as the visualization's resource composite ID. Both will work.
65+
func extractVisualizationID(id string) string {
66+
parts := strings.SplitN(id, "/", 2)
67+
return parts[len(parts)-1]
68+
}
69+
6170
func (w *WidgetEntity) toAPIObject(schema map[string]*schema.Schema, data *schema.ResourceData) (*api.Widget, error) {
6271
var aw api.Widget
6372

@@ -70,7 +79,7 @@ func (w *WidgetEntity) toAPIObject(schema map[string]*schema.Schema, data *schem
7079

7180
// The visualization ID is a string for the Terraform resource and an integer in the API.
7281
if w.VisualizationID != "" {
73-
visualizationID, err := strconv.Atoi(w.VisualizationID)
82+
visualizationID, err := strconv.Atoi(extractVisualizationID(w.VisualizationID))
7483
if err != nil {
7584
return nil, err
7685
}
@@ -244,6 +253,14 @@ func ResourceWidget() *schema.Resource {
244253
WidgetEntity{},
245254
func(m map[string]*schema.Schema) map[string]*schema.Schema {
246255
m["text"].ConflictsWith = []string{"visualization_id"}
256+
257+
// Ignore the query ID part in composite visualization ID.
258+
// It is present in this field if users refer to a visualization by the native
259+
// Terraform resource ID (e.g. `databricks_sql_visualization.name.id`)
260+
m["visualization_id"].DiffSuppressFunc = func(_, old, new string, d *schema.ResourceData) bool {
261+
return extractVisualizationID(old) == extractVisualizationID(new)
262+
}
263+
247264
return m
248265
})
249266

sqlanalytics/resource_widget_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,66 @@ func TestWidgetCreateWithVisualization(t *testing.T) {
6969
assert.Equal(t, "678", d.Get("visualization_id"))
7070
}
7171

72+
func TestWidgetCreateWithVisualizationByResourceID(t *testing.T) {
73+
i678 := 678
74+
75+
d, err := qa.ResourceFixture{
76+
Fixtures: []qa.HTTPFixture{
77+
{
78+
Method: "POST",
79+
Resource: "/api/2.0/preview/sql/widgets",
80+
ExpectedRequest: api.Widget{
81+
DashboardID: "some-uuid",
82+
VisualizationID: &i678,
83+
},
84+
Response: api.Widget{
85+
ID: 12345,
86+
DashboardID: "some-uuid",
87+
},
88+
},
89+
{
90+
Method: "GET",
91+
Resource: "/api/2.0/preview/sql/dashboards/some-uuid",
92+
Response: api.Dashboard{
93+
ID: "some-uuid",
94+
Widgets: []json.RawMessage{
95+
json.RawMessage(`
96+
{
97+
"id": 12344
98+
}
99+
`),
100+
json.RawMessage(`
101+
{
102+
"id": 12345,
103+
"visualization": {
104+
"id": 678
105+
}
106+
}
107+
`),
108+
json.RawMessage(`
109+
{
110+
"id": 12346
111+
}
112+
`),
113+
},
114+
},
115+
},
116+
},
117+
Resource: ResourceWidget(),
118+
Create: true,
119+
HCL: `
120+
dashboard_id = "some-uuid"
121+
visualization_id = "doesnt-matter/678"
122+
`,
123+
}.Apply(t)
124+
125+
assert.NoError(t, err, err)
126+
assert.Equal(t, "some-uuid/12345", d.Id(), "Resource ID should not be empty")
127+
assert.Equal(t, "some-uuid", d.Get("dashboard_id"))
128+
assert.Equal(t, "12345", d.Get("widget_id"))
129+
assert.Equal(t, "678", d.Get("visualization_id"))
130+
}
131+
72132
func TestWidgetCreateWithText(t *testing.T) {
73133
sText := "widget text"
74134

0 commit comments

Comments
 (0)