diff --git a/dashboards/resource_dashboard.go b/dashboards/resource_dashboard.go index b9bdc5e15e..531a6e5de4 100644 --- a/dashboards/resource_dashboard.go +++ b/dashboards/resource_dashboard.go @@ -2,6 +2,8 @@ package dashboards import ( "context" + "log" + "strings" "github.com/databricks/databricks-sdk-go/service/dashboards" "github.com/databricks/terraform-provider-databricks/common" @@ -75,6 +77,14 @@ func ResourceDashboard() common.Resource { d.Set("md5", md5Hash) newDashboardRequest.SerializedDashboard = content createdDashboard, err := w.Lakeview.Create(ctx, newDashboardRequest) + if err != nil && isParentDoesntExistError(err) { + log.Printf("[DEBUG] Parent folder '%s' doesn't exist, creating...", newDashboardRequest.ParentPath) + err = w.Workspace.MkdirsByPath(ctx, newDashboardRequest.ParentPath) + if err != nil { + return err + } + createdDashboard, err = w.Lakeview.Create(ctx, newDashboardRequest) + } if err != nil { return err } @@ -168,3 +178,8 @@ func ResourceDashboard() common.Resource { }, } } + +func isParentDoesntExistError(err error) bool { + errStr := err.Error() + return strings.HasPrefix(errStr, "Path (") && strings.HasSuffix(errStr, ") doesn't exist.") +} diff --git a/dashboards/resource_dashboard_test.go b/dashboards/resource_dashboard_test.go index 0e40037e8a..0b450fdd7d 100644 --- a/dashboards/resource_dashboard_test.go +++ b/dashboards/resource_dashboard_test.go @@ -1,6 +1,7 @@ package dashboards import ( + "fmt" "testing" "github.com/databricks/databricks-sdk-go/experimental/mocks" @@ -61,6 +62,64 @@ func TestDashboardCreate(t *testing.T) { }) } +func TestDashboardCreate_NoParent(t *testing.T) { + qa.ResourceFixture{ + MockWorkspaceClientFunc: func(w *mocks.MockWorkspaceClient) { + lv := w.GetMockLakeviewAPI().EXPECT() + lv.Create(mock.Anything, dashboards.CreateDashboardRequest{ + DisplayName: "Dashboard name", + WarehouseId: "abc", + ParentPath: "/path", + SerializedDashboard: "serialized_json", + }).Return(nil, fmt.Errorf("Path (/path) doesn't exist.")).Once() + w.GetMockWorkspaceAPI().EXPECT().MkdirsByPath(mock.Anything, "/path").Return(nil) + lv.Create(mock.Anything, dashboards.CreateDashboardRequest{ + DisplayName: "Dashboard name", + WarehouseId: "abc", + ParentPath: "/path", + SerializedDashboard: "serialized_json", + }).Return(&dashboards.Dashboard{ + DashboardId: "xyz", + DisplayName: "Dashboard name", + SerializedDashboard: "serialized_json_2", + WarehouseId: "abc", + UpdateTime: "2125678", + }, nil) + lv.Publish(mock.Anything, dashboards.PublishRequest{ + EmbedCredentials: true, + WarehouseId: "abc", + DashboardId: "xyz", + ForceSendFields: []string{"EmbedCredentials"}, + }).Return(&dashboards.PublishedDashboard{ + EmbedCredentials: true, + WarehouseId: "abc", + DisplayName: "Dashboard name", + RevisionCreateTime: "823828", + }, nil) + lv.Get(mock.Anything, dashboards.GetDashboardRequest{ + DashboardId: "xyz", + }).Return(&dashboards.Dashboard{ + DashboardId: "xyz", + DisplayName: "Dashboard name", + SerializedDashboard: "serialized_json_2", + WarehouseId: "abc", + UpdateTime: "2125678", + }, nil) + }, + Resource: ResourceDashboard(), + Create: true, + HCL: ` + display_name = "Dashboard name" + warehouse_id = "abc" + parent_path = "/path" + serialized_dashboard = "serialized_json" + `, + }.ApplyAndExpectData(t, map[string]any{ + "id": "xyz", + "display_name": "Dashboard name", + }) +} + func TestDashboardRead(t *testing.T) { qa.ResourceFixture{ MockWorkspaceClientFunc: func(w *mocks.MockWorkspaceClient) { diff --git a/docs/resources/dashboard.md b/docs/resources/dashboard.md index b18ce755d3..e1215bd515 100644 --- a/docs/resources/dashboard.md +++ b/docs/resources/dashboard.md @@ -49,7 +49,7 @@ The following arguments are supported: * `serialized_dashboard` - (Optional) The contents of the dashboard in serialized string form. Conflicts with `file_path`. * `file_path` - (Optional) The path to the dashboard JSON file. Conflicts with `serialized_dashboard`. * `embed_credentials` - (Optional) Whether to embed credentials in the dashboard. Default is `true`. -* `parent_path` - (Required) The workspace path of the folder containing the dashboard. Includes leading slash and no trailing slash. +* `parent_path` - (Required) The workspace path of the folder containing the dashboard. Includes leading slash and no trailing slash. If folder doesn't exist, it will be created. ## Attribute Reference