mirror of https://github.com/databricks/cli.git
Compare commits
No commits in common. "a3e32c0adb0594b043d5a42987c270e3129f8bd8" and "866bfc5be7f9b87e562872c7473ac7e054ef5cd4" have entirely different histories.
a3e32c0adb
...
866bfc5be7
|
@ -214,7 +214,7 @@ func (m *applyPresets) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnos
|
||||||
|
|
||||||
// Dashboards: Prefix
|
// Dashboards: Prefix
|
||||||
for key, dashboard := range r.Dashboards {
|
for key, dashboard := range r.Dashboards {
|
||||||
if dashboard == nil || dashboard.CreateDashboardRequest == nil {
|
if dashboard == nil {
|
||||||
diags = diags.Extend(diag.Errorf("dashboard %s s is not defined", key))
|
diags = diags.Extend(diag.Errorf("dashboard %s s is not defined", key))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/databricks/cli/bundle/config/resources"
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
"github.com/databricks/cli/bundle/internal/bundletest"
|
"github.com/databricks/cli/bundle/internal/bundletest"
|
||||||
"github.com/databricks/cli/libs/dyn"
|
"github.com/databricks/cli/libs/dyn"
|
||||||
"github.com/databricks/databricks-sdk-go/service/dashboards"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -26,16 +25,12 @@ func TestConfigureDashboardDefaultsParentPath(t *testing.T) {
|
||||||
"d1": {
|
"d1": {
|
||||||
// Empty string is skipped.
|
// Empty string is skipped.
|
||||||
// See below for how it is set.
|
// See below for how it is set.
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
ParentPath: "",
|
ParentPath: "",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"d2": {
|
"d2": {
|
||||||
// Non-empty string is skipped.
|
// Non-empty string is skipped.
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
ParentPath: "already-set",
|
ParentPath: "already-set",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"d3": {
|
"d3": {
|
||||||
// No parent path set.
|
// No parent path set.
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/databricks/cli/bundle/config/resources"
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
"github.com/databricks/databricks-sdk-go/service/catalog"
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
"github.com/databricks/databricks-sdk-go/service/compute"
|
"github.com/databricks/databricks-sdk-go/service/compute"
|
||||||
"github.com/databricks/databricks-sdk-go/service/dashboards"
|
|
||||||
"github.com/databricks/databricks-sdk-go/service/jobs"
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
||||||
"github.com/databricks/databricks-sdk-go/service/ml"
|
"github.com/databricks/databricks-sdk-go/service/ml"
|
||||||
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
||||||
|
@ -89,13 +88,11 @@ func TestInitializeURLs(t *testing.T) {
|
||||||
Dashboards: map[string]*resources.Dashboard{
|
Dashboards: map[string]*resources.Dashboard{
|
||||||
"dashboard1": {
|
"dashboard1": {
|
||||||
ID: "01ef8d56871e1d50ae30ce7375e42478",
|
ID: "01ef8d56871e1d50ae30ce7375e42478",
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "My special dashboard",
|
DisplayName: "My special dashboard",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedURLs := map[string]string{
|
expectedURLs := map[string]string{
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
sdkconfig "github.com/databricks/databricks-sdk-go/config"
|
sdkconfig "github.com/databricks/databricks-sdk-go/config"
|
||||||
"github.com/databricks/databricks-sdk-go/service/catalog"
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
"github.com/databricks/databricks-sdk-go/service/compute"
|
"github.com/databricks/databricks-sdk-go/service/compute"
|
||||||
"github.com/databricks/databricks-sdk-go/service/dashboards"
|
|
||||||
"github.com/databricks/databricks-sdk-go/service/iam"
|
"github.com/databricks/databricks-sdk-go/service/iam"
|
||||||
"github.com/databricks/databricks-sdk-go/service/jobs"
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
||||||
"github.com/databricks/databricks-sdk-go/service/ml"
|
"github.com/databricks/databricks-sdk-go/service/ml"
|
||||||
|
@ -125,11 +124,7 @@ func mockBundle(mode config.Mode) *bundle.Bundle {
|
||||||
"cluster1": {ClusterSpec: &compute.ClusterSpec{ClusterName: "cluster1", SparkVersion: "13.2.x", NumWorkers: 1}},
|
"cluster1": {ClusterSpec: &compute.ClusterSpec{ClusterName: "cluster1", SparkVersion: "13.2.x", NumWorkers: 1}},
|
||||||
},
|
},
|
||||||
Dashboards: map[string]*resources.Dashboard{
|
Dashboards: map[string]*resources.Dashboard{
|
||||||
"dashboard1": {
|
"dashboard1": {DisplayName: "dashboard1"},
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "dashboard1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,18 +17,27 @@ type Dashboard struct {
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
URL string `json:"url,omitempty" bundle:"internal"`
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
|
|
||||||
*dashboards.CreateDashboardRequest
|
// ===========================
|
||||||
|
// === BEGIN OF API FIELDS ===
|
||||||
|
// ===========================
|
||||||
|
|
||||||
// =========================
|
// DisplayName is the display name of the dashboard (both as title and as basename in the workspace).
|
||||||
// === Additional fields ===
|
DisplayName string `json:"display_name"`
|
||||||
// =========================
|
|
||||||
|
// WarehouseID is the ID of the SQL Warehouse used to run the dashboard's queries.
|
||||||
|
WarehouseID string `json:"warehouse_id"`
|
||||||
|
|
||||||
// SerializedDashboard holds the contents of the dashboard in serialized JSON form.
|
// SerializedDashboard holds the contents of the dashboard in serialized JSON form.
|
||||||
// We override the field's type from the SDK struct here to allow for inlining as YAML.
|
// Note: its type is any and not string such that it can be inlined as YAML.
|
||||||
// If the value is a string, it is used as is.
|
// If it is not a string, its contents will be marshalled as JSON.
|
||||||
// If it is not a string, its contents is marshalled as JSON.
|
|
||||||
SerializedDashboard any `json:"serialized_dashboard,omitempty"`
|
SerializedDashboard any `json:"serialized_dashboard,omitempty"`
|
||||||
|
|
||||||
|
// ParentPath is the workspace path of the folder containing the dashboard.
|
||||||
|
// Includes leading slash and no trailing slash.
|
||||||
|
//
|
||||||
|
// Defaults to ${workspace.resource_path} if not set.
|
||||||
|
ParentPath string `json:"parent_path,omitempty"`
|
||||||
|
|
||||||
// EmbedCredentials is a flag to indicate if the publisher's credentials should
|
// EmbedCredentials is a flag to indicate if the publisher's credentials should
|
||||||
// be embedded in the published dashboard. These embedded credentials will be used
|
// be embedded in the published dashboard. These embedded credentials will be used
|
||||||
// to execute the published dashboard's queries.
|
// to execute the published dashboard's queries.
|
||||||
|
@ -36,6 +45,10 @@ type Dashboard struct {
|
||||||
// Defaults to false if not set.
|
// Defaults to false if not set.
|
||||||
EmbedCredentials bool `json:"embed_credentials,omitempty"`
|
EmbedCredentials bool `json:"embed_credentials,omitempty"`
|
||||||
|
|
||||||
|
// ===========================
|
||||||
|
// ==== END OF API FIELDS ====
|
||||||
|
// ===========================
|
||||||
|
|
||||||
// FilePath points to the local `.lvdash.json` file containing the dashboard definition.
|
// FilePath points to the local `.lvdash.json` file containing the dashboard definition.
|
||||||
FilePath string `json:"file_path,omitempty"`
|
FilePath string `json:"file_path,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ type dashboardState struct {
|
||||||
ETag string
|
ETag string
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectDashboardsFromState(ctx context.Context, b *bundle.Bundle) ([]dashboardState, error) {
|
func collectDashboards(ctx context.Context, b *bundle.Bundle) ([]dashboardState, error) {
|
||||||
state, err := ParseResourcesState(ctx, b)
|
state, err := ParseResourcesState(ctx, b)
|
||||||
if err != nil && state == nil {
|
if err != nil && state == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -28,12 +28,22 @@ func collectDashboardsFromState(ctx context.Context, b *bundle.Bundle) ([]dashbo
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, instance := range resource.Instances {
|
for _, instance := range resource.Instances {
|
||||||
|
id := instance.Attributes.ID
|
||||||
|
if id == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch resource.Type {
|
switch resource.Type {
|
||||||
case "databricks_dashboard":
|
case "databricks_dashboard":
|
||||||
|
etag := instance.Attributes.ETag
|
||||||
|
if etag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
dashboards = append(dashboards, dashboardState{
|
dashboards = append(dashboards, dashboardState{
|
||||||
Name: resource.Name,
|
Name: resource.Name,
|
||||||
ID: instance.Attributes.ID,
|
ID: id,
|
||||||
ETag: instance.Attributes.ETag,
|
ETag: etag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,14 +52,14 @@ func collectDashboardsFromState(ctx context.Context, b *bundle.Bundle) ([]dashbo
|
||||||
return dashboards, nil
|
return dashboards, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkDashboardsModifiedRemotely struct {
|
type checkModifiedDashboards struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *checkDashboardsModifiedRemotely) Name() string {
|
func (l *checkModifiedDashboards) Name() string {
|
||||||
return "CheckDashboardsModifiedRemotely"
|
return "CheckModifiedDashboards"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *checkDashboardsModifiedRemotely) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
func (l *checkModifiedDashboards) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
// This mutator is relevant only if the bundle includes dashboards.
|
// This mutator is relevant only if the bundle includes dashboards.
|
||||||
if len(b.Config.Resources.Dashboards) == 0 {
|
if len(b.Config.Resources.Dashboards) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -60,7 +70,7 @@ func (l *checkDashboardsModifiedRemotely) Apply(ctx context.Context, b *bundle.B
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboards, err := collectDashboardsFromState(ctx, b)
|
dashboards, err := collectDashboards(ctx, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return diag.FromErr(err)
|
return diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
@ -112,6 +122,6 @@ func (l *checkDashboardsModifiedRemotely) Apply(ctx context.Context, b *bundle.B
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckDashboardsModifiedRemotely() *checkDashboardsModifiedRemotely {
|
func CheckModifiedDashboards() *checkModifiedDashboards {
|
||||||
return &checkDashboardsModifiedRemotely{}
|
return &checkModifiedDashboards{}
|
||||||
}
|
}
|
|
@ -29,18 +29,16 @@ func mockDashboardBundle(t *testing.T) *bundle.Bundle {
|
||||||
Resources: config.Resources{
|
Resources: config.Resources{
|
||||||
Dashboards: map[string]*resources.Dashboard{
|
Dashboards: map[string]*resources.Dashboard{
|
||||||
"dash1": {
|
"dash1": {
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "My Special Dashboard",
|
DisplayName: "My Special Dashboard",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDashboardsModifiedRemotely_NoDashboards(t *testing.T) {
|
func TestCheckModifiedDashboards_NoDashboards(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
b := &bundle.Bundle{
|
b := &bundle.Bundle{
|
||||||
BundleRootPath: dir,
|
BundleRootPath: dir,
|
||||||
|
@ -52,17 +50,17 @@ func TestCheckDashboardsModifiedRemotely_NoDashboards(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
diags := bundle.Apply(context.Background(), b, CheckDashboardsModifiedRemotely())
|
diags := bundle.Apply(context.Background(), b, CheckModifiedDashboards())
|
||||||
assert.Empty(t, diags)
|
assert.Empty(t, diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDashboardsModifiedRemotely_FirstDeployment(t *testing.T) {
|
func TestCheckModifiedDashboards_FirstDeployment(t *testing.T) {
|
||||||
b := mockDashboardBundle(t)
|
b := mockDashboardBundle(t)
|
||||||
diags := bundle.Apply(context.Background(), b, CheckDashboardsModifiedRemotely())
|
diags := bundle.Apply(context.Background(), b, CheckModifiedDashboards())
|
||||||
assert.Empty(t, diags)
|
assert.Empty(t, diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDashboardsModifiedRemotely_ExistingStateNoChange(t *testing.T) {
|
func TestCheckModifiedDashboards_ExistingStateNoChange(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
b := mockDashboardBundle(t)
|
b := mockDashboardBundle(t)
|
||||||
|
@ -81,11 +79,11 @@ func TestCheckDashboardsModifiedRemotely_ExistingStateNoChange(t *testing.T) {
|
||||||
b.SetWorkpaceClient(m.WorkspaceClient)
|
b.SetWorkpaceClient(m.WorkspaceClient)
|
||||||
|
|
||||||
// No changes, so no diags.
|
// No changes, so no diags.
|
||||||
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely())
|
diags := bundle.Apply(ctx, b, CheckModifiedDashboards())
|
||||||
assert.Empty(t, diags)
|
assert.Empty(t, diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDashboardsModifiedRemotely_ExistingStateChange(t *testing.T) {
|
func TestCheckModifiedDashboards_ExistingStateChange(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
b := mockDashboardBundle(t)
|
b := mockDashboardBundle(t)
|
||||||
|
@ -104,14 +102,14 @@ func TestCheckDashboardsModifiedRemotely_ExistingStateChange(t *testing.T) {
|
||||||
b.SetWorkpaceClient(m.WorkspaceClient)
|
b.SetWorkpaceClient(m.WorkspaceClient)
|
||||||
|
|
||||||
// The dashboard has changed, so expect an error.
|
// The dashboard has changed, so expect an error.
|
||||||
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely())
|
diags := bundle.Apply(ctx, b, CheckModifiedDashboards())
|
||||||
if assert.Len(t, diags, 1) {
|
if assert.Len(t, diags, 1) {
|
||||||
assert.Equal(t, diag.Error, diags[0].Severity)
|
assert.Equal(t, diag.Error, diags[0].Severity)
|
||||||
assert.Equal(t, `dashboard "dash1" has been modified remotely`, diags[0].Summary)
|
assert.Equal(t, `dashboard "dash1" has been modified remotely`, diags[0].Summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckDashboardsModifiedRemotely_ExistingStateFailureToGet(t *testing.T) {
|
func TestCheckModifiedDashboards_ExistingStateFailureToGet(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
b := mockDashboardBundle(t)
|
b := mockDashboardBundle(t)
|
||||||
|
@ -127,7 +125,7 @@ func TestCheckDashboardsModifiedRemotely_ExistingStateFailureToGet(t *testing.T)
|
||||||
b.SetWorkpaceClient(m.WorkspaceClient)
|
b.SetWorkpaceClient(m.WorkspaceClient)
|
||||||
|
|
||||||
// Unable to get the dashboard, so expect an error.
|
// Unable to get the dashboard, so expect an error.
|
||||||
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely())
|
diags := bundle.Apply(ctx, b, CheckModifiedDashboards())
|
||||||
if assert.Len(t, diags, 1) {
|
if assert.Len(t, diags, 1) {
|
||||||
assert.Equal(t, diag.Error, diags[0].Severity)
|
assert.Equal(t, diag.Error, diags[0].Severity)
|
||||||
assert.Equal(t, `failed to get dashboard "dash1"`, diags[0].Summary)
|
assert.Equal(t, `failed to get dashboard "dash1"`, diags[0].Summary)
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/databricks/cli/libs/dyn/convert"
|
"github.com/databricks/cli/libs/dyn/convert"
|
||||||
"github.com/databricks/databricks-sdk-go/service/catalog"
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
"github.com/databricks/databricks-sdk-go/service/compute"
|
"github.com/databricks/databricks-sdk-go/service/compute"
|
||||||
"github.com/databricks/databricks-sdk-go/service/dashboards"
|
|
||||||
"github.com/databricks/databricks-sdk-go/service/jobs"
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
||||||
"github.com/databricks/databricks-sdk-go/service/ml"
|
"github.com/databricks/databricks-sdk-go/service/ml"
|
||||||
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
||||||
|
@ -792,12 +791,10 @@ func TestTerraformToBundleEmptyRemoteResources(t *testing.T) {
|
||||||
},
|
},
|
||||||
Dashboards: map[string]*resources.Dashboard{
|
Dashboards: map[string]*resources.Dashboard{
|
||||||
"test_dashboard": {
|
"test_dashboard": {
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "test_dashboard",
|
DisplayName: "test_dashboard",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
var tfState = resourcesState{
|
var tfState = resourcesState{
|
||||||
Resources: nil,
|
Resources: nil,
|
||||||
|
@ -951,17 +948,13 @@ func TestTerraformToBundleModifiedResources(t *testing.T) {
|
||||||
},
|
},
|
||||||
Dashboards: map[string]*resources.Dashboard{
|
Dashboards: map[string]*resources.Dashboard{
|
||||||
"test_dashboard": {
|
"test_dashboard": {
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "test_dashboard",
|
DisplayName: "test_dashboard",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
"test_dashboard_new": {
|
"test_dashboard_new": {
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "test_dashboard_new",
|
DisplayName: "test_dashboard_new",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
var tfState = resourcesState{
|
var tfState = resourcesState{
|
||||||
Resources: []stateResource{
|
Resources: []stateResource{
|
||||||
|
|
|
@ -2,7 +2,6 @@ package tfdyn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/databricks/cli/bundle/internal/tf/schema"
|
"github.com/databricks/cli/bundle/internal/tf/schema"
|
||||||
|
@ -11,34 +10,6 @@ import (
|
||||||
"github.com/databricks/cli/libs/log"
|
"github.com/databricks/cli/libs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
filePathFieldName = "file_path"
|
|
||||||
serializedDashboardFieldName = "serialized_dashboard"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Marshal "serialized_dashboard" as JSON if it is set in the input but not in the output.
|
|
||||||
func marshalSerializedDashboard(vin dyn.Value, vout dyn.Value) (dyn.Value, error) {
|
|
||||||
// Skip if the "serialized_dashboard" field is already set.
|
|
||||||
if v := vout.Get(serializedDashboardFieldName); v.IsValid() {
|
|
||||||
return vout, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if the "serialized_dashboard" field on the input is not set.
|
|
||||||
v := vin.Get(serializedDashboardFieldName)
|
|
||||||
if !v.IsValid() {
|
|
||||||
return vout, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal the "serialized_dashboard" field as JSON.
|
|
||||||
data, err := json.Marshal(v.AsAny())
|
|
||||||
if err != nil {
|
|
||||||
return dyn.InvalidValue, fmt.Errorf("failed to marshal serialized_dashboard: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the "serialized_dashboard" field on the output.
|
|
||||||
return dyn.Set(vout, serializedDashboardFieldName, dyn.V(string(data)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertDashboardResource(ctx context.Context, vin dyn.Value) (dyn.Value, error) {
|
func convertDashboardResource(ctx context.Context, vin dyn.Value) (dyn.Value, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -51,8 +22,8 @@ func convertDashboardResource(ctx context.Context, vin dyn.Value) (dyn.Value, er
|
||||||
// Include "serialized_dashboard" field if "file_path" is set.
|
// Include "serialized_dashboard" field if "file_path" is set.
|
||||||
// Note: the Terraform resource supports "file_path" natively, but its
|
// Note: the Terraform resource supports "file_path" natively, but its
|
||||||
// change detection mechanism doesn't work as expected at the time of writing (Sep 30).
|
// change detection mechanism doesn't work as expected at the time of writing (Sep 30).
|
||||||
if path, ok := vout.Get(filePathFieldName).AsString(); ok {
|
if path, ok := vout.Get("file_path").AsString(); ok {
|
||||||
vout, err = dyn.Set(vout, serializedDashboardFieldName, dyn.V(fmt.Sprintf("${file(\"%s\")}", path)))
|
vout, err = dyn.Set(vout, "serialized_dashboard", dyn.V(fmt.Sprintf("${file(\"%s\")}", path)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dyn.InvalidValue, fmt.Errorf("failed to set serialized_dashboard: %w", err)
|
return dyn.InvalidValue, fmt.Errorf("failed to set serialized_dashboard: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +33,7 @@ func convertDashboardResource(ctx context.Context, vin dyn.Value) (dyn.Value, er
|
||||||
case 0:
|
case 0:
|
||||||
return v, nil
|
return v, nil
|
||||||
case 1:
|
case 1:
|
||||||
if p[0] == dyn.Key(filePathFieldName) {
|
if p[0] == dyn.Key("file_path") {
|
||||||
return v, dyn.ErrDrop
|
return v, dyn.ErrDrop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,12 +46,6 @@ func convertDashboardResource(ctx context.Context, vin dyn.Value) (dyn.Value, er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal "serialized_dashboard" as JSON if it is set in the input but not in the output.
|
|
||||||
vout, err = marshalSerializedDashboard(vin, vout)
|
|
||||||
if err != nil {
|
|
||||||
return dyn.InvalidValue, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return vout, nil
|
return vout, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,15 @@ import (
|
||||||
"github.com/databricks/cli/bundle/internal/tf/schema"
|
"github.com/databricks/cli/bundle/internal/tf/schema"
|
||||||
"github.com/databricks/cli/libs/dyn"
|
"github.com/databricks/cli/libs/dyn"
|
||||||
"github.com/databricks/cli/libs/dyn/convert"
|
"github.com/databricks/cli/libs/dyn/convert"
|
||||||
"github.com/databricks/databricks-sdk-go/service/dashboards"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConvertDashboard(t *testing.T) {
|
func TestConvertDashboard(t *testing.T) {
|
||||||
var src = resources.Dashboard{
|
var src = resources.Dashboard{
|
||||||
CreateDashboardRequest: &dashboards.CreateDashboardRequest{
|
|
||||||
DisplayName: "my dashboard",
|
DisplayName: "my dashboard",
|
||||||
WarehouseId: "f00dcafe",
|
WarehouseID: "f00dcafe",
|
||||||
ParentPath: "/some/path",
|
ParentPath: "/some/path",
|
||||||
},
|
|
||||||
|
|
||||||
EmbedCredentials: true,
|
EmbedCredentials: true,
|
||||||
|
|
||||||
Permissions: []resources.Permission{
|
Permissions: []resources.Permission{
|
||||||
|
@ -82,48 +78,3 @@ func TestConvertDashboardFilePath(t *testing.T) {
|
||||||
"file_path": "some/path",
|
"file_path": "some/path",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertDashboardSerializedDashboardString(t *testing.T) {
|
|
||||||
var src = resources.Dashboard{
|
|
||||||
SerializedDashboard: `{ "json": true }`,
|
|
||||||
}
|
|
||||||
|
|
||||||
vin, err := convert.FromTyped(src, dyn.NilValue)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
out := schema.NewResources()
|
|
||||||
err = dashboardConverter{}.Convert(ctx, "my_dashboard", vin, out)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Assert that the "serialized_dashboard" is included.
|
|
||||||
assert.Subset(t, out.Dashboard["my_dashboard"], map[string]any{
|
|
||||||
"serialized_dashboard": `{ "json": true }`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertDashboardSerializedDashboardAny(t *testing.T) {
|
|
||||||
var src = resources.Dashboard{
|
|
||||||
SerializedDashboard: map[string]any{
|
|
||||||
"pages": []map[string]any{
|
|
||||||
{
|
|
||||||
"displayName": "New Page",
|
|
||||||
"layout": []map[string]any{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
vin, err := convert.FromTyped(src, dyn.NilValue)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
out := schema.NewResources()
|
|
||||||
err = dashboardConverter{}.Convert(ctx, "my_dashboard", vin, out)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Assert that the "serialized_dashboard" is included.
|
|
||||||
assert.Subset(t, out.Dashboard["my_dashboard"], map[string]any{
|
|
||||||
"serialized_dashboard": `{"pages":[{"displayName":"New Page","layout":[]}]}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ func Deploy(outputHandler sync.OutputHandler) bundle.Mutator {
|
||||||
bundle.Defer(
|
bundle.Defer(
|
||||||
bundle.Seq(
|
bundle.Seq(
|
||||||
terraform.StatePull(),
|
terraform.StatePull(),
|
||||||
terraform.CheckDashboardsModifiedRemotely(),
|
terraform.CheckModifiedDashboards(),
|
||||||
deploy.StatePull(),
|
deploy.StatePull(),
|
||||||
mutator.ValidateGitDetails(),
|
mutator.ValidateGitDetails(),
|
||||||
artifacts.CleanUp(),
|
artifacts.CleanUp(),
|
||||||
|
|
Loading…
Reference in New Issue