diff --git a/bundle/config/mutator/configure_default_parent_path.go b/bundle/config/mutator/configure_default_parent_path.go new file mode 100644 index 000000000..691a637aa --- /dev/null +++ b/bundle/config/mutator/configure_default_parent_path.go @@ -0,0 +1,62 @@ +package mutator + +import ( + "context" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" +) + +type configureDefaultParentPath struct{} + +func ConfigureDefaultParentPath() bundle.Mutator { + return &configureDefaultParentPath{} +} + +func (m *configureDefaultParentPath) Name() string { + return "ConfigureDefaultParentPath" +} + +func (m *configureDefaultParentPath) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { + var diags diag.Diagnostics + + pattern := dyn.NewPattern( + dyn.Key("resources"), + dyn.Key("dashboards"), + dyn.AnyKey(), + ) + + // Default value for the parent path. + defaultValue := b.Config.Workspace.ResourcePath + + // Configure the default parent path for all dashboards. + err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { + return dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { + // Get the current parent path (if set). + f, err := dyn.Get(v, "parent_path") + switch { + case dyn.IsNoSuchKeyError(err): + // OK, we'll set the default value. + break + case dyn.IsCannotTraverseNilError(err): + // Cannot traverse the value, skip it. + return v, nil + default: + // Return the error. + return v, err + } + + // Configure the default value (if not set). + if !f.IsValid() { + f = dyn.V(defaultValue) + } + + // Set the parent path. + return dyn.Set(v, "parent_path", f) + }) + }) + + diags = diags.Extend(diag.FromErr(err)) + return diags +} diff --git a/bundle/config/mutator/configure_default_parent_path_test.go b/bundle/config/mutator/configure_default_parent_path_test.go new file mode 100644 index 000000000..a9571c850 --- /dev/null +++ b/bundle/config/mutator/configure_default_parent_path_test.go @@ -0,0 +1,56 @@ +package mutator_test + +import ( + "context" + "testing" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/internal/bundletest" + "github.com/databricks/cli/libs/dyn" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfigureDefaultParentPath(t *testing.T) { + b := &bundle.Bundle{ + Config: config.Root{ + Workspace: config.Workspace{ + ResourcePath: "/foo/bar", + }, + Resources: config.Resources{ + Dashboards: map[string]*resources.Dashboard{ + "d1": { + // Empty string is skipped. + // See below for how it is set. + ParentPath: "", + }, + "d2": { + // Non-empty string is skipped. + ParentPath: "already-set", + }, + "d3": { + // No parent path set. + }, + "d4": nil, + }, + }, + }, + } + + // We can't set an empty string in the typed configuration. + // Do it on the dyn.Value directly. + bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { + return dyn.Set(v, "resources.dashboards.d1.parent_path", dyn.V("")) + }) + + diags := bundle.Apply(context.Background(), b, mutator.ConfigureDefaultParentPath()) + require.NoError(t, diags.Error()) + + assert.Equal(t, "", b.Config.Resources.Dashboards["d1"].ParentPath) + assert.Equal(t, "already-set", b.Config.Resources.Dashboards["d2"].ParentPath) + assert.Equal(t, "/foo/bar", b.Config.Resources.Dashboards["d3"].ParentPath) + assert.Nil(t, b.Config.Resources.Dashboards["d4"]) +} diff --git a/bundle/internal/bundletest/mutate.go b/bundle/internal/bundletest/mutate.go new file mode 100644 index 000000000..c30870dd7 --- /dev/null +++ b/bundle/internal/bundletest/mutate.go @@ -0,0 +1,19 @@ +package bundletest + +import ( + "context" + "testing" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" + "github.com/stretchr/testify/require" +) + +func Mutate(t *testing.T, b *bundle.Bundle, f func(v dyn.Value) (dyn.Value, error)) { + bundle.ApplyFunc(context.Background(), b, func(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { + err := b.Config.Mutate(f) + require.NoError(t, err) + return nil + }) +} diff --git a/bundle/phases/initialize.go b/bundle/phases/initialize.go index 93ce61b25..206fe73c5 100644 --- a/bundle/phases/initialize.go +++ b/bundle/phases/initialize.go @@ -57,6 +57,7 @@ func Initialize() bundle.Mutator { ), mutator.SetRunAs(), mutator.OverrideCompute(), + mutator.ConfigureDefaultParentPath(), mutator.ProcessTargetMode(), mutator.ApplyPresets(), mutator.DefaultQueueing(),