diff --git a/.agents/rules/auto-generated-files.md b/.agents/rules/auto-generated-files.md index d59f3d1470a..6cc0172e934 100644 --- a/.agents/rules/auto-generated-files.md +++ b/.agents/rules/auto-generated-files.md @@ -15,6 +15,9 @@ globs: - "internal/mocks/**/*.go" - "bundle/direct/dresources/configs/*.generated.yml" - "bundle/config/validate/*_walk.go" + - "bundle/config/walk/*_walk.go" + - "bundle/config/mutator/resourcemutator/defaults_walk.go" + - "bundle/config/resources_iterate.go" - "bundle/schema/jsonschema.json" - "python/databricks/bundles/version.py" - "python/databricks/bundles/*/__init__.py" @@ -34,6 +37,9 @@ paths: - "internal/mocks/**/*.go" - "bundle/direct/dresources/configs/*.generated.yml" - "bundle/config/validate/*_walk.go" + - "bundle/config/walk/*_walk.go" + - "bundle/config/mutator/resourcemutator/defaults_walk.go" + - "bundle/config/resources_iterate.go" - "bundle/schema/jsonschema.json" - "python/databricks/bundles/version.py" - "python/databricks/bundles/*/__init__.py" diff --git a/.agents/rules/style-guide-go.md b/.agents/rules/style-guide-go.md index ab2dad3b6e9..88c2131a6df 100644 --- a/.agents/rules/style-guide-go.md +++ b/.agents/rules/style-guide-go.md @@ -113,7 +113,7 @@ return fieldPaths ### Encoding -**RULE: When mutating an API response by round-tripping JSON, decode with `json.Decoder` + `UseNumber()`.** The naive `json.Marshal` → `map[string]any` → `Marshal` path corrupts any int64 larger than 2^53 (it degrades to a float64 mantissa — e.g. a real `spark_context_id`) and alphabetizes object keys. `libs/dyn/jsonloader` preserves key order but also lacks `UseNumber`, so it shares the int64 hazard. +**RULE: When mutating an API response by round-tripping JSON, decode with `json.Decoder` + `UseNumber()`.** The naive `json.Marshal` → `map[string]any` → `Marshal` path corrupts any int64 larger than 2^53 (it degrades to a float64 mantissa — e.g. a real `spark_context_id`) and alphabetizes object keys. **RULE: Be careful with `encoding/csv` `Writer.UseCRLF = true`.** It rewrites both record terminators AND embedded newlines inside quoted fields to `\r\n`, so tests for quoted multiline fields must expect `\r\n`, not just the line endings between rows. @@ -191,7 +191,7 @@ func NewService(deps ServiceDeps) *Service { ... } ### Configuration patterns -- Bundle config uses `dyn.Value` for dynamic typing +- Bundle config is loaded, included and target-merged as a `dyn.Value`, then converted once to typed structs (`bundle/config.Root`) at the end of the load phase; after that, mutators use the structs, and `libs/structs/structvar` carries `${...}` references and source locations - Config loading supports includes, variable interpolation, and target overrides - Schema generation is automated from Go struct tags diff --git a/.agents/skills/bump-sdk/SKILL.md b/.agents/skills/bump-sdk/SKILL.md index 14cd3b00044..af9d301200a 100644 --- a/.agents/skills/bump-sdk/SKILL.md +++ b/.agents/skills/bump-sdk/SKILL.md @@ -9,7 +9,7 @@ allowed-tools: Read, Edit, Write, Bash, Glob, Grep, WebFetch, AskUserQuestion The SDK version lives in `go.mod` (`github.com/databricks/databricks-sdk-go`) and the pinned spec SHA lives in `.codegen/_openapi_sha`. These two move as a pair; everything else in this skill is regenerated from them or is fallout you fix by hand. -Do not hand-edit generated files (`.codegen/cli.json`, `cmd/workspace/*`, `cmd/account/*`, `bundle/schema/jsonschema.json`, `bundle/config/validate/*_walk.go`, `bundle/direct/dresources/configs/*.generated.yml`, `python/databricks/bundles/**`); regenerate them. +Do not hand-edit generated files (`.codegen/cli.json`, `cmd/workspace/*`, `cmd/account/*`, `bundle/schema/jsonschema.json`, `bundle/config/validate/*_walk.go`, `bundle/config/walk/*_walk.go`, `bundle/direct/dresources/configs/*.generated.yml`, `python/databricks/bundles/**`); regenerate them. The Python tasks (`pydabs-*`, and the `pydabs-codegen` step inside `generate-check`) all run through `uv`. If one fails because `uv` is missing or because the host's `python3` is too old (e.g. 3.9), install `uv` (`curl -LsSf https://astral.sh/uv/install.sh | sh`) rather than touching the system Python: `uv run` provisions the interpreter each package pins (`>=3.10`, and `==3.13.*` under `python/codegen/`) and downloads it if needed. Do not chase the system Python version. The acceptance goldens are the exception: they need CI's `uv` index, see step 4. diff --git a/.github/workflows/python_push.yml b/.github/workflows/python_push.yml index 1a391719665..a2db548ddac 100644 --- a/.github/workflows/python_push.yml +++ b/.github/workflows/python_push.yml @@ -39,7 +39,7 @@ jobs: acceptance/bundle/python/ \ go.mod \ go.sum \ - libs/dyn/dynvar/testdata/reference_vectors.json \ + libs/structs/structvar/testdata/reference_vectors.json \ python/ \ tools/task/ \ ) diff --git a/.nextchanges/bundles/7098-presence.md b/.nextchanges/bundles/7098-presence.md new file mode 100644 index 00000000000..86765e9cffc --- /dev/null +++ b/.nextchanges/bundles/7098-presence.md @@ -0,0 +1 @@ +* `bundle validate` no longer treats a written zero value in a field that cannot record it (no `ForceSendFields`, e.g. `job_id: 0` in `run_job_task`) or an empty mapping (e.g. `presets: {}`) as set, and does not show the pipeline `development` deprecation warning for `development: true` under `mode: development`. ([#7098](https://github.com/databricks/cli/pull/7098)) diff --git a/.nextchanges/bundles/7098.md b/.nextchanges/bundles/7098.md new file mode 100644 index 00000000000..09e5f5ca992 --- /dev/null +++ b/.nextchanges/bundles/7098.md @@ -0,0 +1 @@ +* Speed up bundle commands on large bundles: configuration is kept as typed structs instead of being converted to and from a dynamic tree (`bundle validate` and `bundle plan` with 1000 jobs are about 10x faster and use about 3.5x less memory). ([#7098](https://github.com/databricks/cli/pull/7098)) diff --git a/AGENTS.md b/AGENTS.md index 02b63df8169..2ec41d4a3b7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,7 @@ This is the Databricks CLI, a command-line interface for interacting with Databr **RULE: Keep each PR focused on one change.** If you notice an unrelated cleanup, bug fix, or refactor while making your primary change, leave it alone or put it in a separate PR. Reviewers consistently ask to split mixed PRs, especially when a dependency bump or schema diff rides along with a feature change. -**RULE: Before adding a new helper, search the codebase for an existing one.** Common homes: `libs/` (shared utilities), `libs/databrickscfg/` (config), `libs/git/`, `libs/filer/`, `libs/cmdio/` (CLI I/O, spinners, prompts), `libs/env/` (env vars), `libs/testserver/`, `libs/structpath/` and `libs/dyn/` (path / dynamic values), `acceptance/bin/` (acceptance test helpers), `internal/mocks/` (generated mocks). A function that duplicates an existing name and signature in the same package is a compile error waiting to happen; grep before you name. +**RULE: Before adding a new helper, search the codebase for an existing one.** Common homes: `libs/` (shared utilities), `libs/databrickscfg/` (config), `libs/git/`, `libs/filer/`, `libs/cmdio/` (CLI I/O, spinners, prompts), `libs/env/` (env vars), `libs/testserver/`, `libs/structs/structpath/` and `libs/structs/structvar/` (paths / typed config values), `acceptance/bin/` (acceptance test helpers), `internal/mocks/` (generated mocks). A function that duplicates an existing name and signature in the same package is a compile error waiting to happen; grep before you name. # Development Commands @@ -73,7 +73,8 @@ GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git rebase or - `bundle/phases/` - High-level deployment phases **libs/** - Shared libraries and utilities -- `libs/dyn/` - Dynamic configuration value manipulation +- `libs/dyn/` - Dynamic configuration value tree: loads `databricks.yml` and its includes and merges targets, before the configuration is converted to typed structs +- `libs/structs/structvar/` - Typed configuration values with references and locations - `libs/filer/` - File system abstraction (local, DBFS, workspace) - `libs/auth/` - Databricks authentication handling - `libs/sync/` - File synchronization between local and remote diff --git a/Taskfile.yml b/Taskfile.yml index 403a5188604..5f0402543a7 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1021,8 +1021,11 @@ tasks: - "{{.EMBED_SOURCES}}" generates: - bundle/config/validate/*_walk.go + - bundle/config/walk/*_walk.go + - bundle/config/mutator/resourcemutator/defaults_walk.go + - bundle/config/resources_iterate.go cmds: - - "sh -c 'go run ./bundle/internal/validation/. && gofmt -w -s ./bundle/config/validate/required_walk.go ./bundle/config/validate/enum_walk.go'" + - "go run ./bundle/internal/validation/." generate-direct: desc: Generate direct engine config (apitypes + resources) diff --git a/acceptance/bundle/python/pipeline-development-deprecated/output.txt b/acceptance/bundle/python/pipeline-development-deprecated/output.txt index 0150edea922..d1beb802f92 100644 --- a/acceptance/bundle/python/pipeline-development-deprecated/output.txt +++ b/acceptance/bundle/python/pipeline-development-deprecated/output.txt @@ -1,22 +1,14 @@ >>> uv run [UV_ARGS] -q [CLI] bundle validate -Warning: The "development" property of pipelines is deprecated. Use "mode: development" in your databricks.yml instead. See https://docs.databricks.com/dev-tools/bundles/deployment-modes. - at resources.pipelines.with_development.development - in resources.py:7:1 - Name: my_project Target: dev Workspace: User: [USERNAME] Path: /Workspace/Users/[USERNAME]/.bundle/my_project/dev -Found 1 warning +Validation OK! >>> uv run [UV_ARGS] -q [CLI] bundle validate --output json -Warning: The "development" property of pipelines is deprecated. Use "mode: development" in your databricks.yml instead. See https://docs.databricks.com/dev-tools/bundles/deployment-modes. - at resources.pipelines.with_development.development - in resources.py:7:1 - { "with_development": true, "without_development": true diff --git a/acceptance/bundle/refschema/output/root.txt b/acceptance/bundle/refschema/output/root.txt index aa8a83ea6cd..c14a7235d39 100644 --- a/acceptance/bundle/refschema/output/root.txt +++ b/acceptance/bundle/refschema/output/root.txt @@ -1,4 +1,4 @@ -__locations *dynloc.Locations INPUT +__locations *loctable.Locations INPUT __locations.files []string INPUT __locations.files[*] string INPUT __locations.locations map[string][][]int INPUT diff --git a/acceptance/bundle/strict_profile_match/default_profile/host/output.txt b/acceptance/bundle/strict_profile_match/default_profile/host/output.txt index 2d194ce17e2..c2043c72c28 100644 --- a/acceptance/bundle/strict_profile_match/default_profile/host/output.txt +++ b/acceptance/bundle/strict_profile_match/default_profile/host/output.txt @@ -18,7 +18,7 @@ Error: cannot resolve bundle auth configuration: resolve: strict profile matchin Name: strict-profile-match Target: classic_other_workspace Workspace: - Host: [DATABRICKS_URL] + Host: [DATABRICKS_URL]/?o=2222222222 Found 1 error diff --git a/acceptance/bundle/strict_profile_match/host/output.txt b/acceptance/bundle/strict_profile_match/host/output.txt index 6c3dcbeefab..54e165fc1b4 100644 --- a/acceptance/bundle/strict_profile_match/host/output.txt +++ b/acceptance/bundle/strict_profile_match/host/output.txt @@ -22,7 +22,7 @@ Error: cannot resolve bundle auth configuration: resolve: strict profile matchin Name: strict-profile-match Target: classic_other_workspace Workspace: - Host: [DATABRICKS_URL] + Host: [DATABRICKS_URL]/?o=2222222222 Found 1 error @@ -48,7 +48,7 @@ Error: cannot resolve bundle auth configuration: resolve: strict profile matchin Name: strict-profile-match Target: custom_host_other_workspace Workspace: - Host: [CUSTOM_HOST] + Host: [CUSTOM_HOST]/?o=2222222222 Found 1 error diff --git a/acceptance/bundle/summary/modified_status/output.txt b/acceptance/bundle/summary/modified_status/output.txt index 59d922c8b85..4c197a9b4ef 100644 --- a/acceptance/bundle/summary/modified_status/output.txt +++ b/acceptance/bundle/summary/modified_status/output.txt @@ -200,4 +200,4 @@ All files and directories at the following location will be deleted: /Workspace/ Destroy: 4 deleted >>> [CLI] bundle summary -o json -{} +null diff --git a/acceptance/bundle/validate/app_resource_duplicate_name_location/output.txt b/acceptance/bundle/validate/app_resource_duplicate_name_location/output.txt index a8eac9a3ace..77dc6d4c97a 100644 --- a/acceptance/bundle/validate/app_resource_duplicate_name_location/output.txt +++ b/acceptance/bundle/validate/app_resource_duplicate_name_location/output.txt @@ -2,7 +2,7 @@ >>> [CLI] bundle validate Warning: invalid value "INVALID" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER] at resources.apps.app.resources[0].job.permission - in databricks.yml:13:25 + in databricks.yml:17:25 Name: test-bundle Target: default @@ -15,7 +15,7 @@ Found 1 warning >>> [CLI] bundle validate -o json Warning: invalid value "INVALID" for enum field. Valid values are [CAN_MANAGE CAN_MANAGE_RUN CAN_VIEW IS_OWNER] at resources.apps.app.resources[0].job.permission - in databricks.yml:13:25 + in databricks.yml:17:25 [ { diff --git a/acceptance/bundle/validate/app_resource_duplicate_name_location/script b/acceptance/bundle/validate/app_resource_duplicate_name_location/script index 3531c01dcef..0f67cd891c8 100644 --- a/acceptance/bundle/validate/app_resource_duplicate_name_location/script +++ b/acceptance/bundle/validate/app_resource_duplicate_name_location/script @@ -1,4 +1,4 @@ -# A diagnostic on a field of a duplicated app resource entry points at the first entry when the duplicate's value is the same. +# A diagnostic on a field of a duplicated app resource entry points at the entry that is kept (the later one). errcode trace $CLI bundle validate # The entries are merged by name, the later one wins. diff --git a/acceptance/bundle/validate/includes/merge_failure_short_circuits_diagnostics/script b/acceptance/bundle/validate/includes/merge_failure_short_circuits_diagnostics/script index 6f34e8010d2..16c1bf033ce 100644 --- a/acceptance/bundle/validate/includes/merge_failure_short_circuits_diagnostics/script +++ b/acceptance/bundle/validate/includes/merge_failure_short_circuits_diagnostics/script @@ -1,2 +1,2 @@ -# Includes after one that fails to merge do not report their diagnostics. +# Includes after one that fails to merge still report their diagnostics. errcode trace $CLI bundle validate diff --git a/acceptance/bundle/validate/nulls/resources_container/script b/acceptance/bundle/validate/nulls/resources_container/script index 253cc3d205d..afd083482ed 100644 --- a/acceptance/bundle/validate/nulls/resources_container/script +++ b/acceptance/bundle/validate/nulls/resources_container/script @@ -1,2 +1,2 @@ -# resources: null is rejected. +# resources: null is accepted. errcode trace $CLI bundle validate diff --git a/acceptance/bundle/validate/nulls/resources_container/test.toml b/acceptance/bundle/validate/nulls/resources_container/test.toml deleted file mode 100644 index 022e0f2c7e9..00000000000 --- a/acceptance/bundle/validate/nulls/resources_container/test.toml +++ /dev/null @@ -1 +0,0 @@ -Badness = 'An empty (null) resources section is an error without a location, while empty targets, variables or sync sections are accepted.' diff --git a/acceptance/bundle/validate/references/resolved_invalid_map_value/script b/acceptance/bundle/validate/references/resolved_invalid_map_value/script index 835edef52e9..cc299050ee2 100644 --- a/acceptance/bundle/validate/references/resolved_invalid_map_value/script +++ b/acceptance/bundle/validate/references/resolved_invalid_map_value/script @@ -1,2 +1,2 @@ -# A map value whose reference resolves to the wrong type is dropped, with a warning at its path. +# A map value whose reference resolves to the wrong type becomes an empty string, with a warning at its path. trace $CLI bundle validate -o json | jq .resources.jobs.j.tags diff --git a/acceptance/bundle/validate/references/resolved_invalid_sequence_item/script b/acceptance/bundle/validate/references/resolved_invalid_sequence_item/script index 61ba88b4d75..91e0e7c9cf0 100644 --- a/acceptance/bundle/validate/references/resolved_invalid_sequence_item/script +++ b/acceptance/bundle/validate/references/resolved_invalid_sequence_item/script @@ -1,2 +1,2 @@ -# A list element whose reference resolves to the wrong type is dropped, with a warning at its path. +# A list element whose reference resolves to the wrong type becomes an empty string, with a warning at its path. trace $CLI bundle validate -o json | jq .resources.jobs.j.email_notifications diff --git a/acceptance/bundle/validate/references/resolved_invalid_sequence_items/databricks.yml b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/databricks.yml new file mode 100644 index 00000000000..94f3c6ee784 --- /dev/null +++ b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/databricks.yml @@ -0,0 +1,15 @@ +bundle: + name: test-bundle + +variables: + bad: + type: complex + default: + x: 1 + +resources: + jobs: + j: + name: test + email_notifications: + on_failure: ["a@example.test", "${var.bad}", "b@example.test", "${var.bad}", "${var.bad}", "c@example.test"] diff --git a/acceptance/bundle/validate/references/resolved_invalid_sequence_items/out.test.toml b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/out.test.toml new file mode 100644 index 00000000000..a927a5fbc06 --- /dev/null +++ b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/out.test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DMS = ["", "true"] diff --git a/acceptance/bundle/validate/references/resolved_invalid_sequence_items/output.txt b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/output.txt new file mode 100644 index 00000000000..e5cba00f5d2 --- /dev/null +++ b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/output.txt @@ -0,0 +1,21 @@ + +>>> [CLI] bundle validate -o json +Warning: expected string, found map + at resources.jobs.j.email_notifications.on_failure[1] + in databricks.yml:15:40 + +Warning: expected string, found map + at resources.jobs.j.email_notifications.on_failure[3] + in databricks.yml:15:72 + +Warning: expected string, found map + at resources.jobs.j.email_notifications.on_failure[4] + in databricks.yml:15:86 + +{ + "on_failure": [ + "a@example.test", + "b@example.test", + "c@example.test" + ] +} diff --git a/acceptance/bundle/validate/references/resolved_invalid_sequence_items/script b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/script new file mode 100644 index 00000000000..674c170cabb --- /dev/null +++ b/acceptance/bundle/validate/references/resolved_invalid_sequence_items/script @@ -0,0 +1,2 @@ +# Several list elements whose reference resolves to the wrong type are dropped, the others keep their order. +trace $CLI bundle validate -o json | jq .resources.jobs.j.email_notifications diff --git a/acceptance/bundle/validate/references/to_omitted_struct_defaults/output.txt b/acceptance/bundle/validate/references/to_omitted_struct_defaults/output.txt index 479ce92fe43..2888dfa1557 100644 --- a/acceptance/bundle/validate/references/to_omitted_struct_defaults/output.txt +++ b/acceptance/bundle/validate/references/to_omitted_struct_defaults/output.txt @@ -2,18 +2,10 @@ >>> [CLI] bundle validate -o json { "default": { - "actual_branch": "", - "branch": "", - "bundle_root_path": ".", - "commit": "", - "origin_url": "" + "bundle_root_path": "." }, "type": "complex", "value": { - "actual_branch": "", - "branch": "", - "bundle_root_path": ".", - "commit": "", - "origin_url": "" + "bundle_root_path": "." } } diff --git a/acceptance/bundle/validate/references/to_omitted_struct_defaults/script b/acceptance/bundle/validate/references/to_omitted_struct_defaults/script index cb09a7b5bec..0f7627f7115 100644 --- a/acceptance/bundle/validate/references/to_omitted_struct_defaults/script +++ b/acceptance/bundle/validate/references/to_omitted_struct_defaults/script @@ -1,3 +1,3 @@ # A reference to a struct that is not set in the configuration (here outside a git repository) -# resolves to the struct with its fields filled in, as empty values. +# resolves to the fields that are set; unset fields are omitted. trace $CLI bundle validate -o json | jq .variables.git_info diff --git a/acceptance/bundle/validate/required/databricks.yml b/acceptance/bundle/validate/required/databricks.yml index 82175fb553c..0dc5e459fb3 100644 --- a/acceptance/bundle/validate/required/databricks.yml +++ b/acceptance/bundle/validate/required/databricks.yml @@ -19,7 +19,7 @@ resources: jobs: my_job_1: tasks: - # job_id being 0 should not trigger a warning. + # job_id 0 is not set either: RunJobTask has no ForceSendFields to record it. - task_key: "task_key1" run_job_task: job_id: 0 diff --git a/acceptance/bundle/validate/required/output.txt b/acceptance/bundle/validate/required/output.txt index ae3e6dad2f8..b16e64d49e1 100644 --- a/acceptance/bundle/validate/required/output.txt +++ b/acceptance/bundle/validate/required/output.txt @@ -4,6 +4,10 @@ Warning: required field "catalog_name" is not set at resources.volumes.my_volume in databricks.yml:35:7 +Warning: required field "job_id" is not set + at resources.jobs.my_job_1.tasks[0].run_job_task + in databricks.yml:25:13 + Warning: required field "job_id" is not set at resources.jobs.my_job_1.tasks[1].run_job_task in databricks.yml:29:24 @@ -26,4 +30,4 @@ Workspace: User: [USERNAME] Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default -Found 5 warnings +Found 6 warnings diff --git a/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/script b/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/script index 4f218738be7..400175c2880 100644 --- a/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/script +++ b/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/script @@ -1,2 +1,2 @@ -# A scalar targets section is an error. +# A scalar targets section is reported with a warning at its location. errcode trace $CLI bundle validate diff --git a/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/test.toml b/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/test.toml deleted file mode 100644 index b1bb8151234..00000000000 --- a/acceptance/bundle/validate/targets/scalar_rewrite_diagnostic/test.toml +++ /dev/null @@ -1 +0,0 @@ -Badness = 'The error names an internal step ("failed to rewrite") and has no location, instead of reporting a type error at targets.' diff --git a/acceptance/bundle/validate/variables/complex_default_nested_timestamp/output.txt b/acceptance/bundle/validate/variables/complex_default_nested_timestamp/output.txt index 3c236fb1c2b..d44a21b582a 100644 --- a/acceptance/bundle/validate/variables/complex_default_nested_timestamp/output.txt +++ b/acceptance/bundle/validate/variables/complex_default_nested_timestamp/output.txt @@ -1,10 +1,9 @@ >>> [CLI] bundle validate -Warn: unable to convert typed configuration to dynamic configuration: cannot convert struct field to dynamic type "time": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) -Error: exit error: cannot convert struct field to dynamic type "time": src=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) ref=time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC) - Name: test-bundle +Target: default +Workspace: + User: [USERNAME] + Path: /Workspace/Users/[USERNAME]/.bundle/test-bundle/default -Found 1 error - -Exit code: 1 +Validation OK! diff --git a/acceptance/bundle/validate/variables/complex_default_nested_timestamp/test.toml b/acceptance/bundle/validate/variables/complex_default_nested_timestamp/test.toml deleted file mode 100644 index b1b00a5e46a..00000000000 --- a/acceptance/bundle/validate/variables/complex_default_nested_timestamp/test.toml +++ /dev/null @@ -1 +0,0 @@ -Badness = "A valid config fails with an internal conversion error: the nested timestamp cannot be converted back to the dynamic configuration." diff --git a/acceptance/bundle/validate/variables/complex_unknown_field_location/output.txt b/acceptance/bundle/validate/variables/complex_unknown_field_location/output.txt index 118399cba34..a1405eadd8d 100644 --- a/acceptance/bundle/validate/variables/complex_unknown_field_location/output.txt +++ b/acceptance/bundle/validate/variables/complex_unknown_field_location/output.txt @@ -2,7 +2,6 @@ >>> [CLI] bundle validate Warning: unknown field: misspelled_field at resources.jobs.j - in databricks.yml:9:7 Name: test-bundle Target: default diff --git a/acceptance/bundle/validate/variables/complex_unknown_field_location/test.toml b/acceptance/bundle/validate/variables/complex_unknown_field_location/test.toml new file mode 100644 index 00000000000..ea2c5233725 --- /dev/null +++ b/acceptance/bundle/validate/variables/complex_unknown_field_location/test.toml @@ -0,0 +1 @@ +Badness = "The unknown field warning has no location; it should point at the key in the variable default (databricks.yml:9:7)." diff --git a/acceptance/bundle/validate/variables/target_timestamp_shorthand/test.toml b/acceptance/bundle/validate/variables/target_timestamp_shorthand/test.toml deleted file mode 100644 index 83f0c9957ac..00000000000 --- a/acceptance/bundle/validate/variables/target_timestamp_shorthand/test.toml +++ /dev/null @@ -1 +0,0 @@ -Badness = 'The timestamp shorthand is dropped with "expected map, found time" instead of being used as the default.' diff --git a/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/output.txt b/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/output.txt index 2efbf8faf73..a59db3f995c 100644 --- a/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/output.txt +++ b/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/output.txt @@ -7,3 +7,11 @@ Error: duplicate YAML merge key ('<<') is not allowed; to merge multiple maps, u Found 1 error Exit code: 1 + +>>> [CLI] bundle validate -t dev -o json +Error: duplicate YAML merge key ('<<') is not allowed; to merge multiple maps, use a sequence: '<<: [*anchor1, *anchor2]' + in databricks.yml:18:7 + + +Exit code: 1 +null diff --git a/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/script b/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/script index e316f5883d8..7ce2255cfee 100644 --- a/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/script +++ b/acceptance/bundle/validate/yaml_anchors/duplicate_merge_keys/script @@ -1,2 +1,4 @@ # Two '<<' keys in target variables are an error. errcode trace $CLI bundle validate -t dev +# What was merged into the variable: +errcode trace $CLI bundle validate -t dev -o json | jq .variables.x diff --git a/acceptance/bundle/validate/yaml_anchors/inline_merge_mapping/test.toml b/acceptance/bundle/validate/yaml_anchors/inline_merge_mapping/test.toml deleted file mode 100644 index 84101454028..00000000000 --- a/acceptance/bundle/validate/yaml_anchors/inline_merge_mapping/test.toml +++ /dev/null @@ -1 +0,0 @@ -Badness = "The YAML merge key spec allows a single mapping as the value of the merge key, but it is rejected." diff --git a/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/output.txt b/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/output.txt index 770f0193a58..0aa7a7a987e 100644 --- a/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/output.txt +++ b/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/output.txt @@ -6,3 +6,10 @@ Error: failed to load [TEST_TMP_DIR]/databricks.yml: yaml ([TEST_TMP_DIR]/databr Found 1 error Exit code: 1 + +>>> [CLI] bundle validate -t dev -o json +Error: failed to load [TEST_TMP_DIR]/databricks.yml: yaml ([TEST_TMP_DIR]/databricks.yml:15:11): map merge requires map or sequence of maps as the value + + +Exit code: 1 +null diff --git a/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/script b/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/script index 497e2fefa39..f7617ec1ae1 100644 --- a/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/script +++ b/acceptance/bundle/validate/yaml_anchors/invalid_merge_sequence_member/script @@ -1,2 +1,4 @@ # A scalar among the maps merged with '<<: [...]' is an error. errcode trace $CLI bundle validate -t dev +# What was merged into the variable: +errcode trace $CLI bundle validate -t dev -o json | jq .variables.x diff --git a/acceptance/bundle/validate/yaml_duplicate_keys/shadowed_invalid_subtree/script b/acceptance/bundle/validate/yaml_duplicate_keys/shadowed_invalid_subtree/script index 58b74e2f574..a2059f0648f 100644 --- a/acceptance/bundle/validate/yaml_duplicate_keys/shadowed_invalid_subtree/script +++ b/acceptance/bundle/validate/yaml_duplicate_keys/shadowed_invalid_subtree/script @@ -1,2 +1,2 @@ -# A value replaced by a later duplicate key is still loaded, so its invalid (integer) key is rejected. +# An invalid (integer) key inside a value that a later duplicate key replaces. errcode trace $CLI bundle validate diff --git a/acceptance/invariant_test.go b/acceptance/invariant_test.go index ad8e1d14022..99ac4f4707c 100644 --- a/acceptance/invariant_test.go +++ b/acceptance/invariant_test.go @@ -8,10 +8,9 @@ import ( "testing" "github.com/databricks/cli/bundle/config" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" ) const invariantConfigsDir = "bundle/invariant/configs" @@ -88,31 +87,22 @@ func scanInvariantConfigs(t *testing.T) (present, withPermissions, withGrants ma contents, err := os.ReadFile(path) require.NoError(t, err) - v, err := yamlloader.LoadYAML(path, strings.NewReader(string(contents))) - require.NoError(t, err, "failed to parse %s", path) - - resources := v.Get("resources") - if resources.Kind() != dyn.KindMap { - // Some configs (e.g. PyDABs) declare resources outside of YAML. - continue + var doc struct { + Resources map[string]any `yaml:"resources"` } + require.NoError(t, yaml.Unmarshal(contents, &doc), "failed to parse %s", path) - for _, group := range resources.MustMap().Pairs() { - groupName := group.Key.MustString() + // Some configs (e.g. PyDABs) declare resources outside of YAML. + for groupName, group := range doc.Resources { present[groupName] = true - if group.Value.Kind() != dyn.KindMap { - continue - } - for _, resource := range group.Value.MustMap().Pairs() { - cfg := resource.Value - if cfg.Kind() != dyn.KindMap { - continue - } - if cfg.Get("permissions").Kind() != dyn.KindInvalid { + resources, _ := group.(map[string]any) + for _, resource := range resources { + cfg, _ := resource.(map[string]any) + if _, ok := cfg["permissions"]; ok { withPermissions[groupName] = true } - if cfg.Get("grants").Kind() != dyn.KindInvalid { + if _, ok := cfg["grants"]; ok { withGrants[groupName] = true } } diff --git a/bundle/apps/validate.go b/bundle/apps/validate.go index 2ab3384f991..d64422bfd04 100644 --- a/bundle/apps/validate.go +++ b/bundle/apps/validate.go @@ -6,9 +6,9 @@ import ( "regexp" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/apps" ) @@ -92,34 +92,12 @@ type appResourceReference struct { permission string } -// hasPermissions checks if a bundle resource at the given dyn path has a non-empty permissions list. -func hasPermissions(b *bundle.Bundle, resourcePath string) bool { - pv, err := dyn.Get(b.Config.Value(), resourcePath+".permissions") - if err != nil { - return false - } - s, ok := pv.AsSequence() - return ok && len(s) > 0 -} - // hasAppSPInPermissions checks if any permission entry for the given resource // references the app's service principal via variable interpolation. -func hasAppSPInPermissions(b *bundle.Bundle, resourcePath, appKey string) bool { +func hasAppSPInPermissions(perms config.PermissionList, appKey string) bool { appSPRef := fmt.Sprintf("${resources.apps.%s.service_principal_client_id}", appKey) - pv, err := dyn.Get(b.Config.Value(), resourcePath+".permissions") - if err != nil { - return false - } - s, ok := pv.AsSequence() - if !ok { - return false - } - for _, entry := range s { - spn, err := dyn.Get(entry, "service_principal_name") - if err != nil { - continue - } - if str, ok := spn.AsString(); ok && str == appSPRef { + for i := range perms.Len() { + if perms.At(i).ServicePrincipalName == appSPRef { return true } } @@ -146,12 +124,12 @@ func warnForAppResourcePermissions(b *bundle.Bundle, appKey string, app *resourc } refType, resourceKey := matches[1], matches[2] - resourcePath := fmt.Sprintf("resources.%s.%s", refType, resourceKey) - if !hasPermissions(b, resourcePath) { + perms, ok := b.Config.Resources.PermissionsOf(refType, resourceKey) + if !ok || perms.Len() == 0 { continue } - if hasAppSPInPermissions(b, resourcePath, appKey) { + if hasAppSPInPermissions(perms, appKey) { continue } diff --git a/bundle/artifacts/expand_globs.go b/bundle/artifacts/expand_globs.go index cc5d1f3fb60..933f9e0c537 100644 --- a/bundle/artifacts/expand_globs.go +++ b/bundle/artifacts/expand_globs.go @@ -6,18 +6,21 @@ import ( "path/filepath" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/patchwheel" + "github.com/databricks/cli/libs/structs/structpath" ) -func createGlobError(v dyn.Value, p dyn.Path, message string) diag.Diagnostic { - // The pattern contained in v is an absolute path. +func createGlobError(source string, locs []diag.Location, p *structpath.PathNode, message string) diag.Diagnostic { + var loc diag.Location + if len(locs) > 0 { + loc = locs[0] + } + // The pattern is an absolute path. // Make it relative to the value's location to make it more readable. - source := v.MustString() - if l := v.Location(); l.File != "" { - rel, err := filepath.Rel(filepath.Dir(l.File), source) + if loc.File != "" { + rel, err := filepath.Rel(filepath.Dir(loc.File), source) if err == nil { source = rel } @@ -26,8 +29,8 @@ func createGlobError(v dyn.Value, p dyn.Path, message string) diag.Diagnostic { return diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("%s: %s", source, message), - Locations: []diag.Location{v.Location()}, - Paths: dyn.ToStructPaths(p), + Locations: []diag.Location{loc}, + Paths: []*structpath.PathNode{p}, } } @@ -42,82 +45,75 @@ func (e expandGlobs) Name() string { func (e expandGlobs) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { // Base path for this mutator. // This path is set with the list of expanded globs when done. - base := dyn.NewPath( - dyn.Key("artifacts"), - dyn.Key(e.name), - dyn.Key("files"), - ) - - // Pattern to match the source key in the files sequence. - pattern := dyn.NewPatternFromPath(base).Append( - dyn.AnyIndex(), - dyn.Key("source"), - ) + base := structpath.NewPath(nil, "artifacts", e.name, "files") + + artifact := b.Config.Artifacts[e.name] + if artifact == nil { + return nil + } var diags diag.Diagnostics - err := b.Config.Mutate(func(rootv dyn.Value) (dyn.Value, error) { - var output []dyn.Value - _, err := dyn.MapByPattern(rootv, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - if v.Kind() != dyn.KindString { - return v, nil - } - - source := v.MustString() - - // Expand any glob reference in files source path - matches, err := filepath.Glob(source) - if err != nil { - diags = diags.Append(createGlobError(v, p, err.Error())) - - // Drop this value from the list; this does not matter since we've raised an error anyway - return v, nil - } - - // Note, we're applying this for all artifact types, not just "whl". - // Rationale: - // 1. type is optional - // 2. if you have wheels in other artifact type, maybe you still want the filter logic? impossible to say. - matches = patchwheel.FilterLatestWheels(ctx, matches) - - if len(matches) == 1 && matches[0] == source { - // No glob expansion was performed. - // Keep node unchanged. We need to ensure that "patched" field remains and not wiped out by code below. - parent, err := dyn.GetByPath(rootv, p[0:len(p)-1]) - if err != nil { - log.Debugf(ctx, "Failed to get parent of %s", p.String()) - } else { - output = append(output, parent) - } - return v, nil - } - - if len(matches) == 0 { - diags = diags.Append(createGlobError(v, p, "no matching files")) - - // Drop this value from the list; this does not matter since we've raised an error anyway - return v, nil - } - - for _, match := range matches { - output = append(output, dyn.V( - map[string]dyn.Value{ - "source": dyn.NewValue(match, v.Locations()), - }, - )) - } - - return v, nil - }) - - if err != nil || diags.HasError() { - return rootv, err + var output []config.ArtifactFile + var sources [][]int + var locations [][]diag.Location + for index, f := range artifact.Files { + np := structpath.NewPath(base, index, "source") + locs := b.Config.GetLocationsOfExact(np) + // An empty source is missing (ArtifactFile has no ForceSendFields), which the + // required-field validation reports. + if f.Source == "" { + continue + } + source := f.Source + + // Expand any glob reference in files source path + matches, err := filepath.Glob(source) + if err != nil { + diags = diags.Append(createGlobError(source, locs, np, err.Error())) + + // Drop this value from the list; this does not matter since we've raised an error anyway + continue + } + + // Note, we're applying this for all artifact types, not just "whl". + // Rationale: + // 1. type is optional + // 2. if you have wheels in other artifact type, maybe you still want the filter logic? impossible to say. + matches = patchwheel.FilterLatestWheels(ctx, matches) + + if len(matches) == 1 && matches[0] == source { + // No glob expansion was performed. + // Keep node unchanged. We need to ensure that "patched" field remains and not wiped out by code below. + output = append(output, artifact.Files[index]) + sources = append(sources, []int{index}) + locations = append(locations, nil) + continue } - // Set the expanded globs back into the configuration. - return dyn.SetByPath(rootv, base, dyn.V(output)) - }) - if err != nil { - diags = diags.Extend(diag.FromErr(err)) + if len(matches) == 0 { + diags = diags.Append(createGlobError(source, locs, np, "no matching files")) + + // Drop this value from the list; this does not matter since we've raised an error anyway + continue + } + + for _, match := range matches { + output = append(output, config.ArtifactFile{Source: match}) + sources = append(sources, []int{index}) + locations = append(locations, locs) + } + } + if diags.HasError() { + return diags + } + + // Set the expanded globs back into the configuration. + artifact.Files = output + b.Config.UpdateSequence(base, sources) + for i, locs := range locations { + if locs != nil { + b.Config.SetLocations(structpath.NewPath(base, i, "source"), locs) + } } return diags diff --git a/bundle/artifacts/prepare.go b/bundle/artifacts/prepare.go index e285eb1ee30..f15add69f74 100644 --- a/bundle/artifacts/prepare.go +++ b/bundle/artifacts/prepare.go @@ -17,6 +17,7 @@ import ( "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" "github.com/databricks/cli/libs/python" + "github.com/databricks/cli/libs/structs/structpath" ) func Prepare() bundle.Mutator { @@ -61,7 +62,7 @@ func (m *prepare) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics logdiag.LogError(ctx, fmt.Errorf("artifact %q: a tgz artifact needs a `files` entry naming the output path", artifactName)) } - l := b.Config.GetLocation("artifacts." + artifactName) + l := b.Config.GetLocationOfExact(structpath.NewPath(nil, "artifacts", artifactName)) dirPath := filepath.Dir(l.File) // Check if source paths are absolute, if not, make them absolute diff --git a/bundle/bundle_test.go b/bundle/bundle_test.go index 69c8fa5bdcb..f2eee52b2bd 100644 --- a/bundle/bundle_test.go +++ b/bundle/bundle_test.go @@ -9,7 +9,6 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/env" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/logdiag" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -157,9 +156,6 @@ func TestBundleGetResourceConfigJobsPointer(t *testing.T) { }, } - // Initialize the dynamic representation so GetResourceConfig can query it. - require.NoError(t, rootCfg.Mutate(func(v dyn.Value) (dyn.Value, error) { return v, nil })) - b := &Bundle{Config: rootCfg} res, err := b.Config.GetResourceConfig("resources.jobs.my_job") diff --git a/bundle/config/.gitattributes b/bundle/config/.gitattributes new file mode 100644 index 00000000000..e127006baf8 --- /dev/null +++ b/bundle/config/.gitattributes @@ -0,0 +1 @@ +resources_iterate.go linguist-generated=true diff --git a/bundle/config/libraries_iterate.go b/bundle/config/libraries_iterate.go new file mode 100644 index 00000000000..6b0ff477bcb --- /dev/null +++ b/bundle/config/libraries_iterate.go @@ -0,0 +1,108 @@ +package config + +import ( + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/databricks/databricks-sdk-go/service/jobs" +) + +// The iterators below visit the places that hold libraries, in the order of the job +// names (and task indices). They visit lists that are set (not nil); the path is built +// for each visited list. + +// IterateJobTasks calls fn for the tasks of all jobs, or (nested) for the tasks of +// their for_each_task, with the path of the task, until fn returns false. +func (r *Resources) IterateJobTasks(nested bool, fn func(p *structpath.PathNode, task *jobs.Task) bool) { + for _, name := range sortedKeys(r.Jobs) { + job := r.Jobs[name] + if job == nil { + continue + } + for i := range job.Tasks { + task := &job.Tasks[i] + if nested { + if task.ForEachTask == nil { + continue + } + task = &task.ForEachTask.Task + } + p := structpath.NewPath(nil, "resources", "jobs", name, "tasks", i) + if nested { + p = structpath.NewPath(p, "for_each_task", "task") + } + if !fn(p, task) { + return + } + } + } +} + +// IterateTaskLibraries calls fn for the libraries of the tasks of all jobs, or (nested) +// of the tasks of their for_each_task, until fn returns false. +func (r *Resources) IterateTaskLibraries(nested bool, fn func(p *structpath.PathNode, libs *[]compute.Library) bool) { + r.IterateJobTasks(nested, func(p *structpath.PathNode, task *jobs.Task) bool { + if task.Libraries == nil { + return true + } + return fn(structpath.NewPath(p, "libraries"), &task.Libraries) + }) +} + +// IterateAiRuntimeCodeSources calls fn for the code_source_path of the AI Runtime tasks +// of all jobs, or (nested) of the tasks of their for_each_task, until fn returns false. +func (r *Resources) IterateAiRuntimeCodeSources(nested bool, fn func(p *structpath.PathNode, s *string) bool) { + r.IterateJobTasks(nested, func(p *structpath.PathNode, task *jobs.Task) bool { + if task.AiRuntimeTask == nil || task.AiRuntimeTask.CodeSourcePath == "" { + return true + } + return fn(structpath.NewPath(p, "ai_runtime_task", "code_source_path"), &task.AiRuntimeTask.CodeSourcePath) + }) +} + +// IterateJobEnvironmentDependencies calls fn for the dependencies of the environments of +// all jobs, until fn returns false. +func (r *Resources) IterateJobEnvironmentDependencies(fn func(p *structpath.PathNode, deps *[]string) bool) { + for _, name := range sortedKeys(r.Jobs) { + job := r.Jobs[name] + if job == nil { + continue + } + for i := range job.Environments { + spec := job.Environments[i].Spec + if spec == nil || spec.Dependencies == nil { + continue + } + if !fn(structpath.NewPath(nil, "resources", "jobs", name, "environments", i, "spec", "dependencies"), &spec.Dependencies) { + return + } + } + } +} + +// IteratePipelineEnvironmentDependencies calls fn for the environment dependencies of +// all pipelines, until fn returns false. +func (r *Resources) IteratePipelineEnvironmentDependencies(fn func(p *structpath.PathNode, deps *[]string) bool) { + for _, name := range sortedKeys(r.Pipelines) { + pl := r.Pipelines[name] + if pl == nil || pl.Environment == nil || pl.Environment.Dependencies == nil { + continue + } + if !fn(structpath.NewPath(nil, "resources", "pipelines", name, "environment", "dependencies"), &pl.Environment.Dependencies) { + return + } + } +} + +// IterateClusterLibraries calls fn for the libraries of all clusters, until fn returns +// false. +func (r *Resources) IterateClusterLibraries(fn func(p *structpath.PathNode, libs *[]compute.Library) bool) { + for _, name := range sortedKeys(r.Clusters) { + c := r.Clusters[name] + if c == nil || c.Libraries == nil { + continue + } + if !fn(structpath.NewPath(nil, "resources", "clusters", name, "libraries"), &c.Libraries) { + return + } + } +} diff --git a/bundle/config/loader/entry_point.go b/bundle/config/loader/entry_point.go index d476cb221c4..b44ab81e813 100644 --- a/bundle/config/loader/entry_point.go +++ b/bundle/config/loader/entry_point.go @@ -24,7 +24,7 @@ func (m *entryPoint) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics if err != nil { return diag.FromErr(err) } - this, diags := config.Load(path) + this, diags := config.LoadDyn(path) if diags.HasError() { return diags } diff --git a/bundle/config/loader/entry_point_test.go b/bundle/config/loader/entry_point_test.go index b61d3f876ac..cd088fb771c 100644 --- a/bundle/config/loader/entry_point_test.go +++ b/bundle/config/loader/entry_point_test.go @@ -5,13 +5,14 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/loader" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestEntryPointNoRootPath(t *testing.T) { b := &bundle.Bundle{} - diags := bundle.Apply(t.Context(), b, loader.EntryPoint()) + diags := bundletest.ApplyLoading(t, b, loader.EntryPoint()) require.Error(t, diags.Error()) } @@ -19,7 +20,7 @@ func TestEntryPoint(t *testing.T) { b := &bundle.Bundle{ BundleRootPath: "testdata/basic", } - diags := bundle.Apply(t.Context(), b, loader.EntryPoint()) + diags := bundletest.ApplyLoading(t, b, loader.EntryPoint()) require.NoError(t, diags.Error()) assert.Equal(t, "loader_test", b.Config.Bundle.Name) } diff --git a/bundle/config/loader/process_include.go b/bundle/config/loader/process_include.go index 064855caca2..2d5c3156ad7 100644 --- a/bundle/config/loader/process_include.go +++ b/bundle/config/loader/process_include.go @@ -151,7 +151,7 @@ func (m *processInclude) Name() string { } func (m *processInclude) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { - this, diags := config.Load(m.fullPath) + this, diags := config.LoadDyn(m.fullPath) if diags.HasError() { return diags } @@ -162,7 +162,7 @@ func (m *processInclude) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnos return diags } - if len(this.Include) > 0 { + if include := this.Value().Get("include"); include.Kind() == dyn.KindSequence && len(include.MustSequence()) > 0 { diags = diags.Append(diag.Diagnostic{ Severity: diag.Warning, Summary: "Include section is defined outside root file", diff --git a/bundle/config/loader/process_include_test.go b/bundle/config/loader/process_include_test.go index 16ed455e622..764d576b157 100644 --- a/bundle/config/loader/process_include_test.go +++ b/bundle/config/loader/process_include_test.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/loader" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" @@ -30,7 +31,7 @@ func TestProcessInclude(t *testing.T) { assert.Equal(t, "foo", b.Config.Workspace.Host) // Apply the mutator and assert that the host value has been updated - diags := bundle.Apply(t.Context(), b, m) + diags := bundletest.ApplyLoading(t, b, m) require.NoError(t, diags.Error()) assert.Equal(t, "bar", b.Config.Workspace.Host) } @@ -54,7 +55,7 @@ func TestProcessIncludeFormatMatch(t *testing.T) { } m := loader.ProcessInclude(filepath.Join(b.BundleRootPath, fileName), fileName) - diags := bundle.Apply(t.Context(), b, m) + diags := bundletest.ApplyLoading(t, b, m) assert.Empty(t, diags) }) } @@ -209,7 +210,7 @@ func TestProcessIncludeFormatNotMatch(t *testing.T) { } m := loader.ProcessInclude(filepath.Join(b.BundleRootPath, fileName), fileName) - diags := bundle.Apply(t.Context(), b, m) + diags := bundletest.ApplyLoading(t, b, m) require.Len(t, diags, 1) assert.Equal(t, expectedDiags, diags) }) diff --git a/bundle/config/loader/process_root_includes.go b/bundle/config/loader/process_root_includes.go index 20e80564b0f..853043eb5e9 100644 --- a/bundle/config/loader/process_root_includes.go +++ b/bundle/config/loader/process_root_includes.go @@ -10,6 +10,9 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/log" + "github.com/databricks/cli/libs/logdiag" ) type processRootIncludes struct{} @@ -39,10 +42,14 @@ func hasGlobCharacters(path string) (string, bool) { func (m *processRootIncludes) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { var out []bundle.Mutator + var include []string + if err := b.Config.DecodeLoading(&include, "include"); err != nil { + return diag.FromErr(err) + } + // Preserve the raw include patterns before they are replaced below with the // expanded list of loaded files, so IsFileIncluded can re-match against them. - patterns := b.Config.Include - b.SetIncludePatterns(patterns) + b.SetIncludePatterns(include) // Map with files we've already seen to avoid loading them twice. seen := map[string]bool{} @@ -77,7 +84,7 @@ func (m *processRootIncludes) Apply(ctx context.Context, b *bundle.Bundle) diag. // For each glob, find all files to load. // Ordering of the list of globs is maintained in the output. // For matches that appear in multiple globs, only the first is kept. - for entryIndex, entry := range b.Config.Include { + for entryIndex, entry := range include { // Include paths must be relative. if filepath.IsAbs(entry) { return diag.Errorf("%s: includes must be relative paths", entry) @@ -131,21 +138,38 @@ func (m *processRootIncludes) Apply(ctx context.Context, b *bundle.Bundle) diag. } } + // Swap out the original includes list with the expanded globs. + err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { + // Include is omitempty in the typed configuration, so an empty list must stay + // absent from the dynamic tree rather than be written as []. + if len(files) == 0 { + return dyn.DropKeys(root, []string{"include"}) + } + + includeValues := make([]dyn.Value, 0, len(files)) + for _, file := range files { + includeValues = append(includeValues, dyn.V(file)) + } + return dyn.Set(root, "include", dyn.NewValue(includeValues, root.Get("include").Locations())) + }) + if err != nil { + return diag.FromErr(err) + } + // Track number of bundle YAML (or JSON) files in the configuration. The +1 is there // to account for the root databricks.yaml file. b.Metrics.ConfigurationFileCount = int64(len(files)) + 1 - // ProcessInclude merges into the configuration via [config.Root.Merge], so it does - // not need its own mutator scope. Giving each included file one would re-convert the - // whole accumulated configuration per file, making load quadratic in the number of - // included files (~20 minutes for 6000 files). - bundle.ApplySeqInScopeContext(ctx, b, out...) - - // Swap out the original include patterns with the expanded globs. This is done after - // the includes are merged: [config.Root.Merge] reloads the typed configuration from - // the dynamic tree, which still holds the patterns, followed by the include sections - // of included files (merging concatenates sequences). Those are kept as they are. - // Include is omitempty, so an empty list is absent from the configuration. - b.Config.Include = append(files, b.Config.Include[len(patterns):]...) + // Like main, the includes are applied without being recorded as mutators. + for _, m := range out { + mctx := log.NewContext(ctx, log.GetLogger(ctx).With("mutator", m.Name())) + log.Debugf(mctx, "Apply") + for _, d := range m.Apply(mctx, b) { + logdiag.LogDiag(mctx, d) + } + if logdiag.HasError(ctx) { + break + } + } return nil } diff --git a/bundle/config/loader/process_root_includes_test.go b/bundle/config/loader/process_root_includes_test.go index a3d1c2fec88..5cceb2a7e8a 100644 --- a/bundle/config/loader/process_root_includes_test.go +++ b/bundle/config/loader/process_root_includes_test.go @@ -1,16 +1,15 @@ package loader_test import ( - "path/filepath" "runtime" "testing" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/loader" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/internal/testutil" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -19,7 +18,7 @@ func TestProcessRootIncludesEmpty(t *testing.T) { b := &bundle.Bundle{ BundleRootPath: ".", } - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.NoError(t, diags.Error()) } @@ -39,7 +38,7 @@ func TestProcessRootIncludesAbs(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.True(t, diags.HasError()) assert.ErrorContains(t, diags.Error(), "must be relative paths") } @@ -58,7 +57,7 @@ func TestProcessRootIncludesSingleGlob(t *testing.T) { testutil.Touch(t, b.BundleRootPath, "a.yml") testutil.Touch(t, b.BundleRootPath, "b.yml") - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.NoError(t, diags.Error()) assert.Equal(t, []string{"a.yml", "b.yml"}, b.Config.Include) } @@ -77,7 +76,7 @@ func TestProcessRootIncludesMultiGlob(t *testing.T) { testutil.Touch(t, b.BundleRootPath, "a1.yml") testutil.Touch(t, b.BundleRootPath, "b1.yml") - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.NoError(t, diags.Error()) assert.Equal(t, []string{"a1.yml", "b1.yml"}, b.Config.Include) } @@ -95,34 +94,11 @@ func TestProcessRootIncludesRemoveDups(t *testing.T) { testutil.Touch(t, b.BundleRootPath, "a.yml") - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.NoError(t, diags.Error()) assert.Equal(t, []string{"a.yml"}, b.Config.Include) } -// The expanded include list must be visible in both the typed and the dynamic -// configuration: merging the per-file includes reloads the typed configuration from the -// dynamic tree, so the expanded list must not be overwritten by the raw patterns. -func TestProcessRootIncludesUpdatesDynamicValue(t *testing.T) { - b := &bundle.Bundle{ - BundleRootPath: t.TempDir(), - Config: config.Root{ - Include: []string{ - "*.yml", - }, - }, - } - - testutil.Touch(t, b.BundleRootPath, "databricks.yml") - testutil.Touch(t, b.BundleRootPath, "a.yml") - - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) - require.NoError(t, diags.Error()) - assert.Equal(t, []string{"a.yml"}, b.Config.Include) - - assert.Equal(t, []any{"a.yml"}, b.Config.Value().Get("include").AsAny()) -} - // An empty include list must stay absent from the dynamic tree: the typed field is // omitempty, so writing [] would add an empty "include" to `bundle validate -o json`. func TestProcessRootIncludesEmptyOmitsDynamicValue(t *testing.T) { @@ -137,32 +113,10 @@ func TestProcessRootIncludesEmptyOmitsDynamicValue(t *testing.T) { testutil.Touch(t, b.BundleRootPath, "databricks.yml") - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.NoError(t, diags.Error()) assert.Empty(t, b.Config.Include) - assert.Equal(t, dyn.KindInvalid, b.Config.Value().Get("include").Kind()) -} - -// Include sections of included files are not applied, but they are merged into the -// include list after the expanded globs. -func TestProcessRootIncludesKeepsIncludesOfIncludedFiles(t *testing.T) { - b := &bundle.Bundle{ - BundleRootPath: t.TempDir(), - Config: config.Root{ - Include: []string{ - "*.yml", - }, - }, - } - - testutil.Touch(t, b.BundleRootPath, "databricks.yml") - testutil.WriteFile(t, filepath.Join(b.BundleRootPath, "a.yml"), "include:\n - c.yml\n") - testutil.Touch(t, b.BundleRootPath, "b.yml") - - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) - require.NoError(t, diags.Error()) - assert.Equal(t, []string{"a.yml", "b.yml", "c.yml"}, b.Config.Include) - assert.Equal(t, []any{"a.yml", "b.yml", "c.yml"}, b.Config.Value().Get("include").AsAny()) + assert.Nil(t, b.Config.Include) } func TestProcessRootIncludesNotExists(t *testing.T) { @@ -174,7 +128,7 @@ func TestProcessRootIncludesNotExists(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.True(t, diags.HasError()) assert.ErrorContains(t, diags.Error(), "notexist.yml defined in 'include' section does not match any files") } @@ -238,7 +192,7 @@ func TestProcessRootIncludesGlobInRootPath(t *testing.T) { BundleRootPath: test.root, } - diags := bundle.Apply(t.Context(), b, loader.ProcessRootIncludes()) + diags := bundletest.ApplyLoading(t, b, loader.ProcessRootIncludes()) require.True(t, diags.HasError()) assert.Len(t, diags, 1) assert.Equal(t, test.diag, diags[0]) diff --git a/libs/dyn/dynloc/locations.go b/bundle/config/loctable/locations.go similarity index 61% rename from libs/dyn/dynloc/locations.go rename to bundle/config/loctable/locations.go index d343e24533c..e7307e04c1e 100644 --- a/libs/dyn/dynloc/locations.go +++ b/bundle/config/loctable/locations.go @@ -1,4 +1,4 @@ -package dynloc +package loctable import ( "fmt" @@ -7,7 +7,6 @@ import ( "slices" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" ) const ( @@ -16,7 +15,7 @@ const ( Version = 1 ) -// Locations is a structure that holds location information for (a subset of) a [dyn.Value] value. +// Locations is a structure that holds location information for (a subset of) a configuration. type Locations struct { // Version is the version of the location information. Version int `json:"version"` @@ -24,9 +23,9 @@ type Locations struct { // Files is a list of file paths. Files []string `json:"files"` - // Locations maps the string representation of a [dyn.Path] to a list of 3-tuples that represent the index + // Locations maps the string representation of a path to a list of 3-tuples that represent the index // of the file in the [Files] array, followed by the line and column number. - // A single [dyn.Path] can have multiple locations (e.g. the effective location and original definition). + // A single path can have multiple locations (e.g. the effective location and original definition). Locations map[string][][]int `json:"locations"` // fileToIndex maps file paths to their index in the [Files] array. @@ -38,29 +37,6 @@ type Locations struct { basePath string } -func (l *Locations) gatherLocations(v dyn.Value) (map[string][]diag.Location, error) { - locs := map[string][]diag.Location{} - patterns := []dyn.Pattern{ - dyn.NewPattern(dyn.AnyKey()), // Top level fields - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey()), // Resource groups ("resources.jobs") - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), // Resources for all types ("resources.jobs.my_job") - dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey(), dyn.Key("tasks")), // Job tasks ("resources.jobs.my_job.tasks") - dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey(), dyn.Key("tasks"), dyn.AnyIndex()), // Job task items ("resources.jobs.my_job.tasks[2]") - } - - for _, pattern := range patterns { - _, err := dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - locs[p.String()] = v.Locations() - return v, nil - }) - if err != nil { - return nil, err - } - } - - return locs, nil -} - func (l *Locations) normalizeFilePath(file string) (string, error) { var err error @@ -113,7 +89,7 @@ func (l *Locations) addLocation(path, file string, line, col int) error { // Expect the file to be present in the lookup map. if _, ok := l.fileToIndex[file]; !ok { // This indicates a logic problem below, but we rather not panic. - return fmt.Errorf("dynloc: unknown file %q", file) + return fmt.Errorf("loctable: unknown file %q", file) } // Add the location to the map. @@ -125,8 +101,8 @@ func (l *Locations) addLocation(path, file string, line, col int) error { return nil } -// Build constructs a [Locations] object from a [dyn.Value]. -func Build(v dyn.Value, basePath string) (Locations, error) { +// Build constructs a [Locations] object from locations keyed by the string representation of a path. +func Build(pathToLocations map[string][]diag.Location, basePath string) (Locations, error) { l := Locations{ Version: Version, Files: make([]string, 0), @@ -137,15 +113,9 @@ func Build(v dyn.Value, basePath string) (Locations, error) { basePath: basePath, } - // Traverse the value and collect locations. - pathToLocations, err := l.gatherLocations(v) - if err != nil { - return l, err - } - // Normalize file paths and add locations. // This step adds files to the [Files] array in alphabetical order. - err = l.registerFileNames(slices.Concat(slices.Collect(maps.Values(pathToLocations))...)) + err := l.registerFileNames(slices.Concat(slices.Collect(maps.Values(pathToLocations))...)) if err != nil { return l, err } diff --git a/bundle/config/mutator/collect_escape_telemetry.go b/bundle/config/mutator/collect_escape_telemetry.go index b24922bb5b3..2a844446abc 100644 --- a/bundle/config/mutator/collect_escape_telemetry.go +++ b/bundle/config/mutator/collect_escape_telemetry.go @@ -5,8 +5,8 @@ import ( "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" ) // Temporary: this mutator collects telemetry on escape patterns ($${}, $$, \${}, \$) @@ -24,12 +24,7 @@ func (*collectEscapeTelemetry) Name() string { func (*collectEscapeTelemetry) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { var hasDoubleDollarBrace, hasDoubleDollar, hasBackslashDollarBrace, hasBackslashDollar bool - _, err := dyn.Walk(b.Config.Value(), func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - s, ok := v.AsString() - if !ok { - return v, nil - } - + walk.Strings(&b.Config, walk.NonEmpty, func(_ *walk.Node, s string) (string, bool) { if !hasDoubleDollarBrace && strings.Contains(s, "$${") { hasDoubleDollarBrace = true } @@ -42,12 +37,8 @@ func (*collectEscapeTelemetry) Apply(ctx context.Context, b *bundle.Bundle) diag if !hasBackslashDollar && containsBackslashDollarWithoutBrace(s) { hasBackslashDollar = true } - - return v, nil + return "", false }) - if err != nil { - return diag.FromErr(err) - } if hasDoubleDollarBrace { b.Metrics.SetBoolValue("config_has_double_dollar_brace", true) diff --git a/bundle/config/mutator/compute_id_compate_test.go b/bundle/config/mutator/compute_id_compate_test.go index 728bae43191..34cf1df96c7 100644 --- a/bundle/config/mutator/compute_id_compate_test.go +++ b/bundle/config/mutator/compute_id_compate_test.go @@ -6,6 +6,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" "github.com/stretchr/testify/assert" ) @@ -19,7 +20,7 @@ func TestComputeIdToClusterId(t *testing.T) { }, } - diags := bundle.Apply(t.Context(), b, mutator.ComputeIdToClusterId()) + diags := bundletest.ApplyLoading(t, b, mutator.ComputeIdToClusterId()) assert.NoError(t, diags.Error()) assert.Equal(t, "compute-id", b.Config.Bundle.ClusterId) assert.Empty(t, b.Config.Bundle.ComputeId) @@ -40,11 +41,11 @@ func TestComputeIdToClusterIdInTargetOverride(t *testing.T) { }, } - diags := bundle.Apply(t.Context(), b, mutator.ComputeIdToClusterId()) + diags := bundletest.ApplyLoading(t, b, mutator.ComputeIdToClusterId()) assert.NoError(t, diags.Error()) assert.Empty(t, b.Config.Targets["dev"].ComputeId) - diags = diags.Extend(bundle.Apply(t.Context(), b, mutator.SelectTarget("dev"))) + diags = diags.Extend(bundletest.ApplyLoading(t, b, mutator.SelectTarget("dev"))) assert.NoError(t, diags.Error()) assert.Equal(t, "compute-id-dev", b.Config.Bundle.ClusterId) diff --git a/bundle/config/mutator/default_target.go b/bundle/config/mutator/default_target.go index 73d99002a06..4ec7b942134 100644 --- a/bundle/config/mutator/default_target.go +++ b/bundle/config/mutator/default_target.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" ) type defineDefaultTarget struct { @@ -14,7 +14,8 @@ type defineDefaultTarget struct { } // DefineDefaultTarget adds a target named "default" -// to the configuration if none have been defined. +// to the configuration if none have been defined. It runs while the configuration +// is loading. func DefineDefaultTarget() bundle.Mutator { return &defineDefaultTarget{ name: "default", @@ -26,13 +27,14 @@ func (m *defineDefaultTarget) Name() string { } func (m *defineDefaultTarget) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { - // Nothing to do if the configuration has at least 1 target. - if len(b.Config.Targets) > 0 { - return nil - } - - // Define default target. - b.Config.Targets = make(map[string]*config.Target) - b.Config.Targets[m.name] = &config.Target{} - return nil + err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { + targets := v.Get("targets") + if m, ok := targets.AsMap(); ok && m.Len() > 0 { + return v, nil + } + return dyn.Set(v, "targets", dyn.NewValue(map[string]dyn.Value{ + m.name: dyn.V(map[string]dyn.Value{}), + }, targets.Locations())) + }) + return diag.FromErr(err) } diff --git a/bundle/config/mutator/default_target_test.go b/bundle/config/mutator/default_target_test.go index 6474b81978d..eead6b57400 100644 --- a/bundle/config/mutator/default_target_test.go +++ b/bundle/config/mutator/default_target_test.go @@ -6,13 +6,14 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDefaultTarget(t *testing.T) { b := &bundle.Bundle{} - diags := bundle.Apply(t.Context(), b, mutator.DefineDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.DefineDefaultTarget()) require.NoError(t, diags.Error()) env, ok := b.Config.Targets["default"] @@ -28,7 +29,7 @@ func TestDefaultTargetAlreadySpecified(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.DefineDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.DefineDefaultTarget()) require.NoError(t, diags.Error()) _, ok := b.Config.Targets["default"] diff --git a/bundle/config/mutator/drop_empty_strings.go b/bundle/config/mutator/drop_empty_strings.go index 028351ed943..a193cc26575 100644 --- a/bundle/config/mutator/drop_empty_strings.go +++ b/bundle/config/mutator/drop_empty_strings.go @@ -4,10 +4,9 @@ import ( "context" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/cli/libs/structs/structpath" ) type dropEmptyStrings struct{} @@ -17,10 +16,8 @@ type dropEmptyStrings struct{} // either literally (policy_id: "") or via a variable that resolved to "", so // this must run after variable resolution. // -// The direct engine converts the resolved config through convert.ToTyped, which -// force-sends explicitly-set zero values, defeating the omitempty tag. Dropping -// here fixes it and makes the result visible in `bundle validate -o json`, which -// serializes the dynamic value. +// An explicitly-set zero value is force-sent, defeating the omitempty tag. +// Dropping it here fixes it and makes the result visible in `bundle validate -o json`. func DropEmptyStrings() bundle.Mutator { return &dropEmptyStrings{} } @@ -30,31 +27,29 @@ func (m *dropEmptyStrings) Name() string { } func (m *dropEmptyStrings) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - var diags diag.Diagnostics - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - root, err := dyn.Map(root, "resources", func(_ dyn.Path, resources dyn.Value) (dyn.Value, error) { - // Normalize against the resources type so omitempty is known per field. - // Only DropEmptyStrings is set: existing values are kept as-is otherwise. - out, normDiags := convert.Normalize(config.Resources{}, resources, convert.DropEmptyStrings) - diags = diags.Extend(normDiags) - return out, nil - }) - if err != nil { - return root, err + // Drop the empty strings on all struct fields except the ones that must send their zero value. + var empty []*structpath.PathNode + walk.ResourceStrings(&b.Config, walk.EmptyFields, func(n *walk.Node, _ string) (string, bool) { + empty = append(empty, n.Path()) + return "", false + }) + for _, p := range empty { + if err := b.Config.Delete(p); err != nil { + return diag.FromErr(err) } + } - // Keep an empty apps description instead of dropping it: an app update without - // the field leaves the remote description unchanged, so removing description - // from config would never converge. Force-sending "" clears it. - return dyn.MapByPattern(root, dyn.NewPattern(dyn.Key("resources"), dyn.Key("apps"), dyn.AnyKey()), func(_ dyn.Path, app dyn.Value) (dyn.Value, error) { - if _, err := dyn.Get(app, "description"); err != nil { - return dyn.Set(app, "description", dyn.V("")) - } - return app, nil - }) - }) - if err != nil { - diags = diags.Extend(diag.FromErr(err)) + // Keep an empty apps description instead of dropping it: an app update without + // the field leaves the remote description unchanged, so removing description + // from config would never converge. Force-sending "" clears it. + for name, app := range b.Config.Resources.Apps { + if app != nil && (app.Description != "" || walk.Forced(app.ForceSendFields, "Description")) { + continue + } + err := b.Config.Set(structpath.NewPath(nil, "resources", "apps", name, "description"), "") + if err != nil { + return diag.FromErr(err) + } } - return diags + return nil } diff --git a/bundle/config/mutator/environments_compat.go b/bundle/config/mutator/environments_compat.go index fb898edea36..706984ec53c 100644 --- a/bundle/config/mutator/environments_compat.go +++ b/bundle/config/mutator/environments_compat.go @@ -22,7 +22,7 @@ func (m *environmentsToTargets) Name() string { func (m *environmentsToTargets) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { // Short circuit if the "environments" key is not set. // This is the common case. - if b.Config.Environments == nil { + if k := b.Config.Value().Get("environments").Kind(); k == dyn.KindInvalid || k == dyn.KindNil { return nil } diff --git a/bundle/config/mutator/environments_compat_test.go b/bundle/config/mutator/environments_compat_test.go index 0e7d4caa079..987e41b8ab4 100644 --- a/bundle/config/mutator/environments_compat_test.go +++ b/bundle/config/mutator/environments_compat_test.go @@ -6,6 +6,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,7 +27,7 @@ func TestEnvironmentsToTargetsWithBothDefined(t *testing.T) { }, } - diags := bundle.Apply(t.Context(), b, mutator.EnvironmentsToTargets()) + diags := bundletest.ApplyLoading(t, b, mutator.EnvironmentsToTargets()) assert.ErrorContains(t, diags.Error(), `both 'environments' and 'targets' are specified;`) } @@ -41,7 +42,7 @@ func TestEnvironmentsToTargetsWithEnvironmentsDefined(t *testing.T) { }, } - diags := bundle.Apply(t.Context(), b, mutator.EnvironmentsToTargets()) + diags := bundletest.ApplyLoading(t, b, mutator.EnvironmentsToTargets()) require.NoError(t, diags.Error()) assert.Empty(t, b.Config.Environments) assert.Len(t, b.Config.Targets, 1) @@ -58,7 +59,7 @@ func TestEnvironmentsToTargetsWithTargetsDefined(t *testing.T) { }, } - diags := bundle.Apply(t.Context(), b, mutator.EnvironmentsToTargets()) + diags := bundletest.ApplyLoading(t, b, mutator.EnvironmentsToTargets()) require.NoError(t, diags.Error()) assert.Empty(t, b.Config.Environments) assert.Len(t, b.Config.Targets, 1) diff --git a/bundle/config/mutator/initialize_variables.go b/bundle/config/mutator/initialize_variables.go index e72cdde3103..70d0d55ac62 100644 --- a/bundle/config/mutator/initialize_variables.go +++ b/bundle/config/mutator/initialize_variables.go @@ -4,13 +4,15 @@ import ( "context" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/bundle/config/variable" "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" ) type initializeVariables struct{} // InitializeVariables initializes nil variables to their corresponding zero values. +// It runs while the configuration is loading, so that target overrides can set the +// fields of a variable that is declared without any. func InitializeVariables() bundle.Mutator { return &initializeVariables{} } @@ -20,12 +22,16 @@ func (m *initializeVariables) Name() string { } func (m *initializeVariables) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - vars := b.Config.Variables - for k, v := range vars { - if v == nil { - vars[k] = &variable.Variable{} + err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { + if v.Get("variables").Kind() != dyn.KindMap { + return v, nil } - } - - return nil + return dyn.Map(v, "variables", dyn.Foreach(func(_ dyn.Path, variable dyn.Value) (dyn.Value, error) { + if variable.Kind() != dyn.KindNil { + return variable, nil + } + return dyn.NewValue(map[string]dyn.Value{}, variable.Locations()), nil + })) + }) + return diag.FromErr(err) } diff --git a/bundle/config/mutator/initialize_variables_test.go b/bundle/config/mutator/initialize_variables_test.go index 53899baf617..d3ceb4c135a 100644 --- a/bundle/config/mutator/initialize_variables_test.go +++ b/bundle/config/mutator/initialize_variables_test.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" "github.com/databricks/cli/bundle/config/variable" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,7 +23,7 @@ func TestInitializeVariables(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.InitializeVariables()) + diags := bundletest.ApplyLoading(t, b, mutator.InitializeVariables()) require.NoError(t, diags.Error()) assert.NotNil(t, b.Config.Variables["foo"]) assert.NotNil(t, b.Config.Variables["bar"]) @@ -35,7 +36,7 @@ func TestInitializeVariablesWithoutVariables(t *testing.T) { Variables: nil, }, } - diags := bundle.Apply(t.Context(), b, mutator.InitializeVariables()) + diags := bundletest.ApplyLoading(t, b, mutator.InitializeVariables()) require.NoError(t, diags.Error()) assert.Nil(t, b.Config.Variables) } diff --git a/bundle/config/mutator/initialize_volume_paths.go b/bundle/config/mutator/initialize_volume_paths.go index 20ea9ae6b38..945038ff175 100644 --- a/bundle/config/mutator/initialize_volume_paths.go +++ b/bundle/config/mutator/initialize_volume_paths.go @@ -3,13 +3,13 @@ package mutator import ( "context" "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type initializeVolumePaths struct{} @@ -32,29 +32,35 @@ func (m *initializeVolumePaths) Name() string { } func (m *initializeVolumePaths) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - pattern := dyn.NewPattern(dyn.Key("resources"), dyn.Key("volumes"), dyn.AnyKey()) - return dyn.MapByPattern(root, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - // volume_path is computed and read-only; reject a user-provided value instead of overwriting it. - if existing, ok := v.Get("volume_path").AsString(); ok && existing != "" { - return dyn.InvalidValue, fmt.Errorf("%s.volume_path is computed and read-only; remove it from the configuration", p.String()) - } + // Skip converting the configuration if there are no volumes. + if len(b.Config.Resources.Volumes) == 0 { + return nil + } + + // References are resolved against the configuration tree (they can point anywhere). + view := b.Config.View() + volumes := b.Config.Resources.Volumes + for _, name := range slices.Sorted(maps.Keys(volumes)) { + v := volumes[name] + if v == nil { + continue + } + p := structpath.NewPath(nil, "resources", "volumes", name) - var vol resources.Volume - if err := convert.ToTyped(&vol, v); err != nil { - return dyn.InvalidValue, err - } + // volume_path is computed and read-only; reject a user-provided value instead of overwriting it. + if v.VolumePath != "" { + return diag.FromErr(fmt.Errorf("%s.volume_path is computed and read-only; remove it from the configuration", p.String())) + } - // Resolve references to compute the path only; the field values in v are left untouched. - vol.CatalogName = resolveResourceReference(root, vol.CatalogName) - vol.SchemaName = resolveResourceReference(root, vol.SchemaName) - vol.Name = resolveResourceReference(root, vol.Name) + // Resolve references to compute the path only; the field values are left untouched. + vol := *v + vol.CatalogName = resolveResourceReference(view, vol.CatalogName) + vol.SchemaName = resolveResourceReference(view, vol.SchemaName) + vol.Name = resolveResourceReference(view, vol.Name) - return dyn.Set(v, "volume_path", dyn.V(vol.ComputeVolumePath())) - }) - }) - if err != nil { - return diag.FromErr(err) + if err := b.Config.Set(structpath.NewStringKey(p, "volume_path"), vol.ComputeVolumePath()); err != nil { + return diag.FromErr(err) + } } return nil } @@ -62,16 +68,12 @@ func (m *initializeVolumePaths) Apply(_ context.Context, b *bundle.Bundle) diag. // resolveResourceReference resolves a pure ${resources....} reference by looking it up in root. // Values that are not such a reference, or cannot be resolved, are returned unchanged (still // containing "${"), so the caller embeds the reference verbatim to be resolved later. -func resolveResourceReference(root dyn.Value, s string) string { - p, ok := dynvar.PureReferenceToPath(s) - if !ok || p[0].Key() != "resources" { - return s - } - rv, err := dyn.GetByPath(root, p) - if err != nil { +func resolveResourceReference(root structvar.View, s string) string { + p, ok := structvar.PureReferenceToPath(s) + if !ok || p.KeyAt(0) != "resources" { return s } - rs, ok := rv.AsString() + rs, ok := root.Lookup(p).AsString() if !ok { return s } diff --git a/bundle/config/mutator/load_dbalert_files.go b/bundle/config/mutator/load_dbalert_files.go index 437095aa218..b6914230731 100644 --- a/bundle/config/mutator/load_dbalert_files.go +++ b/bundle/config/mutator/load_dbalert_files.go @@ -11,9 +11,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/marshal" "github.com/databricks/databricks-sdk-go/service/sql" ) @@ -57,26 +56,21 @@ func (m *loadDBAlertFiles) Apply(ctx context.Context, b *bundle.Bundle) diag.Dia continue } - alertV, err := dyn.GetByPath(b.Config.Value(), dyn.NewPath(dyn.Key("resources"), dyn.Key("alerts"), dyn.Key(alertKey))) - if err != nil { - return diag.FromErr(err) - } + alertPath := structpath.NewPath(nil, "resources", "alerts", alertKey) + alertV := b.Config.View().Lookup(alertPath) // No other fields other than allowedInYAML should be set in the bundle YAML. - m, ok := alertV.AsMap() - if !ok { + if alertV.Kind() != structvar.KindMap { return diag.FromErr(fmt.Errorf("internal error: alert value is not a map, got %s", alertV.Kind())) } - for _, p := range m.Pairs() { - k := p.Key.MustString() - v := p.Value + for k, v := range alertV.MapItems() { if slices.Contains(allowedInYAML, k) { continue } - if v.Kind() == dyn.KindNil || v.Kind() == dyn.KindInvalid { + if v.Kind() == structvar.KindNil || v.Kind() == structvar.KindInvalid { continue } @@ -86,7 +80,7 @@ func (m *loadDBAlertFiles) Apply(ctx context.Context, b *bundle.Bundle) diag.Dia Severity: diag.Error, Summary: fmt.Sprintf("field %s is not allowed in the bundle configuration.", k), Detail: "When a .dbalert.json is specified, only the following fields are allowed in the bundle configuration: " + strings.Join(allowedInYAML, ", "), - Paths: structpath.NewPathSlice("resources", "alerts", alertKey, k), + Paths: []*structpath.PathNode{structpath.NewStringKey(alertPath, k)}, Locations: v.Locations(), }, } @@ -108,7 +102,7 @@ func (m *loadDBAlertFiles) Apply(ctx context.Context, b *bundle.Bundle) diag.Dia Severity: diag.Error, Summary: fmt.Sprintf("failed to read .dbalert.json file %s: %s", alert.FilePath, err), Detail: "", - Paths: structpath.NewPathSlice("resources", "alerts", alertKey, "file_path"), + Paths: []*structpath.PathNode{structpath.NewStringKey(alertPath, "file_path")}, Locations: alertV.Get("file_path").Locations(), }, } @@ -123,21 +117,21 @@ func (m *loadDBAlertFiles) Apply(ctx context.Context, b *bundle.Bundle) diag.Dia Severity: diag.Error, Summary: fmt.Sprintf("failed to parse .dbalert.json file %s: %s", alert.FilePath, err), Detail: "", - Paths: structpath.NewPathSlice("resources", "alerts", alertKey, "file_path"), + Paths: []*structpath.PathNode{structpath.NewStringKey(alertPath, "file_path")}, Locations: alertV.Get("file_path").Locations(), }, } } // Check that the file does not have any variable interpolations. - if dynvar.ContainsVariableReference(string(content)) { + if structvar.ContainsVariableReference(string(content)) { return diag.Diagnostics{ { ID: diag.ID(""), Severity: diag.Error, Summary: fmt.Sprintf(".alert file %s must not contain variable interpolations.", alert.FilePath), Detail: "Please inline the alert configuration in the bundle configuration to use variables", - Paths: structpath.NewPathSlice("resources", "alerts", alertKey, "file_path"), + Paths: []*structpath.PathNode{structpath.NewStringKey(alertPath, "file_path")}, Locations: alertV.Get("file_path").Locations(), }, } diff --git a/bundle/config/mutator/mutator.go b/bundle/config/mutator/mutator.go index a00488a50fa..9fe4fc1346f 100644 --- a/bundle/config/mutator/mutator.go +++ b/bundle/config/mutator/mutator.go @@ -8,6 +8,7 @@ import ( "github.com/databricks/cli/bundle/config/loader" "github.com/databricks/cli/bundle/config/validate" "github.com/databricks/cli/bundle/scripts" + "github.com/databricks/cli/libs/logdiag" ) func DefaultMutators(ctx context.Context, b *bundle.Bundle) { @@ -34,3 +35,17 @@ func DefaultMutators(ctx context.Context, b *bundle.Bundle) { validate.UniqueResourceKeys(), ) } + +// FinishLoading converts the configuration of b, loaded and merged by +// [DefaultMutators] (and a target selection), into the typed configuration. It is not +// a mutator: it runs once, at the end of the load phase, also if loading failed so +// that the configuration can still be inspected. +func FinishLoading(ctx context.Context, b *bundle.Bundle) { + diags, err := b.Config.FinishLoading() + for _, d := range diags { + logdiag.LogDiag(ctx, d) + } + if err != nil { + logdiag.LogError(ctx, err) + } +} diff --git a/bundle/config/mutator/normalize_paths.go b/bundle/config/mutator/normalize_paths.go index 7256ebad4b7..f67495aedd0 100644 --- a/bundle/config/mutator/normalize_paths.go +++ b/bundle/config/mutator/normalize_paths.go @@ -14,7 +14,7 @@ import ( "github.com/databricks/cli/bundle/config/mutator/paths" "github.com/databricks/cli/bundle/libraries" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) type normalizePaths struct{} @@ -43,41 +43,35 @@ func (a normalizePaths) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnost // Do not normalize job task paths if using git source gitSourcePaths := collectGitSourcePaths(b) - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return paths.VisitPaths(v, func(path dyn.Path, kind paths.TranslateMode, v dyn.Value) (dyn.Value, error) { - if slices.ContainsFunc(gitSourcePaths, path.HasPrefix) { - return v, nil - } - - value, ok := v.AsString() - if !ok { - return dyn.InvalidValue, fmt.Errorf("value at %s is not a string", path.String()) - } + err := paths.VisitPaths(&b.Config, func(path *structpath.PathNode, kind paths.TranslateMode, s *string) error { + if slices.ContainsFunc(gitSourcePaths, path.HasPrefix) { + return nil + } - newValue, err := normalizePath(value, v.Location(), b.BundleRootPath) - if err != nil { - return dyn.InvalidValue, err - } + newValue, err := normalizePath(*s, b.Config.GetLocationOfExact(path), b.BundleRootPath) + if err != nil { + return err + } - return dyn.NewValue(newValue, v.Locations()), nil - }) + *s = newValue + return nil }) if err != nil { return diag.FromErr(fmt.Errorf("failed to normalize paths: %w", err)) } - return diag.FromErr(err) + return nil } -func collectGitSourcePaths(b *bundle.Bundle) []dyn.Path { - var jobs []dyn.Path +func collectGitSourcePaths(b *bundle.Bundle) []*structpath.PathNode { + var jobs []*structpath.PathNode for name, job := range b.Config.Resources.Jobs { if job == nil { continue } if job.GitSource != nil { - jobs = append(jobs, dyn.NewPath(dyn.Key("resources"), dyn.Key("jobs"), dyn.Key(name))) + jobs = append(jobs, structpath.NewPath(nil, "resources", "jobs", name)) } } diff --git a/bundle/config/mutator/normalize_paths_test.go b/bundle/config/mutator/normalize_paths_test.go index 9c9085a27c6..2f0e8a726d6 100644 --- a/bundle/config/mutator/normalize_paths_test.go +++ b/bundle/config/mutator/normalize_paths_test.go @@ -8,7 +8,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -38,20 +38,14 @@ func TestNormalizePaths(t *testing.T) { // update config as if 'notebook_path' property is defined in resources/job_1.yml location := diag.Location{File: filepath.Join(tmpDir, "resources", "job_1.yml")} - path := dyn.MustPathFromString("resources.jobs.job1.tasks[0].notebook_task.notebook_path") - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.MapByPath(v, path, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return dyn.NewValue(value.MustString(), []diag.Location{location}), nil - }) - }) - require.NoError(t, err) + path := structpath.MustParsePath("resources.jobs.job1.tasks[0].notebook_task.notebook_path") + b.Config.SetLocations(path, []diag.Location{location}) diags := bundle.Apply(t.Context(), b, m) require.NoError(t, diags.Error()) - newValue, err := dyn.GetByPath(b.Config.Value(), path) - require.NoError(t, err) - require.Equal(t, "src/notebook.py", newValue.MustString()) + require.Equal(t, "src/notebook.py", b.Config.Resources.Jobs["job1"].Tasks[0].NotebookTask.NotebookPath) + require.Equal(t, []diag.Location{location}, b.Config.GetLocationsOfExact(path)) } func TestNormalizePaths_jobRunOnFileChange(t *testing.T) { @@ -77,20 +71,14 @@ func TestNormalizePaths_jobRunOnFileChange(t *testing.T) { } location := diag.Location{File: filepath.Join(tmpDir, "resources", "run.yml")} - path := dyn.MustPathFromString("resources.job_runs.run1.lifecycle.triggers[0].on_file_change") - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.MapByPath(v, path, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return dyn.NewValue(value.MustString(), []diag.Location{location}), nil - }) - }) - require.NoError(t, err) + path := structpath.MustParsePath("resources.job_runs.run1.lifecycle.triggers[0].on_file_change") + b.Config.SetLocations(path, []diag.Location{location}) diags := bundle.Apply(t.Context(), b, m) require.NoError(t, diags.Error()) - newValue, err := dyn.GetByPath(b.Config.Value(), path) - require.NoError(t, err) - require.Equal(t, "data/*.txt", newValue.MustString()) + require.Equal(t, "data/*.txt", *b.Config.Resources.JobRuns["run1"].Lifecycle.Triggers[0].OnFileChange) + require.Equal(t, []diag.Location{location}, b.Config.GetLocationsOfExact(path)) } func TestNormalizePath_absolutePath(t *testing.T) { diff --git a/bundle/config/mutator/paths/alert_paths_visitor.go b/bundle/config/mutator/paths/alert_paths_visitor.go index 1f1a010c798..dbc73350151 100644 --- a/bundle/config/mutator/paths/alert_paths_visitor.go +++ b/bundle/config/mutator/paths/alert_paths_visitor.go @@ -1,18 +1,23 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" ) -func VisitAlertPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("alerts"), - dyn.AnyKey(), - dyn.Key("file_path"), - ) - - return dyn.MapByPattern(value, pattern, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return fn(path, TranslateModeLocalRelative, value) - }) +func VisitAlertPaths(root *config.Root, fn VisitFunc) error { + for _, name := range sortedKeys(root.Resources.Alerts) { + if a := root.Resources.Alerts[name]; a != nil { + if err := visit(fn, visitArgs{ + owner: a, + field: "file_path", + value: &a.FilePath, + mode: TranslateModeLocalRelative, + path: structpath.NewPath(resourcePath("alerts", name), "file_path"), + }); err != nil { + return err + } + } + } + return nil } diff --git a/bundle/config/mutator/paths/app_paths_visitor.go b/bundle/config/mutator/paths/app_paths_visitor.go index 21a7e467ae2..445b3c5857e 100644 --- a/bundle/config/mutator/paths/app_paths_visitor.go +++ b/bundle/config/mutator/paths/app_paths_visitor.go @@ -1,18 +1,23 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" ) -func VisitAppPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("apps"), - dyn.AnyKey(), - dyn.Key("source_code_path"), - ) - - return dyn.MapByPattern(value, pattern, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return fn(path, TranslateModeDirectory, value) - }) +func VisitAppPaths(root *config.Root, fn VisitFunc) error { + for _, name := range sortedKeys(root.Resources.Apps) { + if a := root.Resources.Apps[name]; a != nil { + if err := visit(fn, visitArgs{ + owner: a, + field: "source_code_path", + value: &a.SourceCodePath, + mode: TranslateModeDirectory, + path: structpath.NewPath(resourcePath("apps", name), "source_code_path"), + }); err != nil { + return err + } + } + } + return nil } diff --git a/bundle/config/mutator/paths/app_paths_visitor_test.go b/bundle/config/mutator/paths/app_paths_visitor_test.go index 9ec01aeaf83..bf08e3d94d2 100644 --- a/bundle/config/mutator/paths/app_paths_visitor_test.go +++ b/bundle/config/mutator/paths/app_paths_visitor_test.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/apps" "github.com/stretchr/testify/assert" ) @@ -24,9 +24,7 @@ func TestAppPathsVisitor(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitAppPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.apps.app0.source_code_path"), - } + expected := structpath.NewPathSlice("resources", "apps", "app0", "source_code_path") assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/artifact_paths_visitor.go b/bundle/config/mutator/paths/artifact_paths_visitor.go index c37ae19fee2..4a1d871f264 100644 --- a/bundle/config/mutator/paths/artifact_paths_visitor.go +++ b/bundle/config/mutator/paths/artifact_paths_visitor.go @@ -1,42 +1,23 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" ) -type artifactRewritePattern struct { - pattern dyn.Pattern - mode TranslateMode -} - -func artifactRewritePatterns() []artifactRewritePattern { - // Base pattern to match all artifacts. - base := dyn.NewPattern( - dyn.Key("artifacts"), - dyn.AnyKey(), - ) - - // Compile list of configuration paths to rewrite. - return []artifactRewritePattern{ - { - pattern: base.Append(dyn.Key("path")), - mode: TranslateModeLocalAbsoluteDirectory, - }, - } -} - -func VisitArtifactPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - var err error - newValue := value - - for _, rewritePattern := range artifactRewritePatterns() { - newValue, err = dyn.MapByPattern(newValue, rewritePattern.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - return fn(p, rewritePattern.mode, v) - }) - if err != nil { - return dyn.InvalidValue, err +func VisitArtifactPaths(root *config.Root, fn VisitFunc) error { + for _, name := range sortedKeys(root.Artifacts) { + if a := root.Artifacts[name]; a != nil { + if err := visit(fn, visitArgs{ + owner: a, + field: "path", + value: &a.Path, + mode: TranslateModeLocalAbsoluteDirectory, + path: structpath.NewPath(nil, "artifacts", name, "path"), + }); err != nil { + return err + } } } - - return newValue, nil + return nil } diff --git a/bundle/config/mutator/paths/artifact_paths_visitor_test.go b/bundle/config/mutator/paths/artifact_paths_visitor_test.go index 5d43036b813..980b284cb11 100644 --- a/bundle/config/mutator/paths/artifact_paths_visitor_test.go +++ b/bundle/config/mutator/paths/artifact_paths_visitor_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/databricks/cli/bundle/config" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" ) @@ -18,9 +18,7 @@ func TestArtifactPathsVisitor(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitArtifactPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("artifacts.artifact0.path"), - } + expected := structpath.NewPathSlice("artifacts", "artifact0", "path") assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/dashboard_paths_visitor.go b/bundle/config/mutator/paths/dashboard_paths_visitor.go index b0a25dfe2ff..ae2701c9591 100644 --- a/bundle/config/mutator/paths/dashboard_paths_visitor.go +++ b/bundle/config/mutator/paths/dashboard_paths_visitor.go @@ -1,18 +1,23 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" ) -func VisitDashboardPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("dashboards"), - dyn.AnyKey(), - dyn.Key("file_path"), - ) - - return dyn.MapByPattern(value, pattern, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return fn(path, TranslateModeLocalRelative, value) - }) +func VisitDashboardPaths(root *config.Root, fn VisitFunc) error { + for _, name := range sortedKeys(root.Resources.Dashboards) { + if d := root.Resources.Dashboards[name]; d != nil { + if err := visit(fn, visitArgs{ + owner: d, + field: "file_path", + value: &d.FilePath, + mode: TranslateModeLocalRelative, + path: structpath.NewPath(resourcePath("dashboards", name), "file_path"), + }); err != nil { + return err + } + } + } + return nil } diff --git a/bundle/config/mutator/paths/dashboard_paths_visitor_test.go b/bundle/config/mutator/paths/dashboard_paths_visitor_test.go index 4827e49d3ff..da360776db6 100644 --- a/bundle/config/mutator/paths/dashboard_paths_visitor_test.go +++ b/bundle/config/mutator/paths/dashboard_paths_visitor_test.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" ) @@ -21,9 +21,7 @@ func TestVisitDashboardPaths(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitDashboardPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.dashboards.dashboard0.file_path"), - } + expected := structpath.NewPathSlice("resources", "dashboards", "dashboard0", "file_path") assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/genie_space_paths_visitor.go b/bundle/config/mutator/paths/genie_space_paths_visitor.go index edd6ff2d8df..974e5f31979 100644 --- a/bundle/config/mutator/paths/genie_space_paths_visitor.go +++ b/bundle/config/mutator/paths/genie_space_paths_visitor.go @@ -1,18 +1,23 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" ) -func VisitGenieSpacePaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("genie_spaces"), - dyn.AnyKey(), - dyn.Key("file_path"), - ) - - return dyn.MapByPattern(value, pattern, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return fn(path, TranslateModeLocalRelative, value) - }) +func VisitGenieSpacePaths(root *config.Root, fn VisitFunc) error { + for _, name := range sortedKeys(root.Resources.GenieSpaces) { + if g := root.Resources.GenieSpaces[name]; g != nil { + if err := visit(fn, visitArgs{ + owner: g, + field: "file_path", + value: &g.FilePath, + mode: TranslateModeLocalRelative, + path: structpath.NewPath(resourcePath("genie_spaces", name), "file_path"), + }); err != nil { + return err + } + } + } + return nil } diff --git a/bundle/config/mutator/paths/job_libraries_paths_visitor.go b/bundle/config/mutator/paths/job_libraries_paths_visitor.go index ad56b1933dc..7a67e52fda5 100644 --- a/bundle/config/mutator/paths/job_libraries_paths_visitor.go +++ b/bundle/config/mutator/paths/job_libraries_paths_visitor.go @@ -1,100 +1,75 @@ package paths import ( + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/libraries" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/jobs" ) -func jobTaskLibrariesRewritePatterns(base dyn.Pattern) []jobRewritePattern { - return []jobRewritePattern{ - { - base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("whl")), - TranslateModeLocalRelative, - noSkipRewrite, - }, - { - base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("jar")), - TranslateModeLocalRelative, - noSkipRewrite, - }, - } +func libraryRule(key string, field func(*jobs.Task, int) *string) taskRule { + return taskRule{TranslateModeLocalRelative, func(t *jobs.Task, c *taskCursor) error { + for i := range t.Libraries { + if err := c.yield(&t.Libraries[i], key, field(t, i), "libraries", i, key); err != nil { + return err + } + } + return nil + }} } -func jobLibrariesRewritePatterns() []jobRewritePattern { - // Base pattern to match all tasks in all jobs. - base := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("tasks"), - dyn.AnyIndex(), - ) - - // Compile list of patterns and their respective rewrite functions. - jobEnvironmentsPatterns := []jobRewritePattern{ - { - dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("environments"), - dyn.AnyIndex(), - dyn.Key("spec"), - dyn.Key("dependencies"), - dyn.AnyIndex(), - ), - TranslateModeLocalRelativeWithPrefix, - func(s string) bool { - return !libraries.IsLibraryLocal(s) - }, - }, - } +var jobTaskLibrariesRules = []taskRule{ + libraryRule("whl", func(t *jobs.Task, i int) *string { return &t.Libraries[i].Whl }), + libraryRule("jar", func(t *jobs.Task, i int) *string { return &t.Libraries[i].Jar }), +} - jobEnvironmentsWithRequirementsPatterns := []jobRewritePattern{ - { - dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("environments"), - dyn.AnyIndex(), - dyn.Key("spec"), - dyn.Key("dependencies"), - dyn.AnyIndex(), - ), - TranslateModeEnvironmentPipFlag, - func(s string) bool { - _, _, ok := libraries.IsLocalPathInPipFlag(s) - return !ok - }, - }, - } +func notLocalLibrary(s string) bool { + return !libraries.IsLibraryLocal(s) +} - taskPatterns := jobTaskLibrariesRewritePatterns(base) - forEachPatterns := jobTaskLibrariesRewritePatterns(base.Append(dyn.Key("for_each_task"), dyn.Key("task"))) - allPatterns := append(taskPatterns, jobEnvironmentsPatterns...) - allPatterns = append(allPatterns, jobEnvironmentsWithRequirementsPatterns...) - allPatterns = append(allPatterns, forEachPatterns...) - return allPatterns +func noLocalPathInPipFlag(s string) bool { + _, _, ok := libraries.IsLocalPathInPipFlag(s) + return !ok } // VisitJobLibrariesPaths visits all libraries related paths in job resources and applies a function to each path. -func VisitJobLibrariesPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - var err error - newValue := value - - for _, rewritePattern := range jobLibrariesRewritePatterns() { - newValue, err = dyn.MapByPattern(newValue, rewritePattern.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - if rewritePattern.skipRewrite(v.MustString()) { - return v, nil +func VisitJobLibrariesPaths(root *config.Root, fn VisitFunc) error { + // Task libraries first, then environment dependencies (local libraries, then + // local paths in pip flags), then the libraries of tasks nested in for_each_task. + if err := visitTasksAt(root, false, jobTaskLibrariesRules, fn); err != nil { + return err + } + for _, rule := range []struct { + mode TranslateMode + skip func(string) bool + }{ + {TranslateModeLocalRelativeWithPrefix, notLocalLibrary}, + {TranslateModeEnvironmentPipFlag, noLocalPathInPipFlag}, + } { + for _, name := range sortedKeys(root.Resources.Jobs) { + job := root.Resources.Jobs[name] + if job == nil { + continue + } + for i, env := range job.Environments { + if env.Spec == nil { + continue + } + for j := range env.Spec.Dependencies { + // Most dependencies are not local paths: check before building the path. + if rule.skip(env.Spec.Dependencies[j]) { + continue + } + if err := visit(fn, visitArgs{ + value: &env.Spec.Dependencies[j], + mode: rule.mode, + path: structpath.NewPath(resourcePath("jobs", name), "environments", i, "spec", "dependencies", j), + }); err != nil { + return err + } + } } - - return fn(p, rewritePattern.mode, v) - }) - if err != nil { - return dyn.InvalidValue, err } } - - return newValue, nil + return visitTasksAt(root, true, jobTaskLibrariesRules, fn) } diff --git a/bundle/config/mutator/paths/job_libraries_paths_visitor_test.go b/bundle/config/mutator/paths/job_libraries_paths_visitor_test.go index 07b3c6d6641..789a2cc72b5 100644 --- a/bundle/config/mutator/paths/job_libraries_paths_visitor_test.go +++ b/bundle/config/mutator/paths/job_libraries_paths_visitor_test.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" @@ -71,10 +71,10 @@ func TestVisitJobLibrariesPaths(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobLibrariesPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.jobs.job0.tasks[4].libraries[0].whl"), - dyn.MustPathFromString("resources.jobs.job0.tasks[5].libraries[0].jar"), - } + expected := structpath.MustParsePaths( + "resources.jobs.job0.tasks[4].libraries[0].whl", + "resources.jobs.job0.tasks[5].libraries[0].jar", + ) assert.ElementsMatch(t, expected, actual) } @@ -103,10 +103,10 @@ func TestVisitJobLibrariesPaths_environments(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobLibrariesPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.jobs.job0.environments[0].spec.dependencies[0]"), - dyn.MustPathFromString("resources.jobs.job0.environments[0].spec.dependencies[1]"), - } + expected := structpath.MustParsePaths( + "resources.jobs.job0.environments[0].spec.dependencies[0]", + "resources.jobs.job0.environments[0].spec.dependencies[1]", + ) assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/job_paths_visitor.go b/bundle/config/mutator/paths/job_paths_visitor.go index a3abd0090a4..72edeb2ef47 100644 --- a/bundle/config/mutator/paths/job_paths_visitor.go +++ b/bundle/config/mutator/paths/job_paths_visitor.go @@ -1,124 +1,185 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/jobs" ) -type jobRewritePattern struct { - pattern dyn.Pattern - mode TranslateMode - skipRewrite func(string) bool +// taskRule visits one kind of path value of a job task. +type taskRule struct { + mode TranslateMode + visit func(task *jobs.Task, c *taskCursor) error } -func noSkipRewrite(string) bool { - return false +// taskCursor is the task being visited. It is reused for all tasks so visiting does +// not allocate; the path is only built for values that are visited. +type taskCursor struct { + name string + index int + nested bool + rule *taskRule + fn VisitFunc } -func jobTaskRewritePatterns(base dyn.Pattern) []jobRewritePattern { - return []jobRewritePattern{ - { - base.Append(dyn.Key("notebook_task"), dyn.Key("notebook_path")), - TranslateModeNotebook, - noSkipRewrite, - }, - { - base.Append(dyn.Key("spark_python_task"), dyn.Key("python_file")), - TranslateModeFile, - noSkipRewrite, - }, - { - base.Append(dyn.Key("dbt_task"), dyn.Key("project_directory")), - TranslateModeDirectory, - noSkipRewrite, - }, - { - base.Append(dyn.Key("sql_task"), dyn.Key("file"), dyn.Key("path")), - TranslateModeFile, - noSkipRewrite, - }, - { - base.Append(dyn.Key("alert_task"), dyn.Key("workspace_path")), - TranslateModeFile, - noSkipRewrite, - }, - { - base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("requirements")), - TranslateModeFile, - noSkipRewrite, - }, - { - // The AI Runtime task runs this bash script on each node; the backend - // reads it as a workspace file, so translate the local path to its - // remote (or immutable-snapshot) location like any other file. - base.Append(dyn.Key("ai_runtime_task"), dyn.Key("deployments"), dyn.AnyIndex(), dyn.Key("command_path")), - TranslateModeFile, - noSkipRewrite, - }, +// yield visits the value s, the field key of owner, at the parts below the task. +func (c *taskCursor) yield(owner any, key string, s *string, parts ...any) error { + // Most candidates (e.g. the whl, jar and requirements of every library) are empty. + if !present(owner, key, s) { + return nil } + p := structpath.NewPath(resourcePath("jobs", c.name), "tasks", c.index) + if c.nested { + p = structpath.NewPath(p, "for_each_task", "task") + } + return visit(c.fn, visitArgs{ + owner: owner, + field: key, + value: s, + mode: c.rule.mode, + path: structpath.NewPath(p, parts...), + }) } -func jobRewritePatterns() []jobRewritePattern { - // Task-level base pattern for all jobs. - base := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("tasks"), - dyn.AnyIndex(), - ) - - taskPatterns := jobTaskRewritePatterns(base) - forEachPatterns := jobTaskRewritePatterns(base.Append(dyn.Key("for_each_task"), dyn.Key("task"))) - patterns := append(taskPatterns, forEachPatterns...) - return append(patterns, - jobRewritePattern{ - dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("environment_variables"), - dyn.AnyIndex(), - dyn.Key("spec"), - dyn.Key("files"), - dyn.AnyIndex(), - ), - TranslateModeFile, - noSkipRewrite, - }, - // Environment-level pattern for all jobs. - jobRewritePattern{ - dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("environments"), - dyn.AnyIndex(), - dyn.Key("spec"), - dyn.Key("project_environment"), - ), - TranslateModeFile, - noSkipRewrite, - }, - ) +var jobTaskRules = []taskRule{ + {TranslateModeNotebook, func(t *jobs.Task, c *taskCursor) error { + if t.NotebookTask == nil { + return nil + } + return c.yield(t.NotebookTask, "notebook_path", &t.NotebookTask.NotebookPath, "notebook_task", "notebook_path") + }}, + {TranslateModeFile, func(t *jobs.Task, c *taskCursor) error { + if t.SparkPythonTask == nil { + return nil + } + return c.yield(t.SparkPythonTask, "python_file", &t.SparkPythonTask.PythonFile, "spark_python_task", "python_file") + }}, + {TranslateModeDirectory, func(t *jobs.Task, c *taskCursor) error { + if t.DbtTask == nil { + return nil + } + return c.yield(t.DbtTask, "project_directory", &t.DbtTask.ProjectDirectory, "dbt_task", "project_directory") + }}, + {TranslateModeFile, func(t *jobs.Task, c *taskCursor) error { + if t.SqlTask == nil || t.SqlTask.File == nil { + return nil + } + return c.yield(t.SqlTask.File, "path", &t.SqlTask.File.Path, "sql_task", "file", "path") + }}, + {TranslateModeFile, func(t *jobs.Task, c *taskCursor) error { + if t.AlertTask == nil { + return nil + } + return c.yield(t.AlertTask, "workspace_path", &t.AlertTask.WorkspacePath, "alert_task", "workspace_path") + }}, + {TranslateModeFile, func(t *jobs.Task, c *taskCursor) error { + for i := range t.Libraries { + if err := c.yield(&t.Libraries[i], "requirements", &t.Libraries[i].Requirements, "libraries", i, "requirements"); err != nil { + return err + } + } + return nil + }}, + // The AI Runtime task runs this bash script on each node; the backend reads it as + // a workspace file, so translate the local path to its remote (or + // immutable-snapshot) location like any other file. + {TranslateModeFile, func(t *jobs.Task, c *taskCursor) error { + if t.AiRuntimeTask == nil { + return nil + } + for i := range t.AiRuntimeTask.Deployments { + if err := c.yield(&t.AiRuntimeTask.Deployments[i], "command_path", &t.AiRuntimeTask.Deployments[i].CommandPath, "ai_runtime_task", "deployments", i, "command_path"); err != nil { + return err + } + } + return nil + }}, } -// VisitJobPaths visits all paths in job resources and applies a function to each path. -func VisitJobPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - var err error - newValue := value +// visitTasks applies the rules to the tasks of all jobs, then to the tasks nested in +// their for_each_task, one rule at a time. +func visitTasks(root *config.Root, rules []taskRule, fn VisitFunc) error { + if err := visitTasksAt(root, false, rules, fn); err != nil { + return err + } + return visitTasksAt(root, true, rules, fn) +} - for _, rewritePattern := range jobRewritePatterns() { - newValue, err = dyn.MapByPattern(newValue, rewritePattern.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - if rewritePattern.skipRewrite(v.MustString()) { - return v, nil +// visitTasksAt applies the rules, one at a time, to the tasks of all jobs, or (nested) +// to the tasks in their for_each_task. +func visitTasksAt(root *config.Root, nested bool, rules []taskRule, fn VisitFunc) error { + c := &taskCursor{nested: nested, fn: fn} + names := sortedKeys(root.Resources.Jobs) + for r := range rules { + c.rule = &rules[r] + for _, name := range names { + job := root.Resources.Jobs[name] + if job == nil { + continue + } + c.name = name + for i := range job.Tasks { + task := &job.Tasks[i] + if nested { + if task.ForEachTask == nil { + continue + } + task = &task.ForEachTask.Task + } + c.index = i + if err := c.rule.visit(task, c); err != nil { + return err + } } - - return fn(p, rewritePattern.mode, v) - }) - if err != nil { - return dyn.InvalidValue, err } } + return nil +} - return newValue, nil +// VisitJobPaths visits all paths in job resources and applies a function to each path. +func VisitJobPaths(root *config.Root, fn VisitFunc) error { + if err := visitTasks(root, jobTaskRules, fn); err != nil { + return err + } + for _, name := range sortedKeys(root.Resources.Jobs) { + job := root.Resources.Jobs[name] + if job == nil { + continue + } + for i, ev := range job.EnvironmentVariables { + if ev.Spec == nil { + continue + } + for j := range ev.Spec.Files { + if err := visit(fn, visitArgs{ + value: &ev.Spec.Files[j], + mode: TranslateModeFile, + path: structpath.NewPath(resourcePath("jobs", name), "environment_variables", i, "spec", "files", j), + }); err != nil { + return err + } + } + } + } + for _, name := range sortedKeys(root.Resources.Jobs) { + job := root.Resources.Jobs[name] + if job == nil { + continue + } + for i, env := range job.Environments { + if env.Spec == nil { + continue + } + if err := visit(fn, visitArgs{ + owner: env.Spec, + field: "project_environment", + value: &env.Spec.ProjectEnvironment, + mode: TranslateModeFile, + path: structpath.NewPath(resourcePath("jobs", name), "environments", i, "spec", "project_environment"), + }); err != nil { + return err + } + } + } + return nil } diff --git a/bundle/config/mutator/paths/job_paths_visitor_test.go b/bundle/config/mutator/paths/job_paths_visitor_test.go index b9fe29d154e..2d5e0616312 100644 --- a/bundle/config/mutator/paths/job_paths_visitor_test.go +++ b/bundle/config/mutator/paths/job_paths_visitor_test.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" @@ -85,15 +85,15 @@ func TestVisitJobPaths(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.jobs.job0.tasks[0].notebook_task.notebook_path"), - dyn.MustPathFromString("resources.jobs.job0.tasks[1].spark_python_task.python_file"), - dyn.MustPathFromString("resources.jobs.job0.tasks[2].dbt_task.project_directory"), - dyn.MustPathFromString("resources.jobs.job0.tasks[3].sql_task.file.path"), - dyn.MustPathFromString("resources.jobs.job0.tasks[6].libraries[0].requirements"), - dyn.MustPathFromString("resources.jobs.job0.tasks[7].alert_task.workspace_path"), - dyn.MustPathFromString("resources.jobs.job0.tasks[8].ai_runtime_task.deployments[0].command_path"), - } + expected := structpath.MustParsePaths( + "resources.jobs.job0.tasks[0].notebook_task.notebook_path", + "resources.jobs.job0.tasks[1].spark_python_task.python_file", + "resources.jobs.job0.tasks[2].dbt_task.project_directory", + "resources.jobs.job0.tasks[3].sql_task.file.path", + "resources.jobs.job0.tasks[6].libraries[0].requirements", + "resources.jobs.job0.tasks[7].alert_task.workspace_path", + "resources.jobs.job0.tasks[8].ai_runtime_task.deployments[0].command_path", + ) assert.ElementsMatch(t, expected, actual) } @@ -124,10 +124,10 @@ func TestVisitJobPaths_environments(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.jobs.job0.environments[1].spec.project_environment"), - dyn.MustPathFromString("resources.jobs.job0.environments[2].spec.project_environment"), - } + expected := structpath.MustParsePaths( + "resources.jobs.job0.environments[1].spec.project_environment", + "resources.jobs.job0.environments[2].spec.project_environment", + ) assert.ElementsMatch(t, expected, actual) } @@ -156,10 +156,10 @@ func TestVisitJobPaths_environmentVariables(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.jobs.job0.environment_variables[0].spec.files[0]"), - dyn.MustPathFromString("resources.jobs.job0.environment_variables[0].spec.files[1]"), - } + expected := structpath.MustParsePaths( + "resources.jobs.job0.environment_variables[0].spec.files[0]", + "resources.jobs.job0.environment_variables[0].spec.files[1]", + ) assert.ElementsMatch(t, expected, actual) } @@ -199,10 +199,10 @@ func TestVisitJobPaths_foreach(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.jobs.job0.tasks[0].for_each_task.task.notebook_task.notebook_path"), - dyn.MustPathFromString("resources.jobs.job0.tasks[1].for_each_task.task.alert_task.workspace_path"), - } + expected := structpath.MustParsePaths( + "resources.jobs.job0.tasks[0].for_each_task.task.notebook_task.notebook_path", + "resources.jobs.job0.tasks[1].for_each_task.task.alert_task.workspace_path", + ) assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/job_run_paths_visitor.go b/bundle/config/mutator/paths/job_run_paths_visitor.go index 3bc56bacf72..bc5cd3af98f 100644 --- a/bundle/config/mutator/paths/job_run_paths_visitor.go +++ b/bundle/config/mutator/paths/job_run_paths_visitor.go @@ -1,24 +1,32 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" ) // VisitJobRunPaths visits local paths on job_runs so NormalizePaths can rewrite // them relative to the bundle root. Not used by TranslatePaths: hashing still // needs a local glob, not a workspace path. -func VisitJobRunPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("job_runs"), - dyn.AnyKey(), - dyn.Key("lifecycle"), - dyn.Key("triggers"), - dyn.AnyIndex(), - dyn.Key("on_file_change"), - ) - - return dyn.MapByPattern(value, pattern, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { - return fn(path, TranslateModeLocalRelative, value) - }) +func VisitJobRunPaths(root *config.Root, fn VisitFunc) error { + for _, name := range sortedKeys(root.Resources.JobRuns) { + r := root.Resources.JobRuns[name] + if r == nil || r.Lifecycle == nil { + continue + } + for i := range r.Lifecycle.Triggers { + s := r.Lifecycle.Triggers[i].OnFileChange + if s == nil { + continue + } + if err := visit(fn, visitArgs{ + value: s, + mode: TranslateModeLocalRelative, + path: structpath.NewPath(resourcePath("job_runs", name), "lifecycle", "triggers", i, "on_file_change"), + }); err != nil { + return err + } + } + } + return nil } diff --git a/bundle/config/mutator/paths/job_run_paths_visitor_test.go b/bundle/config/mutator/paths/job_run_paths_visitor_test.go index 93df73d3c9e..ac552583a68 100644 --- a/bundle/config/mutator/paths/job_run_paths_visitor_test.go +++ b/bundle/config/mutator/paths/job_run_paths_visitor_test.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" ) @@ -27,9 +27,7 @@ func TestVisitJobRunPaths(t *testing.T) { } actual := collectVisitedPaths(t, root, VisitJobRunPaths) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.job_runs.run0.lifecycle.triggers[0].on_file_change"), - } + expected := structpath.MustParsePaths("resources.job_runs.run0.lifecycle.triggers[0].on_file_change") assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/pipeline_paths_visitor.go b/bundle/config/mutator/paths/pipeline_paths_visitor.go index b5af086dd34..7cb234baf21 100644 --- a/bundle/config/mutator/paths/pipeline_paths_visitor.go +++ b/bundle/config/mutator/paths/pipeline_paths_visitor.go @@ -1,137 +1,106 @@ package paths import ( - "github.com/databricks/cli/bundle/libraries" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/pipelines" ) -type pipelineRewritePattern struct { - pattern dyn.Pattern - mode TranslateMode - - // If function defined in skipRewrite returns true, we skip rewriting the path. - // For example, for environment dependencies, we skip rewriting if the path is not a local library. - skipRewrite func(string) bool -} - -// Base pattern to match all libraries in all pipelines. -var base = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("pipelines"), - dyn.AnyKey(), -) - -func pipelineRewritePatterns() []pipelineRewritePattern { - // Compile list of configuration paths to rewrite. - allPatterns := []pipelineRewritePattern{ - { - pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("notebook"), dyn.Key("path")), - mode: TranslateModeNotebook, - skipRewrite: noSkipRewrite, - }, - { - pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("file"), dyn.Key("path")), - mode: TranslateModeFile, - skipRewrite: noSkipRewrite, - }, - { - pattern: base.Append(dyn.Key("libraries"), dyn.AnyIndex(), dyn.Key("glob"), dyn.Key("include")), - mode: TranslateModeGlob, - skipRewrite: noSkipRewrite, - }, - { - pattern: base.Append(dyn.Key("root_path")), - mode: TranslateModeDirectory, - skipRewrite: noSkipRewrite, - }, - } - - return allPatterns +// pipelineLibraryRule visits one kind of path value of the libraries of a pipeline. +type pipelineLibraryRule struct { + mode TranslateMode + keys []any + field func(*pipelines.PipelineLibrary) (owner any, s *string) } -func pipelineLibrariesRewritePatterns() []pipelineRewritePattern { - pipelineEnvironmentsPatterns := []pipelineRewritePattern{ - { - pattern: dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("pipelines"), - dyn.AnyKey(), - dyn.Key("environment"), - dyn.Key("dependencies"), - dyn.AnyIndex(), - ), - mode: TranslateModeLocalRelativeWithPrefix, - skipRewrite: func(s string) bool { - return !libraries.IsLibraryLocal(s) - }, - }, - } - - pipelineEnvironmentsPatternsWithPipFlags := []pipelineRewritePattern{ - { - dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("pipelines"), - dyn.AnyKey(), - dyn.Key("environment"), - dyn.Key("dependencies"), - dyn.AnyIndex(), - ), - TranslateModeEnvironmentPipFlag, - func(s string) bool { - _, _, ok := libraries.IsLocalPathInPipFlag(s) - return !ok - }, - }, - } - - return append(pipelineEnvironmentsPatterns, pipelineEnvironmentsPatternsWithPipFlags...) +var pipelineLibraryRules = []pipelineLibraryRule{ + {TranslateModeNotebook, []any{"notebook", "path"}, func(l *pipelines.PipelineLibrary) (any, *string) { + if l.Notebook == nil { + return nil, nil + } + return l.Notebook, &l.Notebook.Path + }}, + {TranslateModeFile, []any{"file", "path"}, func(l *pipelines.PipelineLibrary) (any, *string) { + if l.File == nil { + return nil, nil + } + return l.File, &l.File.Path + }}, + {TranslateModeGlob, []any{"glob", "include"}, func(l *pipelines.PipelineLibrary) (any, *string) { + if l.Glob == nil { + return nil, nil + } + return l.Glob, &l.Glob.Include + }}, } -func VisitPipelinePaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - var err error - newValue := value - - for _, rewritePattern := range pipelineRewritePatterns() { - newValue, err = dyn.MapByPattern(newValue, rewritePattern.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - sv, ok := v.AsString() - if !ok { - return v, nil +func VisitPipelinePaths(root *config.Root, fn VisitFunc) error { + for _, rule := range pipelineLibraryRules { + for _, name := range sortedKeys(root.Resources.Pipelines) { + pl := root.Resources.Pipelines[name] + if pl == nil { + continue } - if rewritePattern.skipRewrite(sv) { - return v, nil + for i := range pl.Libraries { + owner, s := rule.field(&pl.Libraries[i]) + if s == nil { + continue + } + if err := visit(fn, visitArgs{ + owner: owner, + field: rule.keys[len(rule.keys)-1].(string), + value: s, + mode: rule.mode, + path: structpath.NewPath(structpath.NewPath(resourcePath("pipelines", name), "libraries", i), rule.keys...), + }); err != nil { + return err + } } - - return fn(p, rewritePattern.mode, v) - }) - if err != nil { - return dyn.InvalidValue, err } } - - return newValue, nil + for _, name := range sortedKeys(root.Resources.Pipelines) { + if pl := root.Resources.Pipelines[name]; pl != nil { + if err := visit(fn, visitArgs{ + owner: pl, + field: "root_path", + value: &pl.RootPath, + mode: TranslateModeDirectory, + path: structpath.NewPath(resourcePath("pipelines", name), "root_path"), + }); err != nil { + return err + } + } + } + return nil } -func VisitPipelineLibrariesPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { - var err error - newValue := value - - for _, rewritePattern := range pipelineLibrariesRewritePatterns() { - newValue, err = dyn.MapByPattern(newValue, rewritePattern.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - sv, ok := v.AsString() - if !ok { - return v, nil +func VisitPipelineLibrariesPaths(root *config.Root, fn VisitFunc) error { + for _, rule := range []struct { + mode TranslateMode + skip func(string) bool + }{ + {TranslateModeLocalRelativeWithPrefix, notLocalLibrary}, + {TranslateModeEnvironmentPipFlag, noLocalPathInPipFlag}, + } { + for _, name := range sortedKeys(root.Resources.Pipelines) { + pl := root.Resources.Pipelines[name] + if pl == nil || pl.Environment == nil { + continue } - if rewritePattern.skipRewrite(sv) { - return v, nil + for i := range pl.Environment.Dependencies { + // Most dependencies are not local paths: check before building the path. + if rule.skip(pl.Environment.Dependencies[i]) { + continue + } + if err := visit(fn, visitArgs{ + value: &pl.Environment.Dependencies[i], + mode: rule.mode, + path: structpath.NewPath(resourcePath("pipelines", name), "environment", "dependencies", i), + }); err != nil { + return err + } } - - return fn(p, rewritePattern.mode, v) - }) - if err != nil { - return dyn.InvalidValue, err } } - - return newValue, nil + return nil } diff --git a/bundle/config/mutator/paths/pipeline_paths_visitor_test.go b/bundle/config/mutator/paths/pipeline_paths_visitor_test.go index eb9748bba37..6a7b2c6183d 100644 --- a/bundle/config/mutator/paths/pipeline_paths_visitor_test.go +++ b/bundle/config/mutator/paths/pipeline_paths_visitor_test.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/assert" ) @@ -47,13 +47,13 @@ func TestVisitPipelinePaths(t *testing.T) { actual := collectVisitedPaths(t, root, VisitPipelinePaths) actual = append(actual, collectVisitedPaths(t, root, VisitPipelineLibrariesPaths)...) - expected := []dyn.Path{ - dyn.MustPathFromString("resources.pipelines.pipeline0.libraries[0].file.path"), - dyn.MustPathFromString("resources.pipelines.pipeline0.libraries[1].notebook.path"), - dyn.MustPathFromString("resources.pipelines.pipeline0.libraries[2].glob.include"), - dyn.MustPathFromString("resources.pipelines.pipeline0.root_path"), - dyn.MustPathFromString("resources.pipelines.pipeline0.environment.dependencies[0]"), - } + expected := structpath.MustParsePaths( + "resources.pipelines.pipeline0.libraries[0].file.path", + "resources.pipelines.pipeline0.libraries[1].notebook.path", + "resources.pipelines.pipeline0.libraries[2].glob.include", + "resources.pipelines.pipeline0.root_path", + "resources.pipelines.pipeline0.environment.dependencies[0]", + ) assert.ElementsMatch(t, expected, actual) } diff --git a/bundle/config/mutator/paths/visitor.go b/bundle/config/mutator/paths/visitor.go index cadc52b75fe..dc3752debe7 100644 --- a/bundle/config/mutator/paths/visitor.go +++ b/bundle/config/mutator/paths/visitor.go @@ -1,14 +1,22 @@ package paths import ( - "github.com/databricks/cli/libs/dyn" + "maps" + "reflect" + "slices" + + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) -type VisitFunc func(path dyn.Path, mode TranslateMode, value dyn.Value) (dyn.Value, error) +// VisitFunc is called for every path value. It may rewrite the value through the +// pointer; the value keeps its locations. +type VisitFunc func(path *structpath.PathNode, mode TranslateMode, value *string) error // VisitPaths visits all paths in bundle configuration -func VisitPaths(root dyn.Value, fn VisitFunc) (dyn.Value, error) { - visitors := []func(dyn.Value, VisitFunc) (dyn.Value, error){ +func VisitPaths(root *config.Root, fn VisitFunc) error { + visitors := []func(*config.Root, VisitFunc) error{ VisitJobPaths, VisitJobRunPaths, VisitJobLibrariesPaths, @@ -21,14 +29,61 @@ func VisitPaths(root dyn.Value, fn VisitFunc) (dyn.Value, error) { VisitPipelineLibrariesPaths, } - newRoot := root for _, visitor := range visitors { - updatedRoot, err := visitor(newRoot, fn) - if err != nil { - return dyn.InvalidValue, err + if err := visitor(root, fn); err != nil { + return err } - newRoot = updatedRoot } - return newRoot, nil + return nil +} + +// visitArgs describes one path value for [visit]. +type visitArgs struct { + // owner is the struct that holds the field, for its ForceSendFields; nil for list + // elements, which are always present. + owner any + + // field is the JSON key of the field in owner. + field string + + // value is the path value; fn may rewrite it. + value *string + + mode TranslateMode + + // path is the path of the value. + path *structpath.PathNode +} + +// present reports whether the path value is present: an empty value is present only if +// it is in the owner's ForceSendFields (as [structvar.View] decides). Callers that visit +// many empty values check it before building the path. +func present(owner any, field string, value *string) bool { + return *value != "" || owner == nil || isForced(owner, field) +} + +// visit calls fn for the path value described by a, unless it is not present. +func visit(fn VisitFunc, a visitArgs) error { + if !present(a.owner, a.field, a.value) { + return nil + } + return fn(a.path, a.mode, a.value) +} + +// isForced reports whether the field key of the struct *owner is in its ForceSendFields. +// It is only called for empty values. +func isForced(owner any, key string) bool { + v := reflect.ValueOf(owner).Elem() + return structvar.GetStructInfo(v.Type()).IsForceSend(v, key) +} + +// sortedKeys returns the keys of a resource map in the order the paths are visited. +func sortedKeys[V any](m map[string]V) []string { + return slices.Sorted(maps.Keys(m)) +} + +// resourcePath returns the path of the resource name of the given type. +func resourcePath(typ, name string) *structpath.PathNode { + return structpath.NewPath(nil, "resources", typ, name) } diff --git a/bundle/config/mutator/paths/visitor_test.go b/bundle/config/mutator/paths/visitor_test.go index 019342404c8..5f9c8dc2af6 100644 --- a/bundle/config/mutator/paths/visitor_test.go +++ b/bundle/config/mutator/paths/visitor_test.go @@ -4,18 +4,16 @@ import ( "testing" "github.com/databricks/cli/bundle/config" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/require" ) // collectVisitedPaths is a helper function that collects all visited paths for testing -func collectVisitedPaths(t *testing.T, root config.Root, visitFn func(value dyn.Value, fn VisitFunc) (dyn.Value, error)) []dyn.Path { - var actual []dyn.Path - err := root.Mutate(func(value dyn.Value) (dyn.Value, error) { - return visitFn(value, func(p dyn.Path, mode TranslateMode, v dyn.Value) (dyn.Value, error) { - actual = append(actual, p) - return v, nil - }) +func collectVisitedPaths(t *testing.T, root config.Root, visitFn func(root *config.Root, fn VisitFunc) error) []*structpath.PathNode { + var actual []*structpath.PathNode + err := visitFn(&root, func(p *structpath.PathNode, mode TranslateMode, s *string) error { + actual = append(actual, p) + return nil }) require.NoError(t, err) return actual diff --git a/bundle/config/mutator/populate_locations.go b/bundle/config/mutator/populate_locations.go index 8f7a1258b1e..31bafec647c 100644 --- a/bundle/config/mutator/populate_locations.go +++ b/bundle/config/mutator/populate_locations.go @@ -2,10 +2,15 @@ package mutator import ( "context" + "fmt" + "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/loctable" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/dynloc" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type populateLocations struct{} @@ -20,9 +25,67 @@ func (m *populateLocations) Name() string { return "PopulateLocations" } +// pathString returns the path with keys separated by dots, whatever characters they contain. +func pathString(p *structpath.PathNode) string { + var b strings.Builder + for i, n := range p.AsSlice() { + if k, ok := n.StringKey(); ok { + if i > 0 { + b.WriteByte('.') + } + b.WriteString(k) + } else if idx, ok := n.Index(); ok { + fmt.Fprintf(&b, "[%d]", idx) + } + } + return b.String() +} + +// gatherLocations returns the locations of the top level fields, the resource groups +// ("resources.jobs"), the resources ("resources.jobs.my_job"), and job tasks +// ("resources.jobs.my_job.tasks", "resources.jobs.my_job.tasks[2]"). Resources and tasks +// come from the configuration, their locations from the locations tree (values without +// locations have no entry). +func gatherLocations(r *config.Root) map[string][]diag.Location { + out := map[string][]diag.Location{} + tree := r.LocationTree() + for k, top := range tree.KeyItems() { + addLocations(out, structpath.NewPath(nil, k), top) + } + groups := tree.Key("resources") + for group, g := range groups.KeyItems() { + addLocations(out, structpath.NewPath(nil, "resources", group), g) + } + r.Resources.IterateAllResources(func(group, name string, _ config.ConfigResource) bool { + rp := structpath.NewPath(nil, "resources", group, name) + res := groups.Key(group).Key(name) + addLocations(out, rp, res) + if group == "jobs" { + if job := r.Resources.Jobs[name]; job != nil { + tasks := res.Key("tasks") + addLocations(out, structpath.NewPath(rp, "tasks"), tasks) + for i := range job.Tasks { + addLocations(out, structpath.NewPath(rp, "tasks", i), tasks.Index(i)) + } + } + } + return true + }) + return out +} + +// addLocations records the locations of the value at p (from its tree l), if any. +func addLocations(out map[string][]diag.Location, p *structpath.PathNode, l *structvar.Locations) { + if locs := l.Get(); len(locs) > 0 { + out[pathString(p)] = locs + } +} + func (m *populateLocations) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - locs, err := dynloc.Build( - b.Config.Value(), + pathToLocations := gatherLocations(&b.Config) + + locs, err := loctable.Build( + pathToLocations, // Make all paths relative to the bundle root. b.BundleRootPath, ) diff --git a/bundle/config/mutator/python/apply_python_output.go b/bundle/config/mutator/python/apply_python_output.go index fb5452d3b4f..33a957f3e48 100644 --- a/bundle/config/mutator/python/apply_python_output.go +++ b/bundle/config/mutator/python/apply_python_output.go @@ -3,10 +3,10 @@ package python import ( "fmt" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/resourcemutator" - - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) // applyPythonOutputResult contains which resources where added, updated, or deleted by Python mutator. @@ -24,162 +24,150 @@ type applyPythonOutputResult struct { // Return value is equivalent to output except for: // - if property is unchanged in output, it's original location will be preserved // - if empty sequence/mapping is deleted in output, it's original value will be preserved -func applyPythonOutput(root, output dyn.Value) (dyn.Value, applyPythonOutputResult, error) { +func applyPythonOutput(root, output *config.Root) (*structvar.OverridePlan, applyPythonOutputResult, error) { result, visitor := createOverrideVisitor(root, output) - merged, err := merge.Override(root, output, visitor) + plan, err := structvar.PlanOverride(root.View(), output.View(), visitor) if err != nil { - return dyn.InvalidValue, result, err + return nil, result, err } - return merged, result, nil + return plan, result, nil } -func createOverrideVisitor(leftRoot, rightRoot dyn.Value) (applyPythonOutputResult, merge.OverrideVisitor) { - resourcesPath := dyn.NewPath(dyn.Key("resources")) +func createOverrideVisitor(leftRoot, rightRoot *config.Root) (applyPythonOutputResult, structvar.OverrideVisitor) { + resourcesPath := structpath.NewPath(nil, "resources") deleted := resourcemutator.NewResourceKeySet() updated := resourcemutator.NewResourceKeySet() added := resourcemutator.NewResourceKeySet() - visitor := merge.OverrideVisitor{ - VisitDelete: func(valuePath dyn.Path, left dyn.Value) error { + visitor := structvar.OverrideVisitor{ + VisitDelete: func(np *structpath.PathNode, left structvar.View) error { if isOmitemptyDelete(left) { - return merge.ErrOverrideUndoDelete + return structvar.ErrOverrideUndoDelete } - if !valuePath.HasPrefix(resourcesPath) { - return fmt.Errorf("unexpected change at %q (delete)", valuePath.String()) + if !np.HasPrefix(resourcesPath) { + return fmt.Errorf("unexpected change at %q (delete)", np.String()) } // use leftRoot below because it contains deleted resources - if len(valuePath) == 1 { + if np.Len() == 1 { // Example: // // valuePath: "resources" // leftRoot: {"bundle": ..., "resources": ...}, // rightRoot: {"bundle": ...} - return deleted.AddPattern( - dyn.NewPatternFromPath(valuePath).Append(dyn.AnyKey(), dyn.AnyKey()), - leftRoot, - ) - } else if len(valuePath) == 2 { + deleted.AddResources(&leftRoot.Resources) + return nil + } else if np.Len() == 2 { // Example: // // valuePath: "resources.jobs" // leftRoot: {"resources": { "jobs": ..., "pipeline": ...}}}, // rightRoot: {"resources": { "jobs": ...}}}, - return deleted.AddPattern( - dyn.NewPatternFromPath(valuePath).Append(dyn.AnyKey()), - leftRoot, - ) - } else if len(valuePath) == 3 { + deleted.AddResourcesOfType(&leftRoot.Resources, np.KeyAt(1)) + return nil + } else if np.Len() == 3 { // Example: "resources.jobs.job_0" - resourceKey := resourcemutator.ResourceKey{Type: valuePath[1].Key(), Name: valuePath[2].Key()} + resourceKey := resourcemutator.ResourceKey{Type: np.KeyAt(1), Name: np.KeyAt(2)} deleted.AddResourceKey(resourceKey) return nil } else { // Example: "resources.jobs.job_0.tags" - resourceKey := resourcemutator.ResourceKey{Type: valuePath[1].Key(), Name: valuePath[2].Key()} + resourceKey := resourcemutator.ResourceKey{Type: np.KeyAt(1), Name: np.KeyAt(2)} updated.AddResourceKey(resourceKey) return nil } }, - VisitInsert: func(valuePath dyn.Path, right dyn.Value) (dyn.Value, error) { - if !valuePath.HasPrefix(resourcesPath) { - return dyn.InvalidValue, fmt.Errorf("unexpected change at %q (insert)", valuePath.String()) + VisitInsert: func(np *structpath.PathNode, right structvar.View) error { + if !np.HasPrefix(resourcesPath) { + return fmt.Errorf("unexpected change at %q (insert)", np.String()) } // use rightRoot below because it contains result - if len(valuePath) == 1 { + if np.Len() == 1 { // Example: // // valuePath: "resources" // leftRoot: {"bundle": ..., } // rightRoot: {"bundle": ..., "resources": {...} } - return right, added.AddPattern( - dyn.NewPatternFromPath(valuePath).Append(dyn.AnyKey(), dyn.AnyKey()), - rightRoot, - ) - } else if len(valuePath) == 2 { + added.AddResources(&rightRoot.Resources) + return nil + } else if np.Len() == 2 { // Example: // // valuePath: "resources.jobs" // leftRoot: {"resources": { }} // rightRoot: {"resources": { "jobs": {...} }} - return right, added.AddPattern( - dyn.NewPatternFromPath(valuePath).Append(dyn.AnyKey()), - rightRoot, - ) - } else if len(valuePath) == 3 { + added.AddResourcesOfType(&rightRoot.Resources, np.KeyAt(1)) + return nil + } else if np.Len() == 3 { // Example: // // valuePath: "resources.jobs" // leftRoot: {"resources": { "jobs": { }}} // rightRoot: {"resources": { "jobs": {"job_0": {...} }}} - resourceKey := resourcemutator.ResourceKey{Type: valuePath[1].Key(), Name: valuePath[2].Key()} + resourceKey := resourcemutator.ResourceKey{Type: np.KeyAt(1), Name: np.KeyAt(2)} added.AddResourceKey(resourceKey) - return right, nil + return nil } else { // Example: "resources.jobs.job_0.email_notifications" - resourceKey := resourcemutator.ResourceKey{Type: valuePath[1].Key(), Name: valuePath[2].Key()} + resourceKey := resourcemutator.ResourceKey{Type: np.KeyAt(1), Name: np.KeyAt(2)} updated.AddResourceKey(resourceKey) - return right, nil + return nil } }, - VisitUpdate: func(valuePath dyn.Path, _, right dyn.Value) (dyn.Value, error) { - if !valuePath.HasPrefix(resourcesPath) { - return dyn.InvalidValue, fmt.Errorf("unexpected change at %q (update)", valuePath.String()) + VisitUpdate: func(np *structpath.PathNode, _, right structvar.View) error { + if !np.HasPrefix(resourcesPath) { + return fmt.Errorf("unexpected change at %q (update)", np.String()) } // use rightRoot below because it contains result - if len(valuePath) == 1 { + if np.Len() == 1 { // Example: // // valuePath: "resources" // leftRoot: {"bundle": ..., "resources": null } // rightRoot: {"bundle": ..., "resources": {...} } - return right, added.AddPattern( - dyn.NewPatternFromPath(valuePath).Append(dyn.AnyKey(), dyn.AnyKey()), - rightRoot, - ) - } else if len(valuePath) == 2 { + added.AddResources(&rightRoot.Resources) + return nil + } else if np.Len() == 2 { // Example: // // valuePath: "resources.jobs" // leftRoot: {"resources": { "jobs": null }} // rightRoot: {"resources": { "jobs": {...} }} - return right, added.AddPattern( - dyn.NewPatternFromPath(valuePath).Append(dyn.AnyKey()), - rightRoot, - ) - } else if len(valuePath) == 3 { + added.AddResourcesOfType(&rightRoot.Resources, np.KeyAt(1)) + return nil + } else if np.Len() == 3 { // Example: // // valuePath: "resources.jobs.job_0" // leftRoot: {"resources": { "jobs": {"job_0": null }}} // rightRoot: {"resources": { "jobs": {"job_0": {...} }}} - resourceKey := resourcemutator.ResourceKey{Type: valuePath[1].Key(), Name: valuePath[2].Key()} + resourceKey := resourcemutator.ResourceKey{Type: np.KeyAt(1), Name: np.KeyAt(2)} added.AddResourceKey(resourceKey) - return right, nil + return nil } else { // Example: "resources.jobs.job_0.name" - resourceKey := resourcemutator.ResourceKey{Type: valuePath[1].Key(), Name: valuePath[2].Key()} + resourceKey := resourcemutator.ResourceKey{Type: np.KeyAt(1), Name: np.KeyAt(2)} updated.AddResourceKey(resourceKey) - return right, nil + return nil } }, } @@ -191,19 +179,25 @@ func createOverrideVisitor(leftRoot, rightRoot dyn.Value) (applyPythonOutputResu }, visitor } -func isOmitemptyDelete(left dyn.Value) bool { +func isOmitemptyDelete(left structvar.View) bool { // Python output can omit empty sequences/mappings, because we don't track them as optional, // there is no semantic difference between empty and missing, so we keep them as they were before // Python mutator deleted them. switch left.Kind() { - case dyn.KindMap: - return left.MustMap().Len() == 0 + case structvar.KindMap: + for range left.MapItems() { + return false + } + return true - case dyn.KindSequence: - return len(left.MustSequence()) == 0 + case structvar.KindSequence: + for range left.Sequence() { + return false + } + return true - case dyn.KindNil: + case structvar.KindNil: // map/sequence can be nil, for instance, bad YAML like: `foo:` return true diff --git a/bundle/config/mutator/python/apply_python_output_test.go b/bundle/config/mutator/python/apply_python_output_test.go index 94d7e7c39cc..bd27baf8eec 100644 --- a/bundle/config/mutator/python/apply_python_output_test.go +++ b/bundle/config/mutator/python/apply_python_output_test.go @@ -1,22 +1,24 @@ package python import ( + "encoding/json" "testing" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/resourcemutator" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" - "github.com/databricks/cli/libs/dyn" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type applyPythonOutputTestCase struct { name string - input dyn.Value - output dyn.Value + input any + output any added []resourcemutator.ResourceKey updated []resourcemutator.ResourceKey @@ -24,8 +26,8 @@ type applyPythonOutputTestCase struct { } func TestApplyPythonOutput(t *testing.T) { - job1 := mapOf("name", dyn.V("job 1")) - job2 := mapOf("name", dyn.V("job 2")) + job1 := mapOf("name", "job 1") + job2 := mapOf("name", "job 2") testCases := []applyPythonOutputTestCase{ { @@ -130,10 +132,10 @@ func TestApplyPythonOutput(t *testing.T) { { name: "update job through 'description' insert", input: mapOf("resources", mapOf("jobs", - mapOf("job_1", mapOf("name", dyn.V("name"))), + mapOf("job_1", mapOf("name", "name")), )), output: mapOf("resources", mapOf("jobs", - mapOf("job_1", mapOf2("name", dyn.V("name"), "description", dyn.V("description"))), + mapOf("job_1", mapOf2("name", "name", "description", "description")), )), updated: []resourcemutator.ResourceKey{ { @@ -146,10 +148,13 @@ func TestApplyPythonOutput(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - merged, state, err := applyPythonOutput(tc.input, tc.output) + input := loadRoot(t, tc.input) + output := loadRoot(t, tc.output) + plan, state, err := applyPythonOutput(input, output) + require.NoError(t, err) - assert.NoError(t, err) - assert.Equal(t, tc.output, merged) + require.NoError(t, input.Override(plan)) + assert.Equal(t, output.View().AsAny(), input.View().AsAny()) assert.ElementsMatch(t, tc.added, state.AddedResources.ToArray()) assert.ElementsMatch(t, tc.updated, state.UpdatedResources.ToArray()) @@ -159,36 +164,45 @@ func TestApplyPythonOutput(t *testing.T) { } func TestMergeOutput_disallowDelete(t *testing.T) { - input := mapOf("not_resource", dyn.V("value")) - output := emptyMap() + input := loadRoot(t, mapOf("bundle", mapOf("name", "value"))) + output := loadRoot(t, emptyMap()) _, _, err := applyPythonOutput(input, output) - assert.EqualError(t, err, `unexpected change at "not_resource" (delete)`) + assert.EqualError(t, err, `unexpected change at "bundle" (delete)`) } func TestMergeOutput_disallowInsert(t *testing.T) { - output := mapOf("not_resource", dyn.V("value")) - input := emptyMap() + output := loadRoot(t, mapOf("bundle", mapOf("name", "value"))) + input := loadRoot(t, emptyMap()) _, _, err := applyPythonOutput(input, output) - assert.EqualError(t, err, `unexpected change at "not_resource" (insert)`) + assert.EqualError(t, err, `unexpected change at "bundle" (insert)`) } func TestMergeOutput_disallowUpdate(t *testing.T) { - output := mapOf("not_resource", dyn.V("value")) - input := mapOf("not_resource", dyn.V("new value")) + output := loadRoot(t, mapOf("bundle", mapOf("name", "value"))) + input := loadRoot(t, mapOf("bundle", mapOf("name", "new value"))) _, _, err := applyPythonOutput(input, output) - assert.EqualError(t, err, `unexpected change at "not_resource" (update)`) + assert.EqualError(t, err, `unexpected change at "bundle.name" (update)`) +} + +// loadRoot returns the configuration for the configuration tree v. +func loadRoot(t *testing.T, v any) *config.Root { + raw, err := json.Marshal(v) + require.NoError(t, err) + r, diags := config.LoadFromBytes("output.json", raw) + require.NoError(t, diags.Error()) + return r } type overrideVisitorOmitemptyTestCase struct { name string - path dyn.Path - left dyn.Value + path *structpath.PathNode + left any expectedErr error } @@ -197,76 +211,70 @@ func TestCreateOverrideVisitor_omitempty(t *testing.T) { // there is no semantic difference between empty and missing, so we keep them as they were before // Python code deleted them. - location := diag.Location{ - File: "databricks.yml", - Line: 10, - Column: 20, - } - testCases := []overrideVisitorOmitemptyTestCase{ { name: "undo delete of empty variables", - path: dyn.MustPathFromString("variables"), - left: dyn.NewValue([]dyn.Value{}, []diag.Location{location}), - expectedErr: merge.ErrOverrideUndoDelete, + path: structpath.MustParsePath("variables"), + left: &[]string{}, + expectedErr: structvar.ErrOverrideUndoDelete, }, { name: "undo delete of empty job clusters", - path: dyn.MustPathFromString("resources.jobs.job0.job_clusters"), - left: dyn.NewValue([]dyn.Value{}, []diag.Location{location}), - expectedErr: merge.ErrOverrideUndoDelete, + path: structpath.MustParsePath("resources.jobs.job0.job_clusters"), + left: &[]string{}, + expectedErr: structvar.ErrOverrideUndoDelete, }, { name: "allow delete of non-empty job clusters", - path: dyn.MustPathFromString("resources.jobs.job0.job_clusters"), - left: dyn.NewValue([]dyn.Value{dyn.NewValue("abc", []diag.Location{location})}, []diag.Location{location}), + path: structpath.MustParsePath("resources.jobs.job0.job_clusters"), + left: &[]string{"abc"}, expectedErr: nil, }, { name: "undo delete of empty tags", - path: dyn.MustPathFromString("resources.jobs.job0.tags"), - left: dyn.NewValue(map[string]dyn.Value{}, []diag.Location{location}), - expectedErr: merge.ErrOverrideUndoDelete, + path: structpath.MustParsePath("resources.jobs.job0.tags"), + left: &map[string]string{}, + expectedErr: structvar.ErrOverrideUndoDelete, }, { name: "allow delete of non-empty tags", - path: dyn.MustPathFromString("resources.jobs.job0.tags"), - left: dyn.NewValue(map[string]dyn.Value{"dev": dyn.NewValue("true", []diag.Location{location})}, []diag.Location{location}), + path: structpath.MustParsePath("resources.jobs.job0.tags"), + left: &map[string]string{"dev": "true"}, expectedErr: nil, }, { name: "undo delete of nil", - path: dyn.MustPathFromString("resources.jobs.job0.tags"), - left: dyn.NilValue.WithLocations([]diag.Location{location}), - expectedErr: merge.ErrOverrideUndoDelete, + path: structpath.MustParsePath("resources.jobs.job0.tags"), + left: (*map[string]string)(nil), + expectedErr: structvar.ErrOverrideUndoDelete, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - _, visitor := createOverrideVisitor(dyn.NilValue, dyn.NilValue) + _, visitor := createOverrideVisitor(nil, nil) - err := visitor.VisitDelete(tc.path, tc.left) + err := visitor.VisitDelete(tc.path, structvar.NewView(tc.left, nil, nil)) assert.Equal(t, tc.expectedErr, err) }) } } -func mapOf(key string, value dyn.Value) dyn.Value { - return dyn.V(map[string]dyn.Value{ +func mapOf(key string, value any) any { + return map[string]any{ key: value, - }) + } } -func mapOf2(key1 string, value1 dyn.Value, key2 string, value2 dyn.Value) dyn.Value { - return dyn.V(map[string]dyn.Value{ +func mapOf2(key1 string, value1 any, key2 string, value2 any) any { + return map[string]any{ key1: value1, key2: value2, - }) + } } -func emptyMap() dyn.Value { - return dyn.V(map[string]dyn.Value{}) +func emptyMap() any { + return map[string]any{} } diff --git a/bundle/config/mutator/python/python_diagnostics.go b/bundle/config/mutator/python/python_diagnostics.go index 3006ee8321b..9c01a9b349d 100644 --- a/bundle/config/mutator/python/python_diagnostics.go +++ b/bundle/config/mutator/python/python_diagnostics.go @@ -6,7 +6,6 @@ import ( "io" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" ) @@ -58,7 +57,7 @@ func parsePythonDiagnostics(input io.Reader) (diag.Diagnostics, error) { } var paths []*structpath.PathNode if path != nil { - paths = dyn.ToStructPaths(path) + paths = []*structpath.PathNode{path} } var locations []diag.Location @@ -81,12 +80,12 @@ func parsePythonDiagnostics(input io.Reader) (diag.Diagnostics, error) { return diags, nil } -func convertPythonPath(path string) (dyn.Path, error) { +func convertPythonPath(path string) (*structpath.PathNode, error) { if path == "" { return nil, nil } - return dyn.NewPathFromString(path) + return structpath.ParsePath(path) } func convertPythonSeverity(severity pythonSeverity) (diag.Severity, error) { diff --git a/bundle/config/mutator/python/python_locations.go b/bundle/config/mutator/python/python_locations.go index bb301da9755..ab4d319c4b7 100644 --- a/bundle/config/mutator/python/python_locations.go +++ b/bundle/config/mutator/python/python_locations.go @@ -8,7 +8,8 @@ import ( "path/filepath" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) // generatedFileName is used as the virtual file name for YAML generated by Python code. @@ -19,7 +20,7 @@ const generatedFileName = "__generated_by_python__.yml" // pythonLocations is data structure for efficient location lookup for a given path // -// Locations form a tree, and we assign locations of the closest ancestor to each dyn.Value based on its path. +// Locations form a tree, and we assign locations of the closest ancestor to each value based on its path. // We implement it as a trie (prefix tree) where keys are components of the path. With that, lookups are O(n) // where n is the number of components in the path. // @@ -57,15 +58,16 @@ type pythonLocationEntry struct { Column int `json:"column"` } -// mergePythonLocations applies locations from Python mutator into given dyn.Value +// mergePythonLocations returns a location mapper that applies locations from the +// Python mutator. // // The primary use-case is to merge locations.json with output.json, so that any // validation errors will point to Python source code instead of generated YAML. -func mergePythonLocations(value dyn.Value, locations *pythonLocations) (dyn.Value, error) { - return dyn.Walk(value, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { +func mergePythonLocations(locations *pythonLocations) structvar.LocationMapper { + return func(path *structpath.PathNode, locs []diag.Location) []diag.Location { newLocation, ok := findPythonLocation(locations, path) if !ok { - return value, nil + return locs } // The first item in the list is the "last" location used for error reporting @@ -75,13 +77,8 @@ func mergePythonLocations(value dyn.Value, locations *pythonLocations) (dyn.Valu // // We preserve any previous non-virtual locations in case when Python function modified // resource defined in YAML. - newLocations := append( - []diag.Location{newLocation}, - removeVirtualLocations(value.Locations())..., - ) - - return value.WithLocations(newLocations), nil - }) + return append([]diag.Location{newLocation}, removeVirtualLocations(locs)...) + } } func removeVirtualLocations(locations []diag.Location) []diag.Location { @@ -113,7 +110,7 @@ func parsePythonLocations(bundleRoot string, input io.Reader) (*pythonLocations, return nil, fmt.Errorf("failed to parse python location: %w", err) } - path, err := dyn.NewPathFromString(entry.Path) + path, err := structpath.ParsePath(entry.Path) if err != nil { return nil, fmt.Errorf("failed to parse python location: %w", err) } @@ -137,18 +134,18 @@ func parsePythonLocations(bundleRoot string, input io.Reader) (*pythonLocations, } // putPythonLocation puts the location to the trie for the given path -func putPythonLocation(trie *pythonLocations, path dyn.Path, location diag.Location) { +func putPythonLocation(trie *pythonLocations, path *structpath.PathNode, location diag.Location) { currentNode := trie - for _, component := range path { - if key := component.Key(); key != "" { + for _, component := range path.AsSlice() { + if key, ok := component.StringKey(); ok { if _, ok := currentNode.keys[key]; !ok { currentNode.keys[key] = newPythonLocations() } currentNode = currentNode.keys[key] } else { - index := component.Index() + index, _ := component.Index() if _, ok := currentNode.indexes[index]; !ok { currentNode.indexes[index] = newPythonLocations() } @@ -171,20 +168,20 @@ func newPythonLocations() *pythonLocations { // findPythonLocation finds the location or closest ancestor location in the trie for the given path // if no ancestor or exact location is found, false is returned. -func findPythonLocation(locations *pythonLocations, path dyn.Path) (diag.Location, bool) { +func findPythonLocation(locations *pythonLocations, path *structpath.PathNode) (diag.Location, bool) { currentNode := locations lastLocation := locations.location exists := locations.exists - for _, component := range path { - if key := component.Key(); key != "" { + for _, component := range path.AsSlice() { + if key, ok := component.StringKey(); ok { if _, ok := currentNode.keys[key]; !ok { break } currentNode = currentNode.keys[key] } else { - index := component.Index() + index, _ := component.Index() if _, ok := currentNode.indexes[index]; !ok { break } diff --git a/bundle/config/mutator/python/python_locations_test.go b/bundle/config/mutator/python/python_locations_test.go index 2bc815e7cdc..7db88971064 100644 --- a/bundle/config/mutator/python/python_locations_test.go +++ b/bundle/config/mutator/python/python_locations_test.go @@ -6,8 +6,7 @@ import ( "testing" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynassert" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -18,42 +17,25 @@ func TestMergeLocations(t *testing.T) { yamlLocation := diag.Location{File: "foo.yml", Line: 1, Column: 1} locations := newPythonLocations() - putPythonLocation(locations, dyn.MustPathFromString("foo"), pythonLocation) - - input := dyn.NewValue( - map[string]dyn.Value{ - "foo": dyn.V( - map[string]dyn.Value{ - "baz": dyn.NewValue("baz", []diag.Location{yamlLocation}), - "qux": dyn.NewValue("baz", []diag.Location{generatedLocation, yamlLocation}), - }, - ), - "bar": dyn.NewValue("baz", []diag.Location{generatedLocation}), - }, - []diag.Location{yamlLocation}, - ) - - expected := dyn.NewValue( - map[string]dyn.Value{ - "foo": dyn.NewValue( - map[string]dyn.Value{ - // pythonLocation is appended to the beginning of the list if absent - "baz": dyn.NewValue("baz", []diag.Location{pythonLocation, yamlLocation}), - // generatedLocation is replaced by pythonLocation - "qux": dyn.NewValue("baz", []diag.Location{pythonLocation, yamlLocation}), - }, - []diag.Location{pythonLocation}, - ), - // if location is unknown, we keep it as-is - "bar": dyn.NewValue("baz", []diag.Location{generatedLocation}), - }, - []diag.Location{yamlLocation}, - ) - - actual, err := mergePythonLocations(input, locations) + putPythonLocation(locations, structpath.MustParsePath("foo"), pythonLocation) - assert.NoError(t, err) - dynassert.Equal(t, expected, actual) + mapper := mergePythonLocations(locations) + at := func(path string, locs ...diag.Location) []diag.Location { + var p *structpath.PathNode + if path != "" { + p = structpath.MustParsePath(path) + } + return mapper(p, locs) + } + + // pythonLocation is prepended if absent + assert.Equal(t, []diag.Location{pythonLocation, yamlLocation}, at("foo.baz", yamlLocation)) + // generatedLocation is replaced by pythonLocation + assert.Equal(t, []diag.Location{pythonLocation, yamlLocation}, at("foo.qux", generatedLocation, yamlLocation)) + assert.Equal(t, []diag.Location{pythonLocation}, at("foo")) + // if location is unknown, we keep it as-is + assert.Equal(t, []diag.Location{generatedLocation}, at("bar", generatedLocation)) + assert.Equal(t, []diag.Location{yamlLocation}, at("", yamlLocation)) } func TestFindLocation(t *testing.T) { @@ -61,10 +43,10 @@ func TestFindLocation(t *testing.T) { location1 := diag.Location{File: "foo.py", Line: 2, Column: 1} locations := newPythonLocations() - putPythonLocation(locations, dyn.MustPathFromString("foo"), location0) - putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1) + putPythonLocation(locations, structpath.MustParsePath("foo"), location0) + putPythonLocation(locations, structpath.MustParsePath("foo.bar"), location1) - actual, exists := findPythonLocation(locations, dyn.MustPathFromString("foo.bar")) + actual, exists := findPythonLocation(locations, structpath.MustParsePath("foo.bar")) assert.True(t, exists) assert.Equal(t, location1, actual) @@ -76,11 +58,11 @@ func TestFindLocation_indexPathComponent(t *testing.T) { location2 := diag.Location{File: "foo.py", Line: 3, Column: 1} locations := newPythonLocations() - putPythonLocation(locations, dyn.MustPathFromString("foo"), location0) - putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1) - putPythonLocation(locations, dyn.MustPathFromString("foo.bar[0]"), location2) + putPythonLocation(locations, structpath.MustParsePath("foo"), location0) + putPythonLocation(locations, structpath.MustParsePath("foo.bar"), location1) + putPythonLocation(locations, structpath.MustParsePath("foo.bar[0]"), location2) - actual, exists := findPythonLocation(locations, dyn.MustPathFromString("foo.bar[0]")) + actual, exists := findPythonLocation(locations, structpath.MustParsePath("foo.bar[0]")) assert.True(t, exists) assert.Equal(t, location2, actual) @@ -91,10 +73,10 @@ func TestFindLocation_closestAncestorLocation(t *testing.T) { location1 := diag.Location{File: "foo.py", Line: 2, Column: 1} locations := newPythonLocations() - putPythonLocation(locations, dyn.MustPathFromString("foo"), location0) - putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1) + putPythonLocation(locations, structpath.MustParsePath("foo"), location0) + putPythonLocation(locations, structpath.MustParsePath("foo.bar"), location1) - actual, exists := findPythonLocation(locations, dyn.MustPathFromString("foo.bar.baz")) + actual, exists := findPythonLocation(locations, structpath.MustParsePath("foo.bar.baz")) assert.True(t, exists) assert.Equal(t, location1, actual) @@ -105,10 +87,10 @@ func TestFindLocation_unknownLocation(t *testing.T) { location1 := diag.Location{File: "foo.py", Line: 2, Column: 1} locations := newPythonLocations() - putPythonLocation(locations, dyn.MustPathFromString("foo"), location0) - putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1) + putPythonLocation(locations, structpath.MustParsePath("foo"), location0) + putPythonLocation(locations, structpath.MustParsePath("foo.bar"), location1) - _, exists := findPythonLocation(locations, dyn.MustPathFromString("bar")) + _, exists := findPythonLocation(locations, structpath.MustParsePath("bar")) assert.False(t, exists) } @@ -137,21 +119,18 @@ func TestLoadOutput(t *testing.T) { locations := newPythonLocations() putPythonLocation( locations, - dyn.MustPathFromString("resources.jobs.my_job"), + structpath.MustParsePath("resources.jobs.my_job"), location, ) - value, diags := loadOutput( + root, diags := loadOutput( bundleRoot, bytes.NewReader([]byte(output)), locations, ) assert.Equal(t, diag.Diagnostics{}, diags) - - name, err := dyn.Get(value, "resources.jobs.my_job.name") - require.NoError(t, err) - require.Equal(t, []diag.Location{location}, name.Locations()) + require.Equal(t, []diag.Location{location}, root.GetLocationsOfExact(structpath.MustParsePath("resources.jobs.my_job.name"))) } func TestParsePythonLocations_absolutePath(t *testing.T) { diff --git a/bundle/config/mutator/python/python_mutator.go b/bundle/config/mutator/python/python_mutator.go index a6dbdfb8fd4..7998585593f 100644 --- a/bundle/config/mutator/python/python_mutator.go +++ b/bundle/config/mutator/python/python_mutator.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "reflect" + "slices" "strings" "github.com/databricks/cli/bundle/config/mutator/resourcemutator" @@ -28,10 +29,9 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/databricks/cli/libs/process" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/cli/libs/telemetry/protos" ) @@ -188,43 +188,29 @@ func pythonConfigSection(b *bundle.Bundle) protos.PydabsConfigSection { // // Later version of Python package should reject 'experimental.python' unless 'python' section // is set to equivalent value, and after that reject 'experimental.python' altogether. -func applyBackwardsCompatibilityFixes(b *bundle.Bundle) error { - return b.Config.Mutate(func(value dyn.Value) (dyn.Value, error) { - outValue := value - - pythonValue, _ := dyn.Get(outValue, "python") - if !pythonValue.IsValid() { - // if 'python' section doesn't exist, nothing to do - return value, nil - } - - experimentalPythonValue, _ := dyn.Get(outValue, "experimental.python") - - if experimentalPythonValue.IsValid() { - // if 'experimental.python' section exists, nothing to do - return value, nil - } - - experimentalValue, _ := dyn.Get(outValue, "experimental") - if !experimentalValue.IsValid() { - updated, err := dyn.Set(outValue, "experimental", dyn.NewValue(map[string]dyn.Value{}, nil)) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to create 'experimental' section: %w", err) - } else { - outValue = updated - } - } +func applyBackwardsCompatibilityFixes(b *bundle.Bundle) { + // if 'python' section doesn't exist, nothing to do + if reflect.DeepEqual(b.Config.Python, config.Python{}) { + return + } - // move 'python' section to 'experimental.python' - updated, err := dyn.Set(outValue, "experimental.python", pythonValue) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to set 'experimental.python' section: %w", err) - } else { - outValue = updated - } + // if 'experimental.python' section exists, nothing to do + experimental := b.Config.Experimental + if experimental != nil && !reflect.DeepEqual(experimental.Python, config.Python{}) { + return + } - return outValue, nil - }) + // copy 'python' section to 'experimental.python' (creating 'experimental' if needed) + if experimental == nil { + experimental = &config.Experimental{} + b.Config.Experimental = experimental + } + experimental.Python = config.Python{ + Resources: slices.Clone(b.Config.Python.Resources), + Mutators: slices.Clone(b.Config.Python.Mutators), + VEnvPath: b.Config.Python.VEnvPath, + } + b.Config.CopyMetadataAt(structpath.NewStringKey(nil, "python"), structpath.NewPath(nil, "experimental", "python")) } func (m *pythonMutator) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { @@ -234,10 +220,7 @@ func (m *pythonMutator) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagno b.Metrics.PythonConfigSection = pythonConfigSection(b) } - err := applyBackwardsCompatibilityFixes(b) - if err != nil { - return diag.FromErr(fmt.Errorf("failed to apply backwards compatibility fixes: %w", err)) - } + applyBackwardsCompatibilityFixes(b) opts, err := getOpts(b, m.phase) if err != nil { @@ -262,93 +245,82 @@ func (m *pythonMutator) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagno return diag.FromErr(err) } - // mutateDiags is used because Mutate returns 'error' instead of 'diag.Diagnostics' - var mutateDiags diag.Diagnostics - var result applyPythonOutputResult - mutateDiagsHasError := errors.New("unexpected error") - - err = b.Config.Mutate(func(leftRoot dyn.Value) (dyn.Value, error) { - pythonPath, err := detectExecutable(ctx, opts.venvPath) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to get Python interpreter path: %w", err) - } + result, diags := m.run(ctx, b, opts, authEnv) + // we can precisely track resources that are added/updated, so sum doesn't double-count + b.Metrics.PythonUpdatedResources = addResourceCounts(b.Metrics.PythonUpdatedResources, result.UpdatedResources) + b.Metrics.PythonAddedResources = addResourceCounts(b.Metrics.PythonAddedResources, result.AddedResources) - cacheDir, cleanup, err := createCacheDir(ctx) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to create cache dir: %w", err) - } - defer cleanup() - - rightRoot, diags := m.runPythonMutator(ctx, leftRoot, runPythonMutatorOpts{ - cacheDir: cacheDir, - bundleRootPath: b.BundleRootPath, - pythonPath: pythonPath, - loadLocations: opts.loadLocations, - authEnv: authEnv, - }) - mutateDiags = diags - if diags.HasError() { - return dyn.InvalidValue, mutateDiagsHasError - } + if diags.HasError() { + return diags + } - newRoot, result0, err := applyPythonOutput(leftRoot, rightRoot) - result = result0 - if err != nil { - return dyn.InvalidValue, fmt.Errorf("internal error when merging output of Python mutator: %w", err) - } + resourcemutator.NormalizeAndInitializeResources(ctx, b, result.AddedResources) + if logdiag.HasError(ctx) { + return diags + } - for _, resourceKey := range result.AddedResources.ToArray() { - log.Debugf(ctx, "added resource at 'resources.%s.%s'", resourceKey.Type, resourceKey.Name) - } + resourcemutator.NormalizeResources(ctx, b, result.UpdatedResources) + return diags +} - for _, resourceKey := range result.UpdatedResources.ToArray() { - log.Debugf(ctx, "updated resource at 'resources.%s.%s'", resourceKey.Type, resourceKey.Name) - } +// run runs the Python mutator and replaces the configuration with its output. +// The configuration is left unchanged if there are errors. +func (m *pythonMutator) run(ctx context.Context, b *bundle.Bundle, opts opts, authEnv map[string]string) (applyPythonOutputResult, diag.Diagnostics) { + var result applyPythonOutputResult - for _, resourceKey := range result.DeletedResources.ToArray() { - log.Debugf(ctx, "deleted resource at 'resources.%s.%s'", resourceKey.Type, resourceKey.Name) - } + pythonPath, err := detectExecutable(ctx, opts.venvPath) + if err != nil { + return result, diag.FromErr(fmt.Errorf("failed to get Python interpreter path: %w", err)) + } - if !result.DeletedResources.IsEmpty() { - return dyn.InvalidValue, fmt.Errorf("unexpected deleted resources: %s", result.DeletedResources.ToArray()) - } + cacheDir, cleanup, err := createCacheDir(ctx) + if err != nil { + return result, diag.FromErr(fmt.Errorf("failed to create cache dir: %w", err)) + } + defer cleanup() - if !result.AddedResources.IsEmpty() && m.phase == PythonMutatorPhaseApplyMutators { - return dyn.InvalidValue, fmt.Errorf("unexpected added resources: %s", result.AddedResources.ToArray()) - } + leftRoot := b.Config.View() + rightRoot, diags := m.runPythonMutator(ctx, leftRoot, runPythonMutatorOpts{ + cacheDir: cacheDir, + bundleRootPath: b.BundleRootPath, + pythonPath: pythonPath, + loadLocations: opts.loadLocations, + authEnv: authEnv, + }) + if diags.HasError() { + return result, diags + } - if !result.UpdatedResources.IsEmpty() && m.phase == PythonMutatorPhaseLoadResources { - return dyn.InvalidValue, fmt.Errorf("unexpected updated resources: %s", result.UpdatedResources.ToArray()) - } + plan, result, err := applyPythonOutput(&b.Config, rightRoot) + if err != nil { + return result, diags.Extend(diag.FromErr(fmt.Errorf("internal error when merging output of Python mutator: %w", err))) + } - return newRoot, nil - }) + for _, resourceKey := range result.AddedResources.ToArray() { + log.Debugf(ctx, "added resource at 'resources.%s.%s'", resourceKey.Type, resourceKey.Name) + } - // we can precisely track resources that are added/updated, so sum doesn't double-count - b.Metrics.PythonUpdatedResources = addResourceCounts(b.Metrics.PythonUpdatedResources, result.UpdatedResources) - b.Metrics.PythonAddedResources = addResourceCounts(b.Metrics.PythonAddedResources, result.AddedResources) + for _, resourceKey := range result.UpdatedResources.ToArray() { + log.Debugf(ctx, "updated resource at 'resources.%s.%s'", resourceKey.Type, resourceKey.Name) + } - if errors.Is(err, mutateDiagsHasError) { - if !mutateDiags.HasError() { - panic("mutateDiags has no error, but error is expected") - } + for _, resourceKey := range result.DeletedResources.ToArray() { + log.Debugf(ctx, "deleted resource at 'resources.%s.%s'", resourceKey.Type, resourceKey.Name) + } - return mutateDiags - } else { - mutateDiags = mutateDiags.Extend(diag.FromErr(err)) + if !result.DeletedResources.IsEmpty() { + return result, diags.Extend(diag.Errorf("unexpected deleted resources: %s", result.DeletedResources.ToArray())) } - if mutateDiags.HasError() { - return mutateDiags + if !result.AddedResources.IsEmpty() && m.phase == PythonMutatorPhaseApplyMutators { + return result, diags.Extend(diag.Errorf("unexpected added resources: %s", result.AddedResources.ToArray())) } - resourcemutator.NormalizeAndInitializeResources(ctx, b, result.AddedResources) - if logdiag.HasError(ctx) { - return mutateDiags + if !result.UpdatedResources.IsEmpty() && m.phase == PythonMutatorPhaseLoadResources { + return result, diags.Extend(diag.Errorf("unexpected updated resources: %s", result.UpdatedResources.ToArray())) } - resourcemutator.NormalizeResources(ctx, b, result.UpdatedResources) - return mutateDiags + return result, diags.Extend(diag.FromErr(b.Config.Override(plan))) } // createCacheDir returns the directory for input/output files of the Python subprocess, and a cleanup function. @@ -378,7 +350,7 @@ func createCacheDir(ctx context.Context) (string, func(), error) { return cacheDir, func() { _ = os.RemoveAll(cacheDir) }, nil } -func (m *pythonMutator) runPythonMutator(ctx context.Context, root dyn.Value, opts runPythonMutatorOpts) (dyn.Value, diag.Diagnostics) { +func (m *pythonMutator) runPythonMutator(ctx context.Context, root structvar.View, opts runPythonMutatorOpts) (*config.Root, diag.Diagnostics) { inputPath := filepath.Join(opts.cacheDir, "input.json") outputPath := filepath.Join(opts.cacheDir, "output.json") diagnosticsPath := filepath.Join(opts.cacheDir, "diagnostics.json") @@ -403,7 +375,7 @@ func (m *pythonMutator) runPythonMutator(ctx context.Context, root dyn.Value, op } if err := writeInputFile(inputPath, root); err != nil { - return dyn.InvalidValue, diag.Errorf("failed to write input file: %s", err) + return nil, diag.Errorf("failed to write input file: %s", err) } stderrBuf := bytes.Buffer{} @@ -433,7 +405,7 @@ func (m *pythonMutator) runPythonMutator(ctx context.Context, root dyn.Value, op // if diagnostics file exists, it gives the most descriptive errors // if there is any error, we treat it as fatal error, and stop processing if pythonDiagnostics.HasError() { - return dyn.InvalidValue, pythonDiagnostics + return nil, pythonDiagnostics } // process can fail without reporting errors in diagnostics file or creating it, for instance, @@ -445,21 +417,24 @@ func (m *pythonMutator) runPythonMutator(ctx context.Context, root dyn.Value, op Detail: explainProcessErr(ctx, stderrBuf.String()), } - return dyn.InvalidValue, diag.Diagnostics{diagnostic} + return nil, diag.Diagnostics{diagnostic} } // or we can fail to read diagnostics file, that should always be created if pythonDiagnosticsErr != nil { - return dyn.InvalidValue, diag.Errorf("failed to load diagnostics: %s", pythonDiagnosticsErr) + return nil, diag.Errorf("failed to load diagnostics: %s", pythonDiagnosticsErr) } locations, err := loadLocationsFile(opts.bundleRootPath, locationsPath) if err != nil { - return dyn.InvalidValue, diag.Errorf("failed to load locations: %s", err) + return nil, diag.Errorf("failed to load locations: %s", err) } output, outputDiags := loadOutputFile(opts.bundleRootPath, outputPath, locations) pythonDiagnostics = pythonDiagnostics.Extend(outputDiags) + if pythonDiagnostics.HasError() { + return nil, pythonDiagnostics + } // we pass through pythonDiagnostic because it contains warnings return output, pythonDiagnostics @@ -491,9 +466,9 @@ func explainProcessErr(ctx context.Context, stderr string) string { return stderr } -func writeInputFile(inputPath string, input dyn.Value) error { - // we need to marshal dyn.Value instead of bundle.Config to JSON to support - // non-string fields assigned with bundle variables +func writeInputFile(inputPath string, input structvar.View) error { + // we need to marshal the configuration tree instead of bundle.Config to JSON to + // support non-string fields assigned with bundle variables rootConfigJson, err := json.Marshal(input.AsAny()) if err != nil { return fmt.Errorf("failed to marshal input: %w", err) @@ -516,10 +491,10 @@ func loadLocationsFile(bundleRoot, locationsPath string) (*pythonLocations, erro return parsePythonLocations(bundleRoot, locationsFile) } -func loadOutputFile(rootPath, outputPath string, locations *pythonLocations) (dyn.Value, diag.Diagnostics) { +func loadOutputFile(rootPath, outputPath string, locations *pythonLocations) (*config.Root, diag.Diagnostics) { outputFile, err := os.Open(outputPath) if err != nil { - return dyn.InvalidValue, diag.FromErr(fmt.Errorf("failed to open output file: %w", err)) + return nil, diag.FromErr(fmt.Errorf("failed to open output file: %w", err)) } defer outputFile.Close() @@ -527,7 +502,7 @@ func loadOutputFile(rootPath, outputPath string, locations *pythonLocations) (dy return loadOutput(rootPath, outputFile, locations) } -func loadOutput(rootPath string, outputFile io.Reader, locations *pythonLocations) (dyn.Value, diag.Diagnostics) { +func loadOutput(rootPath string, outputFile io.Reader, locations *pythonLocations) (*config.Root, diag.Diagnostics) { // we need absolute path because later parts of pipeline assume all paths are absolute // and this file will be used as location to resolve relative paths. // @@ -538,41 +513,27 @@ func loadOutput(rootPath string, outputFile io.Reader, locations *pythonLocation // for that, we pass virtualPath instead of outputPath as file location virtualPath, err := filepath.Abs(filepath.Join(rootPath, generatedFileName)) if err != nil { - return dyn.InvalidValue, diag.FromErr(fmt.Errorf("failed to get absolute path: %w", err)) + return nil, diag.FromErr(fmt.Errorf("failed to get absolute path: %w", err)) } - generated, err := yamlloader.LoadYAML(virtualPath, outputFile) + // The output has locations as if it comes from the generated YAML file; earlier we + // loaded locations.json with source locations in Python code. + root, diags, err := config.LoadFromReader(virtualPath, outputFile, mergePythonLocations(locations)) if err != nil { - return dyn.InvalidValue, diag.FromErr(fmt.Errorf("failed to parse output file: %w", err)) + return nil, diag.FromErr(fmt.Errorf("failed to parse output file: %w", err)) } - // generated has diag.Location as if it comes from generated YAML file - // earlier we loaded locations.json with source locations in Python code - generatedWithLocations, err := mergePythonLocations(generated, locations) - if err != nil { - return dyn.InvalidValue, diag.FromErr(fmt.Errorf("failed to update locations: %w", err)) - } - - return strictNormalize(config.Root{}, generatedWithLocations) -} - -func strictNormalize(dst any, generated dyn.Value) (dyn.Value, diag.Diagnostics) { - normalized, diags := convert.Normalize(dst, generated) - // warnings shouldn't happen because output should be already normalized // when it happens, it's a bug in the mutator, and should be treated as an error - strictDiags := diag.Diagnostics{} - for _, d := range diags { if d.Severity == diag.Warning { d.Severity = diag.Error } - strictDiags = strictDiags.Append(d) } - return normalized, strictDiags + return root, strictDiags } // loadDiagnosticsFile loads diagnostics from a file. diff --git a/bundle/config/mutator/python/python_mutator_test.go b/bundle/config/mutator/python/python_mutator_test.go index f3378b1bd51..2b72dbd13fa 100644 --- a/bundle/config/mutator/python/python_mutator_test.go +++ b/bundle/config/mutator/python/python_mutator_test.go @@ -9,16 +9,15 @@ import ( "path/filepath" "runtime" "slices" + "strings" "testing" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/bundle/env" "github.com/stretchr/testify/require" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/internal/testutil" @@ -121,11 +120,11 @@ workspace: { current_user: { userName: test }}`) assert.Equal(t, "pipeline_0", pipeline0.Name) } - // output of locations.json should be applied to underlying dyn.Value - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { + // output of locations.json should be applied to underlying configuration + { // location is databricks.yml, because output contains resource as-is - jobName0, err := dyn.GetByPath(v, dyn.MustPathFromString("resources.jobs.job0.name")) - require.NoError(t, err) + jobName0 := b.Config.View().Lookup(structpath.MustParsePath("resources.jobs.job0.name")) + require.True(t, jobName0.IsValid()) assert.Equal(t, []diag.Location{ { File: "databricks.yml", @@ -134,8 +133,8 @@ workspace: { current_user: { userName: test }}`) }, }, jobName0.Locations()) - jobName1, err := dyn.GetByPath(v, dyn.MustPathFromString("resources.jobs.job1.name")) - require.NoError(t, err) + jobName1 := b.Config.View().Lookup(structpath.MustParsePath("resources.jobs.job1.name")) + require.True(t, jobName1.IsValid()) assert.Equal(t, []diag.Location{ { File: filepath.Join(rootPath, "src/examples/job1.py"), @@ -144,8 +143,8 @@ workspace: { current_user: { userName: test }}`) }, }, jobName1.Locations()) - pipelineName0, err := dyn.GetByPath(v, dyn.MustPathFromString("resources.pipelines.pipeline0.name")) - require.NoError(t, err) + pipelineName0 := b.Config.View().Lookup(structpath.MustParsePath("resources.pipelines.pipeline0.name")) + require.True(t, pipelineName0.IsValid()) assert.Equal(t, []diag.Location{ { File: filepath.Join(rootPath, "src/examples/pipeline0.py"), @@ -154,9 +153,7 @@ workspace: { current_user: { userName: test }}`) }, }, pipelineName0.Locations()) - return v, nil - }) - assert.NoError(t, err) + } assert.Len(t, diags, 1) assert.Equal(t, "job doesn't have any tasks", diags[0].Summary) @@ -217,24 +214,22 @@ resources: assert.Equal(t, "job_0", b.Config.Resources.Jobs["job0"].Name) assert.Equal(t, "my job", b.Config.Resources.Jobs["job0"].Description) - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { + { // 'name' wasn't changed, so it keeps its location - name, err := dyn.GetByPath(v, dyn.MustPathFromString("resources.jobs.job0.name")) - require.NoError(t, err) + name := b.Config.View().Lookup(structpath.MustParsePath("resources.jobs.job0.name")) + require.True(t, name.IsValid()) assert.Equal(t, "databricks.yml", name.Location().File) // 'description' was updated by Python code and has location of generated file until // we implement source maps - description, err := dyn.GetByPath(v, dyn.MustPathFromString("resources.jobs.job0.description")) - require.NoError(t, err) + description := b.Config.View().Lookup(structpath.MustParsePath("resources.jobs.job0.description")) + require.True(t, description.IsValid()) expectedVirtualPath, err := filepath.Abs(generatedFileName) require.NoError(t, err) assert.Equal(t, expectedVirtualPath, description.Location().File) - return v, nil - }) - assert.NoError(t, err) + } } func TestPythonConfigSection(t *testing.T) { @@ -429,12 +424,27 @@ func TestApplyBackwardCompatibilityFixes(t *testing.T) { }, } - err := applyBackwardsCompatibilityFixes(b) + applyBackwardsCompatibilityFixes(b) - assert.NoError(t, err) assert.Equal(t, config.Python{VEnvPath: ".venv"}, b.Config.Experimental.Python) } +func TestApplyBackwardCompatibilityFixes_references(t *testing.T) { + b := &bundle.Bundle{ + Config: config.Root{ + Python: config.Python{ + VEnvPath: ".venv", + }, + }, + } + require.NoError(t, b.Config.SetReference(structpath.NewPath(nil, "python", "resources"), "${var.loaders}")) + + applyBackwardsCompatibilityFixes(b) + + assert.Equal(t, "${var.loaders}", b.Config.ReferenceAt(structpath.NewPath(nil, "experimental", "python", "resources"))) + assert.Equal(t, "${var.loaders}", b.Config.ReferenceAt(structpath.NewPath(nil, "python", "resources"))) +} + func TestApplyBackwardCompatibilityFixes_unchanged(t *testing.T) { b := &bundle.Bundle{ Config: config.Root{ @@ -449,9 +459,8 @@ func TestApplyBackwardCompatibilityFixes_unchanged(t *testing.T) { }, } - err := applyBackwardsCompatibilityFixes(b) + applyBackwardsCompatibilityFixes(b) - assert.NoError(t, err) assert.Equal(t, config.Python{VEnvPath: "should_remain_unchanged"}, b.Config.Experimental.Python) } @@ -470,20 +479,14 @@ func TestInterpreterPath(t *testing.T) { } } -func TestStrictNormalize(t *testing.T) { - // NB: there is no way to trigger diag.Error, so we don't test it - - type TestStruct struct { - A int `json:"a"` - } - - value := dyn.NewValue(map[string]dyn.Value{"A": dyn.NewValue("abc", nil)}, nil) +func TestLoadOutputStrict(t *testing.T) { + // Warnings when decoding the output are a bug in the Python code and are errors. + output := `{"resources": {"jobs": {"my_job": {"max_concurrent_runs": "abc"}}}}` - _, diags := convert.Normalize(TestStruct{}, value) - _, strictDiags := strictNormalize(TestStruct{}, value) + _, diags := loadOutput(t.TempDir(), strings.NewReader(output), newPythonLocations()) - assert.False(t, diags.HasError()) - assert.True(t, strictDiags.HasError()) + require.True(t, diags.HasError()) + assert.Equal(t, `cannot parse "abc" as an integer`, diags[0].Summary) } func TestCreateCacheDir(t *testing.T) { diff --git a/bundle/config/mutator/resolve_variable_references.go b/bundle/config/mutator/resolve_variable_references.go index 42c22b61533..af6df23c43d 100644 --- a/bundle/config/mutator/resolve_variable_references.go +++ b/bundle/config/mutator/resolve_variable_references.go @@ -1,20 +1,21 @@ package mutator import ( + "cmp" "context" "errors" "fmt" + "maps" "slices" "strings" - "github.com/databricks/cli/libs/dyn/merge" - "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/variable" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) /* @@ -44,18 +45,31 @@ var defaultPrefixes = []string{ "variables", } -var artifactPath = dyn.MustPathFromString("artifacts") +var artifactPath = structpath.NewPath(nil, "artifacts") + +// resolveScope is the part of the configuration a [resolveVariableReferences] resolves. +type resolveScope int + +const ( + // scopeConfig is the whole configuration ("resources" only if includeResources). + scopeConfig resolveScope = iota + // scopeResources is "resources". + scopeResources + // scopeVariableLookups is "variables.*.lookup", each lookup resolved separately. + scopeVariableLookups +) type resolveVariableReferences struct { + // prefixes are top-level config keys. prefixes []string - pattern dyn.Pattern - lookupFn func(dyn.Value, dyn.Path, *bundle.Bundle) (dyn.Value, error) - allowPathFn func(dyn.Path) bool + scope resolveScope + lookupFn func(structvar.View, *structpath.PathNode, *bundle.Bundle) (structvar.View, error) + allowPathFn func(*structpath.PathNode) bool extraRounds int // includeResources allows resolving variables in 'resources', otherwise, they are excluded. // - // includeResources can be used with appropriate pattern to avoid resolving variables + // includeResources can be used with scopeResources to avoid resolving variables // outside of 'resources'. includeResources bool @@ -75,7 +89,7 @@ func ResolveVariableReferencesOnlyResources(prefixes ...string) bundle.Mutator { prefixes: prefixes, lookupFn: lookup, extraRounds: maxResolutionRounds - 1, - pattern: dyn.NewPattern(dyn.Key("resources")), + scope: scopeResources, includeResources: true, } } @@ -94,7 +108,7 @@ func ResolveVariableReferencesWithoutResources(prefixes ...string) bundle.Mutato func ResolveVariableReferencesInLookup() bundle.Mutator { return &resolveVariableReferences{ prefixes: defaultPrefixes, - pattern: dyn.NewPattern(dyn.Key("variables"), dyn.AnyKey(), dyn.Key("lookup")), + scope: scopeVariableLookups, lookupFn: lookupForVariables, extraRounds: maxResolutionRounds - 1, } @@ -111,36 +125,44 @@ func ResolveVolumePathReferencesOnlyResources() bundle.Mutator { } } -func lookup(v dyn.Value, path dyn.Path, b *bundle.Bundle) (dyn.Value, error) { +func lookup(v structvar.View, path *structpath.PathNode, b *bundle.Bundle) (structvar.View, error) { if b.IsSourceLinkedDeployment() { if path.String() == "workspace.file_path" { - return dyn.V(b.SyncRootPath), nil + return structvar.NewView(&b.SyncRootPath, nil, nil), nil } } // Future opportunity: if we lookup this path in both the given root // and the synthesized root, we know if it was explicitly set or implied to be empty. // Then we can emit a warning if it was not explicitly set. - return dyn.GetByPath(v, path) + return lookupValue(v, path) } -func lookupForVariables(v dyn.Value, path dyn.Path, b *bundle.Bundle) (dyn.Value, error) { - if path[0].Key() != "variables" { - return lookup(v, path, b) - } +// lookupValue returns the value at path. Fields that are declared in the type but not +// set resolve to their zero value. This enables users to interpolate variable references +// to fields that haven't been set, e.g. ${bundle.git.origin_url} resolves to an empty +// string if a bundle isn't located in a Git repository (yet). +func lookupValue(x structvar.View, path *structpath.PathNode) (structvar.View, error) { + return x.LookupWithDefaults(path) +} - varV, err := dyn.GetByPath(v, path[:len(path)-1]) - if err != nil { - return dyn.InvalidValue, err +func lookupForVariables(v structvar.View, path *structpath.PathNode, b *bundle.Bundle) (structvar.View, error) { + if path.KeyAt(0) != "variables" { + return lookup(v, path, b) } - var vv variable.Variable - err = convert.ToTyped(&vv, varV) + varV, err := lookupValue(v, path.Parent()) if err != nil { - return dyn.InvalidValue, err + return structvar.View{}, err } - if vv.Lookup != nil && vv.Lookup.String() != "" { - return dyn.InvalidValue, errors.New("lookup variables cannot contain references to another lookup variables") + if lookupV := varV.Get("lookup"); lookupV.IsValid() { + var vl variable.Lookup + if _, err := (&structvar.StructVar{Value: &vl}).Assign(nil, lookupV); err != nil { + return structvar.View{}, err + } + if vl.String() != "" { + return structvar.View{}, errors.New("lookup variables cannot contain references to another lookup variables") + } } return lookup(v, path, b) @@ -159,14 +181,23 @@ func (m *resolveVariableReferences) Validate(ctx context.Context, b *bundle.Bund } func (m *resolveVariableReferences) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - prefixes := make([]dyn.Path, len(m.prefixes)) + prefixes := make([]*structpath.PathNode, len(m.prefixes)) for i, prefix := range m.prefixes { - prefixes[i] = dyn.MustPathFromString(prefix) + prefixes[i] = structpath.NewPath(nil, prefix) } // The path ${var.foo} is a shorthand for ${variables.foo.value}. // We rewrite it here to make the resolution logic simpler. - varPath := dyn.NewPath(dyn.Key("var")) + varPath := structpath.NewPath(nil, "var") + + // Resolution converts the whole configuration on every round; skip it if no + // reference would be resolved. + if !m.hasReferencesToResolve(b, prefixes, varPath) { + if m.artifactsReferenceUsed { + b.Metrics.SetBoolValue("artifacts_reference_used", true) + } + return nil + } var diags diag.Diagnostics maxRounds := 1 + m.extraRounds @@ -184,6 +215,11 @@ func (m *resolveVariableReferences) Apply(ctx context.Context, b *bundle.Bundle) break } + // Another round would only find out that nothing is left to resolve. + if !m.hasReferencesToResolve(b, prefixes, varPath) { + break + } + if round >= maxRounds-1 { diags = diags.Append(diag.Diagnostic{ Severity: diag.Warning, @@ -201,86 +237,242 @@ func (m *resolveVariableReferences) Apply(ctx context.Context, b *bundle.Bundle) return diags } -func (m *resolveVariableReferences) resolveOnce(b *bundle.Bundle, prefixes []dyn.Path, varPath dyn.Path) (bool, diag.Diagnostics) { +// hasReferencesToResolve reports whether the configuration in scope of this mutator +// has a reference with one of the prefixes. Like resolution itself, it records whether +// "artifacts" is referenced. +func (m *resolveVariableReferences) hasReferencesToResolve(b *bundle.Bundle, prefixes []*structpath.PathNode, varPath *structpath.PathNode) bool { + found := false + m.walkStrings(&b.Config, func(_ *walk.Node, s string) (string, bool) { + // Every string is checked: each can record the "artifacts" reference. + found = m.referencesPrefix(s, prefixes, varPath) || found + return "", false + }) + for path, ref := range b.Config.References() { + if m.inScope(path) { + found = m.referencesPrefix(ref, prefixes, varPath) || found + } + } + return found +} + +// referencesPrefix reports whether s has a reference with one of the prefixes (or one +// that does not parse, to let resolution report it), and records an "artifacts" reference. +func (m *resolveVariableReferences) referencesPrefix(s string, prefixes []*structpath.PathNode, varPath *structpath.PathNode) bool { + ref, ok := structvar.NewRef(s) + if !ok { + return false + } + found := false + for _, r := range ref.References() { + path, err := structpath.ParsePath(r) + if err != nil { + return true + } + if path.HasPrefix(varPath) { + path = structpath.Join(structpath.NewStringKey(nil, "variables"), path.SkipPrefix(1).AsSlice()...) + } + if path.HasPrefix(artifactPath) { + m.artifactsReferenceUsed = true + } + if slices.ContainsFunc(prefixes, path.HasPrefix) { + found = true + } + } + return found +} + +// walkStrings calls fn for the strings with references in the scope of this mutator. +func (m *resolveVariableReferences) walkStrings(r *config.Root, fn walk.StringFunc) { + switch m.scope { + case scopeConfig: + if m.includeResources { + walk.Strings(r, walk.WithReferences, fn) + } else { + walk.StringsWithoutResources(r, walk.WithReferences, fn) + } + case scopeResources: + walk.ResourceStrings(r, walk.WithReferences, fn) + case scopeVariableLookups: + walk.VariableLookupStrings(r, walk.WithReferences, fn) + } +} + +// inScope reports whether path is in the part of the configuration this mutator resolves. +func (m *resolveVariableReferences) inScope(path *structpath.PathNode) bool { + switch m.scope { + case scopeResources: + return path.KeyAt(0) == "resources" + case scopeVariableLookups: + return path.KeyAt(0) == "variables" && path.KeyAt(2) == "lookup" + default: + return m.includeResources || path.KeyAt(0) != "resources" + } +} + +func (m *resolveVariableReferences) resolveOnce(b *bundle.Bundle, prefixes []*structpath.PathNode, varPath *structpath.PathNode) (bool, diag.Diagnostics) { var diags diag.Diagnostics hasUpdates := false - err := m.selectivelyMutate(b, func(root dyn.Value) (dyn.Value, error) { - // Synthesize a copy of the root that has all fields that are present in the type - // but not set in the dynamic value set to their corresponding empty value. - // This enables users to interpolate variable references to fields that haven't - // been explicitly set in the dynamic value. - // - // For example: ${bundle.git.origin_url} should resolve to an empty string - // if a bundle isn't located in a Git repository (yet). - // - // This is consistent with the behavior prior to using the dynamic value system. - // - // We can ignore the diagnostics return value because we know that the dynamic value - // has already been normalized when it was first loaded from the configuration file. - // - normalized, _ := convert.Normalize(b.Config, root, convert.IncludeMissingFields) - - // If the pattern is nil, we resolve references in the entire configuration. - root, err := dyn.MapByPattern(root, m.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - // Resolve variable references in all values. - return dynvar.Resolve(v, func(path dyn.Path) (dyn.Value, error) { - // Rewrite the shorthand path ${var.foo} into ${variables.foo.value}. - if path.HasPrefix(varPath) { - newPath := dyn.NewPath( - dyn.Key("variables"), - path[1], - dyn.Key("value"), - ) - - if len(path) > 2 { - newPath = newPath.Append(path[2:]...) - } - - path = newPath - } - - // If the path starts with "artifacts", we need to add a metric to track if this reference is used. - if path.HasPrefix(artifactPath) { - m.artifactsReferenceUsed = true - } - - // Perform resolution only if the path starts with one of the specified prefixes. - if slices.ContainsFunc(prefixes, path.HasPrefix) { - if slices.Contains(m.excludePaths, path.String()) { - return dyn.InvalidValue, dynvar.ErrSkipResolution - } - if m.allowPathFn != nil && !m.allowPathFn(path) { - return dyn.InvalidValue, dynvar.ErrSkipResolution - } - value, err := m.lookupFn(normalized, path, b) - hasUpdates = hasUpdates || (err == nil && value.IsValid()) - return value, err - } - - return dyn.InvalidValue, dynvar.ErrSkipResolution - }) - }) - if err != nil { - return dyn.InvalidValue, err + root := b.Config.View() + + lookupFn := func(sp *structpath.PathNode) (structvar.View, error) { + path := sp + // Rewrite the shorthand path ${var.foo} into ${variables.foo.value}. + if path.HasPrefix(varPath) { + path = structpath.Join(structpath.NewPath(nil, "variables", path.KeyAt(1), "value"), path.SkipPrefix(2).AsSlice()...) } - // Normalize the result because variable resolution may have been applied to non-string fields. - // For example, a variable reference may have been resolved to a integer. - root, normaliseDiags := convert.Normalize(b.Config, root) - diags = diags.Extend(normaliseDiags) - return root, nil - }) + // If the path starts with "artifacts", we need to add a metric to track if this reference is used. + if path.HasPrefix(artifactPath) { + m.artifactsReferenceUsed = true + } + + // Perform resolution only if the path starts with one of the specified prefixes. + if slices.ContainsFunc(prefixes, path.HasPrefix) { + if slices.Contains(m.excludePaths, path.String()) { + return structvar.View{}, structvar.ErrSkipResolution + } + if m.allowPathFn != nil && !m.allowPathFn(path) { + return structvar.View{}, structvar.ErrSkipResolution + } + value, err := m.lookupFn(root, path, b) + hasUpdates = hasUpdates || (err == nil && value.IsValid()) + return value, err + } + + return structvar.View{}, structvar.ErrSkipResolution + } + + // Resolve the references in each value of the scope: the whole configuration, + // "resources", or each variable lookup. Template keys are relative to that value, like + // the paths in cycle errors. The results are applied after all of them are resolved. + var updates []resolvedValue + var err error + for _, refs := range m.referenceStrings(&b.Config) { + if updates, err = resolveReferenceStrings(updates, refs, lookupFn); err != nil { + break + } + } if err != nil { - diags = diags.Extend(resolveErrorDiags(err)) + return hasUpdates, diags.Extend(resolveErrorDiags(err)) + } + + // Store the results in the typed configuration, converting them to the type of the + // field (e.g. a variable reference resolved to an integer). + // A value that cannot be converted is dropped, which moves the elements after it in + // a sequence: apply the updates from the last element to the first. + slices.SortStableFunc(updates, func(a, b resolvedValue) int { return -comparePaths(a.path, b.path) }) + var updateDiags []diag.Diagnostics + for _, u := range updates { + d, err := b.Config.Decode(u.path, u.value) + updateDiags = append(updateDiags, d.Extend(diag.FromErr(err))) + } + for _, d := range slices.Backward(updateDiags) { + diags = diags.Extend(d) } return hasUpdates, diags } +// comparePaths orders paths by their components; indices are compared as numbers. +func comparePaths(a, b *structpath.PathNode) int { + as, bs := a.AsSlice(), b.AsSlice() + for i := 0; i < len(as) && i < len(bs); i++ { + ai, aok := as[i].Index() + bi, bok := bs[i].Index() + if aok && bok { + if c := cmp.Compare(ai, bi); c != 0 { + return c + } + continue + } + if c := strings.Compare(as[i].String(), bs[i].String()); c != 0 { + return c + } + } + return cmp.Compare(len(as), len(bs)) +} + +// resolvedValue is the value a reference string at path resolved to. +type resolvedValue struct { + path *structpath.PathNode + value structvar.View +} + +// resolveReferenceStrings resolves the reference strings of one base together and +// appends the results to out, in path order. Template keys are relative to the base. +func resolveReferenceStrings(out []resolvedValue, refs []referenceString, lookup structvar.Lookup) ([]resolvedValue, error) { + templates := map[string]structvar.Template{} + paths := map[string]*structpath.PathNode{} + for _, ref := range refs { + key := ref.path.SkipPrefix(ref.base.Len()).String() + templates[key] = structvar.Template{Value: ref.value, Locations: ref.locs} + paths[key] = ref.path + } + resolved, err := structvar.Resolve(templates, lookup) + if err != nil { + return out, err + } + for _, key := range slices.Sorted(maps.Keys(resolved)) { + out = append(out, resolvedValue{path: paths[key], value: resolved[key]}) + } + return out, nil +} + +// referenceString is a string with a variable reference at path. Strings with the same +// base (the scope value they are in) are resolved together. +type referenceString struct { + base *structpath.PathNode + path *structpath.PathNode + value string + locs []diag.Location +} + +// referenceStrings returns the strings with variable references in the scope of this +// mutator, grouped by base in the order of the bases. Pure references in fields that +// cannot hold a string are included as their reference strings. +func (m *resolveVariableReferences) referenceStrings(r *config.Root) [][]referenceString { + var out []referenceString + m.walkStrings(r, func(n *walk.Node, s string) (string, bool) { + if _, ok := structvar.NewRef(s); ok { + out = append(out, referenceString{path: n.Path(), value: s, locs: n.Locations().Get()}) + } + return "", false + }) + for path, ref := range r.References() { + if m.inScope(path) { + out = append(out, referenceString{path: path, value: ref, locs: r.GetLocationsOfExact(path)}) + } + } + + groups := map[string][]referenceString{} + for _, ref := range out { + ref.base = m.base(ref.path) + key := ref.base.String() + groups[key] = append(groups[key], ref) + } + result := make([][]referenceString, 0, len(groups)) + for _, key := range slices.Sorted(maps.Keys(groups)) { + result = append(result, groups[key]) + } + return result +} + +// base returns the scope value path is in. +func (m *resolveVariableReferences) base(path *structpath.PathNode) *structpath.PathNode { + switch m.scope { + case scopeResources: + return path.Prefix(1) + case scopeVariableLookups: + return path.Prefix(3) + default: + return nil + } +} + // resolveErrorDiags renders "did you mean" suggestions as a diagnostic Detail so // libs/diag owns the multi-line formatting. func resolveErrorDiags(err error) diag.Diagnostics { - refErr, ok := errors.AsType[*dynvar.ReferenceError](err) + refErr, ok := errors.AsType[*structvar.ReferenceError](err) if !ok || len(refErr.Suggestions) == 0 { return diag.FromErr(err) } @@ -302,66 +494,11 @@ func resolveErrorDiags(err error) diag.Diagnostics { }} } -// selectivelyMutate applies a function to a subset of the configuration -func (m *resolveVariableReferences) selectivelyMutate(b *bundle.Bundle, fn func(value dyn.Value) (dyn.Value, error)) error { - return b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - allKeys, err := getAllKeys(root) - if err != nil { - return dyn.InvalidValue, err - } - - var included []string - for _, key := range allKeys { - if key == "resources" { - if m.includeResources { - included = append(included, key) - } - } else { - included = append(included, key) - } - } - - includedRoot, err := merge.Select(root, included) - if err != nil { - return dyn.InvalidValue, err - } - - excludedRoot, err := merge.AntiSelect(root, included) - if err != nil { - return dyn.InvalidValue, err - } - - updatedRoot, err := fn(includedRoot) - if err != nil { - return dyn.InvalidValue, err - } - - // merge is recursive, but it doesn't matter because keys are mutually exclusive - return merge.Merge(updatedRoot, excludedRoot) - }) -} - -func getAllKeys(root dyn.Value) ([]string, error) { - var keys []string - - if mapping, ok := root.AsMap(); ok { - for _, key := range mapping.Keys() { - if keyString, ok := key.AsString(); ok { - keys = append(keys, keyString) - } else { - return nil, fmt.Errorf("key is not a string: %v", key) - } - } - } - - return keys, nil -} - -func isVolumePathReferencePath(path dyn.Path) bool { - if len(path) != 4 { +func isVolumePathReferencePath(path *structpath.PathNode) bool { + if path.Len() != 4 { return false } - return path[0].Key() == "resources" && - path[1].Key() == "volumes" && - path[3].Key() == "volume_path" + return path.KeyAt(0) == "resources" && + path.KeyAt(1) == "volumes" && + path.KeyAt(3) == "volume_path" } diff --git a/bundle/config/mutator/resolve_variable_references_test.go b/bundle/config/mutator/resolve_variable_references_test.go index 284b78846b4..4be7f012af2 100644 --- a/bundle/config/mutator/resolve_variable_references_test.go +++ b/bundle/config/mutator/resolve_variable_references_test.go @@ -6,7 +6,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/require" ) @@ -106,7 +106,7 @@ func TestResolveVolumePathReferencesOnlyResources_MissingTarget(t *testing.T) { } func TestIsVolumePathReferencePath(t *testing.T) { - require.True(t, isVolumePathReferencePath(dyn.MustPathFromString("resources.volumes.foo.volume_path"))) - require.False(t, isVolumePathReferencePath(dyn.MustPathFromString("resources.volumes.foo.name"))) - require.False(t, isVolumePathReferencePath(dyn.MustPathFromString("resources.jobs.foo.name"))) + require.True(t, isVolumePathReferencePath(structpath.MustParsePath("resources.volumes.foo.volume_path"))) + require.False(t, isVolumePathReferencePath(structpath.MustParsePath("resources.volumes.foo.name"))) + require.False(t, isVolumePathReferencePath(structpath.MustParsePath("resources.jobs.foo.name"))) } diff --git a/bundle/config/mutator/resourcemutator/.gitattributes b/bundle/config/mutator/resourcemutator/.gitattributes new file mode 100644 index 00000000000..a7515927b7c --- /dev/null +++ b/bundle/config/mutator/resourcemutator/.gitattributes @@ -0,0 +1 @@ +defaults_walk.go linguist-generated=true diff --git a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go index 610832c2b92..3d4e5ff3039 100644 --- a/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go +++ b/bundle/config/mutator/resourcemutator/apply_bundle_permissions_test.go @@ -11,7 +11,6 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -262,8 +261,5 @@ func TestApplyBundlePermissionsEmpty(t *testing.T) { // An empty list of permissions is dropped, an unset one stays unset. for _, name := range []string{"empty", "unset"} { require.Nil(t, b.Config.Resources.Jobs[name].Permissions) - v, err := dyn.GetByPath(b.Config.Value(), dyn.MustPathFromString("resources.jobs."+name)) - require.NoError(t, err) - assert.False(t, v.Get("permissions").IsValid(), name) } } diff --git a/bundle/config/mutator/resourcemutator/apply_default_task_source.go b/bundle/config/mutator/resourcemutator/apply_default_task_source.go index 30e000b1fb1..7789af0155d 100644 --- a/bundle/config/mutator/resourcemutator/apply_default_task_source.go +++ b/bundle/config/mutator/resourcemutator/apply_default_task_source.go @@ -2,10 +2,11 @@ package resourcemutator import ( "context" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -37,57 +38,61 @@ func (a *applyDefaultTaskSource) Name() string { return "ApplyDefaultTaskSource" } -// sourceAwareTaskTypes are the task types that support the `source` field. -// https://docs.databricks.com/api/workspace/jobs/create -var sourceAwareTaskTypes = []string{ - "dbt_task", - "gen_ai_compute_task", - "notebook_task", - "spark_python_task", - "sql_task.file", -} - func (a *applyDefaultTaskSource) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - jobsPattern := dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey()) + for name, job := range b.Config.Resources.Jobs { + // Only git_source jobs need an explicit source; leave the rest untouched. + // A missing key or an explicit `git_source: null` both count as absent, + // matching the typed nil pointer TranslatePaths keys off; otherwise we would + // set source: GIT on a job whose paths get translated to workspace paths. + // A reference is not absent, although the typed field is nil. + if job == nil || (job.GitSource == nil && !b.Config.IsReferenceAt(structpath.NewPath(nil, "resources", "jobs", name, "git_source"))) { + continue + } - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - return dyn.MapByPattern(root, jobsPattern, func(_ dyn.Path, job dyn.Value) (dyn.Value, error) { - // Only git_source jobs need an explicit source; leave the rest untouched. - // A missing key (KindInvalid) or an explicit `git_source: null` (KindNil) - // both count as absent, matching the typed nil pointer TranslatePaths keys - // off; otherwise we would set source: GIT on a job whose paths get - // translated to workspace paths. - gitSource := job.Get("git_source") - if gitSource.Kind() == dyn.KindInvalid || gitSource.Kind() == dyn.KindNil { - return job, nil + for i := range job.Tasks { + task := &job.Tasks[i] + if task.ForEachTask != nil { + setGitTaskSource(&task.ForEachTask.Task) } - - return dyn.Map(job, "tasks", dyn.Foreach(func(_ dyn.Path, task dyn.Value) (dyn.Value, error) { - task, err := dyn.Map(task, "for_each_task.task", func(_ dyn.Path, foreachTask dyn.Value) (dyn.Value, error) { - return setGitTaskSource(foreachTask) - }) - if err != nil { - return dyn.InvalidValue, err - } - return setGitTaskSource(task) - })) - }) - }) - - return diag.FromErr(err) + setGitTaskSource(task) + } + } + return nil } // setGitTaskSource sets source: GIT on the first task-type block present that // supports the field, unless the user already set it. -func setGitTaskSource(task dyn.Value) (dyn.Value, error) { - for _, taskType := range sourceAwareTaskTypes { - t, err := dyn.Get(task, taskType) - if err != nil { +// The task types are the ones that support the `source` field: +// https://docs.databricks.com/api/workspace/jobs/create +func setGitTaskSource(task *jobs.Task) { + type taskSource struct { + source *jobs.Source + // fsf records an explicit `source: ""`. spark_python_task and sql_task.file + // have no ForceSendFields, so an explicit empty source there reads as unset + // and gets the GIT default. + fsf []string + } + var sources []taskSource + if t := task.DbtTask; t != nil { + sources = append(sources, taskSource{&t.Source, t.ForceSendFields}) + } + if t := task.GenAiComputeTask; t != nil { + sources = append(sources, taskSource{&t.Source, t.ForceSendFields}) + } + if t := task.NotebookTask; t != nil { + sources = append(sources, taskSource{&t.Source, t.ForceSendFields}) + } + if t := task.SparkPythonTask; t != nil { + sources = append(sources, taskSource{&t.Source, nil}) + } + if task.SqlTask != nil && task.SqlTask.File != nil { + sources = append(sources, taskSource{&task.SqlTask.File.Source, nil}) + } + for _, s := range sources { + if *s.source != "" || slices.Contains(s.fsf, "Source") { continue } - if _, err := dyn.Get(t, "source"); err != nil { - return dyn.Set(task, taskType+".source", dyn.V(string(jobs.SourceGit))) - } + *s.source = jobs.SourceGit + return } - return task, nil } diff --git a/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go b/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go index a0e8d05fbde..4ca98d19bed 100644 --- a/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go +++ b/bundle/config/mutator/resourcemutator/capture_uc_dependencies.go @@ -8,7 +8,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structvar" ) type captureUCDependencies struct{} @@ -159,7 +159,7 @@ func (m *captureUCDependencies) Apply(ctx context.Context, b *bundle.Bundle) dia } // A name that already contains a reference is left as is: we only rewrite a // fully literal name and do not support a mix of references and literals. - if dynvar.ContainsVariableReference(qm.OutputSchemaName) { + if structvar.ContainsVariableReference(qm.OutputSchemaName) { continue } // OutputSchemaName is a compound "catalog.schema" string. @@ -179,7 +179,7 @@ func (m *captureUCDependencies) Apply(ctx context.Context, b *bundle.Bundle) dia } // A name that already contains a reference is left as is; a mix of // references and literals is not supported. - if dynvar.ContainsVariableReference(idx.Name) { + if structvar.ContainsVariableReference(idx.Name) { continue } // Name is a three-part "catalog.schema.index" UC identifier. @@ -194,7 +194,7 @@ func (m *captureUCDependencies) Apply(ctx context.Context, b *bundle.Bundle) dia if f == nil { continue } - if dynvar.ContainsVariableReference(f.FullName) { + if structvar.ContainsVariableReference(f.FullName) { continue } parts, ok := splitUCName(f.FullName, 3) @@ -226,7 +226,7 @@ func (m *captureUCDependencies) Apply(ctx context.Context, b *bundle.Bundle) dia } // A parent that already contains a reference is left as is; a mix of // references and literals is not supported. - if dynvar.ContainsVariableReference(ms.Parent) { + if structvar.ContainsVariableReference(ms.Parent) { continue } ms.Parent = resolveAiGatewayParent(b, ms.Parent) diff --git a/bundle/config/mutator/resourcemutator/configure_cluster_policy_definition.go b/bundle/config/mutator/resourcemutator/configure_cluster_policy_definition.go index 1e7a0d6f64b..8a6ae9fb7e4 100644 --- a/bundle/config/mutator/resourcemutator/configure_cluster_policy_definition.go +++ b/bundle/config/mutator/resourcemutator/configure_cluster_policy_definition.go @@ -2,17 +2,15 @@ package resourcemutator import ( "context" - "encoding/json" "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) -// jsonPolicyFields are the JSON-policy fields normalized from inline YAML to a JSON string. -var jsonPolicyFields = []string{"definition", "policy_family_definition_overrides"} - type configureClusterPolicyDefinition struct{} func ConfigureClusterPolicyDefinition() bundle.Mutator { @@ -26,46 +24,47 @@ func (c configureClusterPolicyDefinition) Name() string { func (c configureClusterPolicyDefinition) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { var diags diag.Diagnostics - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("cluster_policies"), - dyn.AnyKey(), - ) - - 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) { - for _, field := range jsonPolicyFields { - def := v.Get(field) - - // Marshal an inline structured value to a JSON string so both - // config-side and state-side carry the same plain string. Otherwise - // YAML decodes small ints as Go `int` while state JSON round-trip - // decodes them as `float64`, and structdiff reports false drift. - switch def.Kind() { - case dyn.KindInvalid, dyn.KindNil, dyn.KindString: - // KindInvalid means the field is absent; leave it for backend validation. - continue - case dyn.KindMap: - jsonBytes, err := json.Marshal(def.AsAny()) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to marshal inline %s: %w", field, err) - } - v, err = dyn.Set(v, field, dyn.V(string(jsonBytes))) - if err != nil { - return dyn.InvalidValue, err - } - default: - diags = diags.Append(diag.Diagnostic{ - Severity: diag.Error, - Summary: fmt.Sprintf("%s must be a string or map, got %s", field, def.Kind()), - Locations: def.Locations(), - }) - } - } - return v, nil - }) - }) + policies := b.Config.Resources.ClusterPolicies + for _, name := range slices.Sorted(maps.Keys(policies)) { + policy := policies[name] + if policy == nil { + continue + } + d, err := inlinePolicyField(b, name, "definition", policy.Definition) + diags = diags.Extend(d) + if err != nil { + return diags.Extend(diag.FromErr(err)) + } + d, err = inlinePolicyField(b, name, "policy_family_definition_overrides", policy.PolicyFamilyDefinitionOverrides) + diags = diags.Extend(d) + if err != nil { + return diags.Extend(diag.FromErr(err)) + } + } - diags = diags.Extend(diag.FromErr(err)) return diags } + +// inlinePolicyField marshals the inline structured value of the field key of the +// cluster policy name to a JSON string, so both config-side and state-side carry the +// same plain string. Otherwise YAML decodes small ints as Go `int` while state JSON +// round-trip decodes them as `float64`, and structdiff reports false drift. An absent +// value is left for backend validation. +func inlinePolicyField(b *bundle.Bundle, name, key string, value any) (diag.Diagnostics, error) { + fieldPath := structpath.NewPath(nil, "resources", "cluster_policies", name, key) + s, ok, kind, err := inlineJSON(value) + if err != nil { + return nil, fmt.Errorf("failed to marshal inline %s: %w", key, err) + } + if kind != "" { + return diag.Diagnostics{{ + Severity: diag.Error, + Summary: fmt.Sprintf("%s must be a string or map, got %s", key, kind), + Locations: b.Config.GetLocationsOfExact(fieldPath), + }}, nil + } + if !ok { + return nil, nil + } + return nil, b.Config.Set(fieldPath, s) +} diff --git a/bundle/config/mutator/resourcemutator/configure_dashboards_serialized_dashboard.go b/bundle/config/mutator/resourcemutator/configure_dashboards_serialized_dashboard.go index a17e5c6b07c..133b7ef6b50 100644 --- a/bundle/config/mutator/resourcemutator/configure_dashboards_serialized_dashboard.go +++ b/bundle/config/mutator/resourcemutator/configure_dashboards_serialized_dashboard.go @@ -2,12 +2,13 @@ package resourcemutator import ( "context" - "encoding/json" "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) const ( @@ -28,64 +29,60 @@ func (c configureDashboardSerializedDashboard) Name() string { func (c configureDashboardSerializedDashboard) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { var diags diag.Diagnostics - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("dashboards"), - dyn.AnyKey(), - ) + dashboards := b.Config.Resources.Dashboards + for _, name := range slices.Sorted(maps.Keys(dashboards)) { + d := dashboards[name] + if d == nil { + continue + } + sdPath := structpath.NewPath(nil, "resources", "dashboards", name, serializedDashboardFieldName) - // Configure serialized_dashboard field 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) { - // Include "serialized_dashboard" field if "file_path" is set. - // Note: the Terraform resource supports "file_path" natively, but we read the contents of the dashboard here - // to be able to read file contents in Databricks Workspace (reading a dashboard file via file system fails there) - filePath, hasFilePath := v.Get(filePathFieldName).AsString() - sd := v.Get(serializedDashboardFieldName) - - if hasFilePath { - // file_path and serialized_dashboard are two ways to provide the - // same content. Accepting both is ambiguous, so reject it instead - // of silently picking one. - if sd.IsValid() && sd.Kind() != dyn.KindNil { - diags = diags.Append(diag.Diagnostic{ - Severity: diag.Error, - Summary: "both file_path and serialized_dashboard are set; specify only one", - Locations: sd.Locations(), - }) - return v, nil - } - - contents, err := b.SyncRoot.ReadFile(filePath) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to read serialized dashboard from file_path %s: %w", filePath, err) - } - return dyn.Set(v, serializedDashboardFieldName, dyn.V(string(contents))) - } - - // Marshal an inline structured serialized_dashboard to a JSON string - switch sd.Kind() { - case dyn.KindInvalid, dyn.KindNil, dyn.KindString: - // KindInvalid means serialized_dashboard is absent (neither it nor - // file_path is set); leave it for backend validation to reject. - return v, nil - case dyn.KindMap: - jsonBytes, err := json.Marshal(sd.AsAny()) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to marshal inline serialized_dashboard: %w", err) - } - return dyn.Set(v, serializedDashboardFieldName, dyn.V(string(jsonBytes))) - default: + // Include "serialized_dashboard" field if "file_path" is set. + // Note: the Terraform resource supports "file_path" natively, but we read the contents of the dashboard here + // to be able to read file contents in Databricks Workspace (reading a dashboard file via file system fails there) + if d.FilePath != "" { + // file_path and serialized_dashboard are two ways to provide the + // same content. Accepting both is ambiguous, so reject it instead + // of silently picking one. + if d.SerializedDashboard != nil { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, - Summary: fmt.Sprintf("serialized_dashboard must be a string or map, got %s", sd.Kind()), - Locations: sd.Locations(), + Summary: "both file_path and serialized_dashboard are set; specify only one", + Locations: b.Config.GetLocationsOfExact(sdPath), }) - return v, nil + continue + } + + contents, err := b.SyncRoot.ReadFile(d.FilePath) + if err != nil { + return diags.Extend(diag.FromErr(fmt.Errorf("failed to read serialized dashboard from file_path %s: %w", d.FilePath, err))) + } + if err := b.Config.Set(sdPath, string(contents)); err != nil { + return diags.Extend(diag.FromErr(err)) + } + continue + } + + // Marshal an inline structured serialized_dashboard to a JSON string. + // Absent (neither it nor file_path is set) is left for backend validation. + s, ok, kind, err := inlineJSON(d.SerializedDashboard) + if err != nil { + return diags.Extend(diag.FromErr(fmt.Errorf("failed to marshal inline serialized_dashboard: %w", err))) + } + if kind != "" { + diags = diags.Append(diag.Diagnostic{ + Severity: diag.Error, + Summary: "serialized_dashboard must be a string or map, got " + kind, + Locations: b.Config.GetLocationsOfExact(sdPath), + }) + continue + } + if ok { + if err := b.Config.Set(sdPath, s); err != nil { + return diags.Extend(diag.FromErr(err)) } - }) - }) + } + } - diags = diags.Extend(diag.FromErr(err)) return diags } diff --git a/bundle/config/mutator/resourcemutator/configure_genie_space_serialized_space.go b/bundle/config/mutator/resourcemutator/configure_genie_space_serialized_space.go index e16bd57625b..fca28f59d4b 100644 --- a/bundle/config/mutator/resourcemutator/configure_genie_space_serialized_space.go +++ b/bundle/config/mutator/resourcemutator/configure_genie_space_serialized_space.go @@ -2,12 +2,13 @@ package resourcemutator import ( "context" - "encoding/json" "fmt" + "maps" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) const serializedSpaceFieldName = "serialized_space" @@ -25,66 +26,60 @@ func (c configureGenieSpaceSerializedSpace) Name() string { func (c configureGenieSpaceSerializedSpace) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { var diags diag.Diagnostics - pattern := dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("genie_spaces"), - dyn.AnyKey(), - ) + spaces := b.Config.Resources.GenieSpaces + for _, name := range slices.Sorted(maps.Keys(spaces)) { + g := spaces[name] + if g == nil { + continue + } + ssPath := structpath.NewPath(nil, "resources", "genie_spaces", name, serializedSpaceFieldName) - 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) { - filePath, hasFilePath := v.Get(filePathFieldName).AsString() - ss := v.Get(serializedSpaceFieldName) - - if hasFilePath { - // file_path and serialized_space are two ways to provide the same - // content. Accepting both is ambiguous, so reject it instead of - // silently picking one. - if ss.IsValid() && ss.Kind() != dyn.KindNil { - diags = diags.Append(diag.Diagnostic{ - Severity: diag.Error, - Summary: "both file_path and serialized_space are set; specify only one", - Locations: ss.Locations(), - }) - return v, nil - } - contents, err := b.SyncRoot.ReadFile(filePath) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to read serialized genie space from file_path %s: %w", filePath, err) - } - return dyn.Set(v, serializedSpaceFieldName, dyn.V(string(contents))) - } - - // Marshal an inline structured serialized_space to a JSON string so - // both config-side and state-side carry the same plain string. - // Otherwise YAML decodes small ints as Go `int` while state JSON - // round-trip decodes them as `float64`, and structdiff reports - // false drift on every plan. - switch ss.Kind() { - case dyn.KindInvalid, dyn.KindNil, dyn.KindString: - // KindInvalid means serialized_space is absent (neither it nor - // file_path is set); leave it for backend validation to reject. - return v, nil - case dyn.KindMap: - // A top-level sequence would be valid JSON but is meaningless for a - // genie space, so KindSequence is not accepted here and falls through - // to the default rejection below. - jsonBytes, err := json.Marshal(ss.AsAny()) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to marshal inline serialized_space: %w", err) - } - return dyn.Set(v, serializedSpaceFieldName, dyn.V(string(jsonBytes))) - default: + if g.FilePath != "" { + // file_path and serialized_space are two ways to provide the same + // content. Accepting both is ambiguous, so reject it instead of + // silently picking one. + if g.SerializedSpace != nil { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, - Summary: fmt.Sprintf("serialized_space must be a string or map, got %s", ss.Kind()), - Locations: ss.Locations(), + Summary: "both file_path and serialized_space are set; specify only one", + Locations: b.Config.GetLocationsOfExact(ssPath), }) - return v, nil + continue + } + contents, err := b.SyncRoot.ReadFile(g.FilePath) + if err != nil { + return diags.Extend(diag.FromErr(fmt.Errorf("failed to read serialized genie space from file_path %s: %w", g.FilePath, err))) + } + if err := b.Config.Set(ssPath, string(contents)); err != nil { + return diags.Extend(diag.FromErr(err)) + } + continue + } + + // Marshal an inline structured serialized_space to a JSON string so + // both config-side and state-side carry the same plain string. + // Otherwise YAML decodes small ints as Go `int` while state JSON + // round-trip decodes them as `float64`, and structdiff reports + // false drift on every plan. A top-level sequence would be valid JSON + // but is meaningless for a genie space, so it is rejected. + s, ok, kind, err := inlineJSON(g.SerializedSpace) + if err != nil { + return diags.Extend(diag.FromErr(fmt.Errorf("failed to marshal inline serialized_space: %w", err))) + } + if kind != "" { + diags = diags.Append(diag.Diagnostic{ + Severity: diag.Error, + Summary: "serialized_space must be a string or map, got " + kind, + Locations: b.Config.GetLocationsOfExact(ssPath), + }) + continue + } + if ok { + if err := b.Config.Set(ssPath, s); err != nil { + return diags.Extend(diag.FromErr(err)) } - }) - }) + } + } - diags = diags.Extend(diag.FromErr(err)) return diags } diff --git a/bundle/config/mutator/resourcemutator/defaults.go b/bundle/config/mutator/resourcemutator/defaults.go new file mode 100644 index 00000000000..9dea31ba6d9 --- /dev/null +++ b/bundle/config/mutator/resourcemutator/defaults.go @@ -0,0 +1,29 @@ +package resourcemutator + +import ( + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/resourcedefaults" + "github.com/databricks/cli/bundle/config/walk" +) + +// defaultsWalker sets the defaults of resourcedefaults.Defaults found unset by the +// generated walker (defaults_walk.go). +type defaultsWalker struct { + b *bundle.Bundle + err error +} + +// unset sets the default i at n. +func (w *defaultsWalker) unset(n *walk.Node, i int) { + if w.err == nil { + w.err = w.b.Config.Set(n.Path(), resourcedefaults.Defaults[i].Value(&w.b.Config)) + } +} + +// applyResourceDefaults sets the default values of resource fields that are not set. +func applyResourceDefaults(b *bundle.Bundle) error { + w := &defaultsWalker{b: b} + n := walk.RootWithReferences(&b.Config) + defaultsConfigRoot(w, &n, &b.Config) + return w.err +} diff --git a/bundle/config/mutator/resourcemutator/defaults_walk.go b/bundle/config/mutator/resourcemutator/defaults_walk.go new file mode 100644 index 00000000000..46f41bc24d2 --- /dev/null +++ b/bundle/config/mutator/resourcemutator/defaults_walk.go @@ -0,0 +1,358 @@ +// Code generated by bundle/internal/validation. DO NOT EDIT. + +// defaultsWalk calls unset for the fields with a default (resourcedefaults.Defaults) that are not set. + +package resourcemutator + +import ( + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/config/walk" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/databricks/databricks-sdk-go/service/jobs" +) + +func defaultsConfigRoot(w *defaultsWalker, n *walk.Node, v *config.Root) { + if v == nil { + return + } + if x := &v.Resources; !walk.IsZeroStruct(x) || n.HasRefs("resources") { + c := n.Child("resources") + defaultsConfigResources(w, &c, x) + } +} + +func defaultsConfigResources(w *defaultsWalker, n *walk.Node, v *config.Resources) { + if v == nil { + return + } + if len(v.Alerts) > 0 { + c := n.Child("alerts") + for k, x := range v.Alerts { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesAlert(w, &e, x) + } + } + if len(v.Apps) > 0 { + c := n.Child("apps") + for k, x := range v.Apps { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesApp(w, &e, x) + } + } + if len(v.Clusters) > 0 { + c := n.Child("clusters") + for k, x := range v.Clusters { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesCluster(w, &e, x) + } + } + if len(v.Dashboards) > 0 { + c := n.Child("dashboards") + for k, x := range v.Dashboards { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesDashboard(w, &e, x) + } + } + if len(v.GenieSpaces) > 0 { + c := n.Child("genie_spaces") + for k, x := range v.GenieSpaces { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesGenieSpace(w, &e, x) + } + } + if len(v.Jobs) > 0 { + c := n.Child("jobs") + for k, x := range v.Jobs { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesJob(w, &e, x) + } + } + if len(v.Pipelines) > 0 { + c := n.Child("pipelines") + for k, x := range v.Pipelines { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesPipeline(w, &e, x) + } + } + if len(v.SqlWarehouses) > 0 { + c := n.Child("sql_warehouses") + for k, x := range v.SqlWarehouses { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesSqlWarehouse(w, &e, x) + } + } + if len(v.Volumes) > 0 { + c := n.Child("volumes") + for k, x := range v.Volumes { + if k == "" { + continue + } + e := c.Child(k) + defaultsResourcesVolume(w, &e, x) + } + } +} + +func defaultsResourcesAlert(w *defaultsWalker, n *walk.Node, v *resources.Alert) { + if v == nil { + return + } + if !(v.AlertV2.ParentPath != "" || walk.Forced(v.AlertV2.ForceSendFields, "ParentPath")) { + c := n.Child("parent_path") + w.unset(&c, 4) + } +} + +func defaultsResourcesApp(w *defaultsWalker, n *walk.Node, v *resources.App) { + if v == nil { + return + } + if !(v.App.Description != "" || walk.Forced(v.App.ForceSendFields, "Description")) { + c := n.Child("description") + w.unset(&c, 19) + } +} + +func defaultsResourcesCluster(w *defaultsWalker, n *walk.Node, v *resources.Cluster) { + if v == nil { + return + } + if !(v.ClusterSpec.AutoterminationMinutes != 0 || walk.Forced(v.ClusterSpec.ForceSendFields, "AutoterminationMinutes") || n.IsRef("autotermination_minutes")) { + c := n.Child("autotermination_minutes") + w.unset(&c, 20) + } + if x := v.ClusterSpec.WorkloadType; x != nil { + c := n.Child("workload_type") + defaultsComputeWorkloadType(w, &c, x) + } +} + +func defaultsResourcesDashboard(w *defaultsWalker, n *walk.Node, v *resources.Dashboard) { + if v == nil { + return + } + if !(v.DashboardConfig.ParentPath != "" || walk.Forced(v.DashboardConfig.ForceSendFields, "ParentPath")) { + c := n.Child("parent_path") + w.unset(&c, 0) + } + if !(v.DashboardConfig.EmbedCredentials || walk.Forced(v.DashboardConfig.ForceSendFields, "EmbedCredentials") || n.IsRef("embed_credentials")) { + c := n.Child("embed_credentials") + w.unset(&c, 1) + } +} + +func defaultsResourcesGenieSpace(w *defaultsWalker, n *walk.Node, v *resources.GenieSpace) { + if v == nil { + return + } + if !(v.GenieSpaceConfig.ParentPath != "" || walk.Forced(v.GenieSpaceConfig.ForceSendFields, "ParentPath")) { + c := n.Child("parent_path") + w.unset(&c, 2) + } +} + +func defaultsResourcesJob(w *defaultsWalker, n *walk.Node, v *resources.Job) { + if v == nil { + return + } + if !(v.JobSettings.Name != "" || walk.Forced(v.JobSettings.ForceSendFields, "Name")) { + c := n.Child("name") + w.unset(&c, 5) + } + if !(v.JobSettings.MaxConcurrentRuns != 0 || walk.Forced(v.JobSettings.ForceSendFields, "MaxConcurrentRuns") || n.IsRef("max_concurrent_runs")) { + c := n.Child("max_concurrent_runs") + w.unset(&c, 6) + } + if !(v.JobSettings.Queue != nil || walk.Forced(v.JobSettings.ForceSendFields, "Queue") || n.IsRef("queue")) { + c := n.Child("queue") + w.unset(&c, 10) + } + if x := v.JobSettings.Continuous; x != nil { + c := n.Child("continuous") + defaultsJobsContinuous(w, &c, x) + } + if len(v.JobSettings.JobClusters) > 0 { + c := n.Child("job_clusters") + for i := range v.JobSettings.JobClusters { + e := c.Elem(i) + defaultsJobsJobCluster(w, &e, &v.JobSettings.JobClusters[i]) + } + } + if x := v.JobSettings.Schedule; x != nil { + c := n.Child("schedule") + defaultsJobsCronSchedule(w, &c, x) + } + if x := v.JobSettings.Trigger; x != nil { + c := n.Child("trigger") + defaultsJobsTriggerSettings(w, &c, x) + } +} + +func defaultsResourcesPipeline(w *defaultsWalker, n *walk.Node, v *resources.Pipeline) { + if v == nil { + return + } + if !(v.CreatePipeline.Edition != "" || walk.Forced(v.CreatePipeline.ForceSendFields, "Edition")) { + c := n.Child("edition") + w.unset(&c, 13) + } + if !(v.CreatePipeline.Channel != "" || walk.Forced(v.CreatePipeline.ForceSendFields, "Channel")) { + c := n.Child("channel") + w.unset(&c, 14) + } +} + +func defaultsResourcesSqlWarehouse(w *defaultsWalker, n *walk.Node, v *resources.SqlWarehouse) { + if v == nil { + return + } + if !(v.CreateWarehouseRequest.AutoStopMins != 0 || walk.Forced(v.CreateWarehouseRequest.ForceSendFields, "AutoStopMins") || n.IsRef("auto_stop_mins")) { + c := n.Child("auto_stop_mins") + w.unset(&c, 15) + } + if !(v.CreateWarehouseRequest.EnablePhoton || walk.Forced(v.CreateWarehouseRequest.ForceSendFields, "EnablePhoton") || n.IsRef("enable_photon")) { + c := n.Child("enable_photon") + w.unset(&c, 16) + } + if !(v.CreateWarehouseRequest.MaxNumClusters != 0 || walk.Forced(v.CreateWarehouseRequest.ForceSendFields, "MaxNumClusters") || n.IsRef("max_num_clusters")) { + c := n.Child("max_num_clusters") + w.unset(&c, 17) + } + if !(v.CreateWarehouseRequest.SpotInstancePolicy != "" || walk.Forced(v.CreateWarehouseRequest.ForceSendFields, "SpotInstancePolicy")) { + c := n.Child("spot_instance_policy") + w.unset(&c, 18) + } +} + +func defaultsResourcesVolume(w *defaultsWalker, n *walk.Node, v *resources.Volume) { + if v == nil { + return + } + if !(v.CreateVolumeRequestContent.VolumeType != "" || walk.Forced(v.CreateVolumeRequestContent.ForceSendFields, "VolumeType")) { + c := n.Child("volume_type") + w.unset(&c, 3) + } +} + +func defaultsComputeWorkloadType(w *defaultsWalker, n *walk.Node, v *compute.WorkloadType) { + if v == nil { + return + } + if x := &v.Clients; !walk.IsZeroStruct(x) || n.HasRefs("clients") { + c := n.Child("clients") + defaultsComputeClientsTypes(w, &c, x) + } +} + +func defaultsJobsContinuous(w *defaultsWalker, n *walk.Node, v *jobs.Continuous) { + if v == nil { + return + } + if !(v.PauseStatus != "") { + c := n.Child("pause_status") + w.unset(&c, 9) + } +} + +func defaultsJobsJobCluster(w *defaultsWalker, n *walk.Node, v *jobs.JobCluster) { + if v == nil { + return + } + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + defaultsComputeClusterSpec(w, &c, x) + } +} + +func defaultsJobsCronSchedule(w *defaultsWalker, n *walk.Node, v *jobs.CronSchedule) { + if v == nil { + return + } + if !(v.PauseStatus != "") { + c := n.Child("pause_status") + w.unset(&c, 7) + } +} + +func defaultsJobsTriggerSettings(w *defaultsWalker, n *walk.Node, v *jobs.TriggerSettings) { + if v == nil { + return + } + if !(v.PauseStatus != "") { + c := n.Child("pause_status") + w.unset(&c, 8) + } +} + +func defaultsComputeClientsTypes(w *defaultsWalker, n *walk.Node, v *compute.ClientsTypes) { + if v == nil { + return + } + if !(v.Notebooks || walk.Forced(v.ForceSendFields, "Notebooks") || n.IsRef("notebooks")) { + c := n.Child("notebooks") + w.unset(&c, 21) + } + if !(v.Jobs || walk.Forced(v.ForceSendFields, "Jobs") || n.IsRef("jobs")) { + c := n.Child("jobs") + w.unset(&c, 22) + } +} + +func defaultsComputeClusterSpec(w *defaultsWalker, n *walk.Node, v *compute.ClusterSpec) { + if v == nil { + return + } + if x := v.WorkloadType; x != nil { + c := n.Child("workload_type") + defaultsComputeWorkloadType2(w, &c, x) + } +} + +func defaultsComputeWorkloadType2(w *defaultsWalker, n *walk.Node, v *compute.WorkloadType) { + if v == nil { + return + } + if x := &v.Clients; !walk.IsZeroStruct(x) || n.HasRefs("clients") { + c := n.Child("clients") + defaultsComputeClientsTypes2(w, &c, x) + } +} + +func defaultsComputeClientsTypes2(w *defaultsWalker, n *walk.Node, v *compute.ClientsTypes) { + if v == nil { + return + } + if !(v.Notebooks || walk.Forced(v.ForceSendFields, "Notebooks") || n.IsRef("notebooks")) { + c := n.Child("notebooks") + w.unset(&c, 11) + } + if !(v.Jobs || walk.Forced(v.ForceSendFields, "Jobs") || n.IsRef("jobs")) { + c := n.Child("jobs") + w.unset(&c, 12) + } +} diff --git a/bundle/config/mutator/resourcemutator/expand_pipeline_glob_paths_test.go b/bundle/config/mutator/resourcemutator/expand_pipeline_glob_paths_test.go index 7b640df45e1..608ccbf155a 100644 --- a/bundle/config/mutator/resourcemutator/expand_pipeline_glob_paths_test.go +++ b/bundle/config/mutator/resourcemutator/expand_pipeline_glob_paths_test.go @@ -15,7 +15,6 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/require" @@ -235,8 +234,8 @@ func TestExpandGlobPathsInPipelinesKeepsOrder(t *testing.T) { {File: &pipelines.FileLibrary{Path: "a/1.py"}}, {File: &pipelines.FileLibrary{Path: "a/2.py"}}, {File: &pipelines.FileLibrary{Path: "/Workspace/x.py"}}, - // The empty notebook is kept; the round trip through the dynamic configuration forces it. - {Notebook: &pipelines.NotebookLibrary{}, ForceSendFields: []string{"Notebook"}}, + // The empty notebook is kept. + {Notebook: &pipelines.NotebookLibrary{}}, }, b.Config.Resources.Pipelines["pipeline"].Libraries) // The original library is not modified. @@ -265,8 +264,4 @@ func TestExpandGlobPathsInPipelinesWithoutLibraries(t *testing.T) { assert.Nil(t, b.Config.Resources.Pipelines["unset"].Libraries) assert.Equal(t, []pipelines.PipelineLibrary{}, b.Config.Resources.Pipelines["empty"].Libraries) - - v := b.Config.Value() - assert.False(t, v.Get("resources").Get("pipelines").Get("unset").Get("libraries").IsValid()) - assert.Equal(t, dyn.KindSequence, v.Get("resources").Get("pipelines").Get("empty").Get("libraries").Kind()) } diff --git a/bundle/config/mutator/resourcemutator/fix_permissions.go b/bundle/config/mutator/resourcemutator/fix_permissions.go index 9e6c785b0d9..0e28f3b3ac8 100644 --- a/bundle/config/mutator/resourcemutator/fix_permissions.go +++ b/bundle/config/mutator/resourcemutator/fix_permissions.go @@ -5,10 +5,12 @@ import ( "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/iamutil" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/iam" ) const ( @@ -52,165 +54,132 @@ func (m *fixPermissions) Name() string { return "FixPermissions" } -func processPermissions(currentUser string) dyn.MapFunc { - return func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - // Extract resource type from path: resources...permissions - if len(p) != 4 || p[0].Key() != "resources" || p[3].Key() != "permissions" { - return v, nil - } - - resourceType := p[1].Key() - if ignoredResources[resourceType] { - return v, nil - } - - v, err := ensureCurrentUserMgmtPermissions(v, currentUser, resourceType) - if err != nil { - return v, err - } - - return useMaximumLevel(v, resourceType) - } +// permission is a permission entry: a level and the principal it is granted to. +type permission struct { + level string + userName string + servicePrincipalName string + groupName string } -func readUser(v dyn.Value) string { - userName, _ := dyn.GetValue(v, "user_name").AsString() - if userName != "" { - return userName +func (p permission) user() string { + if p.userName != "" { + return p.userName } - servicePrincipalName, _ := dyn.GetValue(v, "service_principal_name").AsString() - return servicePrincipalName + return p.servicePrincipalName } -func readPrincipal(v dyn.Value) string { - value, _ := dyn.GetValue(v, "user_name").AsString() - if value != "" { - return "user_name:" + value - } - value, _ = dyn.GetValue(v, "service_principal_name").AsString() - if value != "" { - return "service_principal_name:" + value - } - value, _ = dyn.GetValue(v, "group_name").AsString() - if value != "" { - return "group_name:" + value +// principal returns the principal in the form ":", or "" if there is none. +func (p permission) principal() string { + switch { + case p.userName != "": + return "user_name:" + p.userName + case p.servicePrincipalName != "": + return "service_principal_name:" + p.servicePrincipalName + case p.groupName != "": + return "group_name:" + p.groupName } return "" } -func ensureCurrentUserMgmtPermissions(permissions dyn.Value, currentUser, resourceType string) (dyn.Value, error) { +func processPermissions(currentUser, resourceType string, permissions []permission) []permission { + return useMaximumLevel(ensureCurrentUserMgmtPermissions(permissions, currentUser, resourceType)) +} + +func ensureCurrentUserMgmtPermissions(permissions []permission, currentUser, resourceType string) []permission { currentUserHasIsOwner := false currentUserIndCanManage := -1 canAddIsOwner := hasIsOwner[resourceType] - permissionArray, ok := permissions.AsSequence() - if !ok { - return permissions, nil - } - - for ind, permission := range permissionArray { - level, ok := dyn.GetValue(permission, "level").AsString() - if !ok { + for ind, p := range permissions { + if p.level == "" { continue } - user := readUser(permission) - if level == isOwner { + user := p.user() + if p.level == isOwner { canAddIsOwner = false if user == currentUser { currentUserHasIsOwner = true } } - if user == currentUser && level == canManage { + if user == currentUser && p.level == canManage { currentUserIndCanManage = ind } } if currentUserHasIsOwner { - return dyn.V(permissionArray), nil + return permissions } if canAddIsOwner { if currentUserIndCanManage >= 0 { // Upgrade current user's CAN_MANAGE to IS_OWNER. We do this because terraform will add IS_OWNER if it does not see one // and that may confuse backend. We can stop doing it when removed terraform. - v, _ := dyn.Set(permissionArray[currentUserIndCanManage], "level", dyn.V(isOwner)) - permissionArray[currentUserIndCanManage] = v + permissions[currentUserIndCanManage].level = isOwner } else { - permissionArray = append(permissionArray, createPermission(currentUser, isOwner)) + permissions = append(permissions, createPermission(currentUser, isOwner)) } - return dyn.V(permissionArray), nil + return permissions } if currentUserIndCanManage < 0 { - permissionArray = append(permissionArray, createPermission(currentUser, canManage)) + permissions = append(permissions, createPermission(currentUser, canManage)) } - return dyn.V(permissionArray), nil + return permissions } -func useMaximumLevel(permissions dyn.Value, resourceType string) (dyn.Value, error) { - permissionArray, ok := permissions.AsSequence() - if !ok { - return permissions, nil - } - +func useMaximumLevel(permissions []permission) []permission { levelPerPrincipal := make(map[string]string) - principalIndex := make(map[string]int) + seen := make(map[string]bool) var principals []string - for _, permission := range permissionArray { - level, _ := dyn.GetValue(permission, "level").AsString() - if level == "" { + for _, p := range permissions { + if p.level == "" { continue } - principal := readPrincipal(permission) + principal := p.principal() if principal == "" { continue } - _, ok = principalIndex[principal] - if !ok { - ind := len(principalIndex) - principalIndex[principal] = ind + if !seen[principal] { + seen[principal] = true principals = append(principals, principal) } - levelPerPrincipal[principal] = resources.GetMaxLevel(levelPerPrincipal[principal], level) + levelPerPrincipal[principal] = resources.GetMaxLevel(levelPerPrincipal[principal], p.level) } - var newPermissions []dyn.Value - + var newPermissions []permission for _, principal := range principals { newPermissions = append(newPermissions, createPermissionFromPrincipal(principal, levelPerPrincipal[principal])) } - return dyn.V(newPermissions), nil + return newPermissions } -func createPermission(user, level string) dyn.Value { - permission := map[string]dyn.Value{ - "level": dyn.V(level), - } - +func createPermission(user, level string) permission { // Determine if currentUser is a service principal or user if iamutil.IsServicePrincipalName(user) { - permission["service_principal_name"] = dyn.V(user) - } else { - permission["user_name"] = dyn.V(user) + return permission{level: level, servicePrincipalName: user} } - - return dyn.V(permission) + return permission{level: level, userName: user} } -func createPermissionFromPrincipal(principal, level string) dyn.Value { - permission := map[string]dyn.Value{ - "level": dyn.V(level), - } - +func createPermissionFromPrincipal(principal, level string) permission { items := strings.SplitN(principal, ":", 2) field := items[0] value := items[1] - permission[field] = dyn.V(value) - return dyn.V(permission) + p := permission{level: level} + switch field { + case "user_name": + p.userName = value + case "service_principal_name": + p.servicePrincipalName = value + case "group_name": + p.groupName = value + } + return p } func (m *fixPermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { @@ -222,15 +191,38 @@ func (m *fixPermissions) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn } currentUser := b.Config.Workspace.CurrentUser.UserName - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - // Use MapByPattern to directly process permissions arrays - return dyn.MapByPattern(v, dyn.NewPattern( - dyn.Key("resources"), - dyn.AnyKey(), - dyn.AnyKey(), - dyn.Key("permissions"), - ), processPermissions(currentUser)) + b.Config.Resources.IterateAllPermissions(func(typ, name string, perms config.PermissionList) bool { + if ignoredResources[typ] || perms.IsNil() { + return true + } + + permissions := make([]permission, 0, perms.Len()) + for i := range perms.Len() { + p := perms.At(i) + permissions = append(permissions, permission{ + level: string(p.Level), + userName: p.UserName, + servicePrincipalName: p.ServicePrincipalName, + groupName: p.GroupName, + }) + } + + processed := processPermissions(currentUser, typ, permissions) + out := make([]resources.Permission, 0, len(processed)) + for _, p := range processed { + out = append(out, resources.Permission{ + Level: iam.PermissionLevel(p.level), + UserName: p.userName, + ServicePrincipalName: p.servicePrincipalName, + GroupName: p.groupName, + }) + } + perms.Set(out) + + // The permissions are rebuilt without locations. + b.Config.SetLocations(structpath.NewPath(nil, "resources", typ, name, "permissions"), nil) + return true }) - return diag.FromErr(err) + return nil } diff --git a/bundle/config/mutator/resourcemutator/inline_json.go b/bundle/config/mutator/resourcemutator/inline_json.go new file mode 100644 index 00000000000..ef91cfa93c7 --- /dev/null +++ b/bundle/config/mutator/resourcemutator/inline_json.go @@ -0,0 +1,38 @@ +package resourcemutator + +import ( + "encoding/json" + "reflect" +) + +// inlineJSON converts the value of a field that holds a JSON document as a string but +// may be authored as inline YAML. It returns the JSON encoding of an inline map and +// true; false if v is not set or already a string; and the kind of v (as reported in +// diagnostics) if it is neither. +func inlineJSON(v any) (string, bool, string, error) { + if v == nil { + return "", false, "", nil + } + if _, ok := v.(string); ok { + return "", false, "", nil + } + switch reflect.ValueOf(v).Kind() { + case reflect.Map, reflect.Struct: + b, err := json.Marshal(v) + if err != nil { + return "", false, "", err + } + return string(b), true, "", nil + case reflect.Slice, reflect.Array: + return "", false, "sequence", nil + case reflect.Bool: + return "", false, "bool", nil + case reflect.Float32, reflect.Float64: + return "", false, "float", nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return "", false, "int", nil + default: + return "", false, reflect.ValueOf(v).Kind().String(), nil + } +} diff --git a/bundle/config/mutator/resourcemutator/keyed_merge.go b/bundle/config/mutator/resourcemutator/keyed_merge.go new file mode 100644 index 00000000000..5f9c7ee26bc --- /dev/null +++ b/bundle/config/mutator/resourcemutator/keyed_merge.go @@ -0,0 +1,80 @@ +package resourcemutator + +import ( + "slices" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" +) + +// mergeByKey merges, for every resource in resources (visited in sorted order), the sequence +// at resources... by key, skipping sequences for which the merge +// is a no-op (see [keyedMergeIsNoop]). The key of an element is noopKey(element), which must +// agree with keyFn applied to the key field of the element. +func mergeByKey[R, E any]( + b *bundle.Bundle, + resourceType string, + resources map[string]*R, + field, keyField string, + elems func(*R) []E, + noopKey func(E) string, + keyFn func(structvar.View) string, + sortKeys bool, +) error { + names := make([]string, 0, len(resources)) + for name := range resources { + names = append(names, name) + } + slices.Sort(names) + for _, name := range names { + r := resources[name] + if r == nil { + continue + } + path := structpath.NewPath(nil, "resources", resourceType, name, field) + if keyedMergeIsNoop(b, path, elems(r), noopKey, sortKeys) { + continue + } + if err := b.Config.MergeElementsByKey(path, keyField, keyFn, sortKeys); err != nil { + return err + } + } + return nil +} + +// stringKey returns a key function for sequence elements whose key is a string +// (an absent key is the empty string). +func stringKey(what string) func(structvar.View) string { + return func(v structvar.View) string { + switch v.Kind() { + case structvar.KindInvalid, structvar.KindNil: + return "" + case structvar.KindString: + s, _ := v.AsString() + return s + default: + panic(what + " must be a string") + } + } +} + +// keyedMergeIsNoop reports whether merging the elements of the sequence at path by key +// leaves it unchanged: the sequence is not a reference, and every key is non-empty and +// unique (and sorted, if the merge sorts by key). The merge operates on the dynamic +// configuration, which is built from the whole typed configuration, so mutators skip +// it when it would have no effect. +func keyedMergeIsNoop[T any](b *bundle.Bundle, path *structpath.PathNode, elems []T, key func(T) string, sorted bool) bool { + if b.Config.IsReferenceAt(path) { + return false + } + keys := make([]string, 0, len(elems)) + for _, e := range elems { + k := key(e) + if k == "" || slices.Contains(keys, k) { + return false + } + keys = append(keys, k) + } + return !sorted || slices.IsSorted(keys) +} diff --git a/bundle/config/mutator/resourcemutator/merge_apps.go b/bundle/config/mutator/resourcemutator/merge_apps.go index edbdf7712ce..2a2b5931f48 100644 --- a/bundle/config/mutator/resourcemutator/merge_apps.go +++ b/bundle/config/mutator/resourcemutator/merge_apps.go @@ -2,11 +2,12 @@ package resourcemutator import ( "context" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/apps" ) type mergeApps struct{} @@ -19,27 +20,52 @@ func (m *mergeApps) Name() string { return "MergeApps" } -func (m *mergeApps) resourceName(v dyn.Value) string { - switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: - return "" - case dyn.KindString: - return v.MustString() - default: - panic("app name must be a string") +func (m *mergeApps) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { + names := make([]string, 0, len(b.Config.Resources.Apps)) + for name := range b.Config.Resources.Apps { + names = append(names, name) } -} + slices.Sort(names) -func (m *mergeApps) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - if v.Kind() == dyn.KindNil { - return v, nil + for _, name := range names { + app := b.Config.Resources.Apps[name] + if app == nil { + continue + } + path := structpath.NewPath(nil, "resources", "apps", name, "resources") + if keyedMergeIsNoop(b, path, app.Resources, func(r apps.AppResource) string { return r.Name }, false) { + continue } - return dyn.Map(v, "resources.apps", dyn.Foreach(func(_ dyn.Path, app dyn.Value) (dyn.Value, error) { - return dyn.Map(app, "resources", merge.ElementsByKeyWithOverride("name", m.resourceName)) - })) - }) + // Elements with the same name are overridden by the later ones. The result + // is ordered by the first appearance of the name. + var order []string + last := map[string]int{} + for i, r := range app.Resources { + if _, ok := last[r.Name]; !ok { + order = append(order, r.Name) + } + last[r.Name] = i + } - return diag.FromErr(err) + merged := make([]apps.AppResource, 0, len(order)) + sources := make([][]int, 0, len(order)) + for _, key := range order { + merged = append(merged, app.Resources[last[key]]) + sources = append(sources, []int{last[key]}) + } + app.Resources = merged + b.Config.UpdateSequence(path, sources) + + // The merge sets the key of every element, even if it is empty. + for i, r := range merged { + if r.Name != "" { + continue + } + if err := b.Config.Set(structpath.NewPath(path, i, "name"), ""); err != nil { + return diag.FromErr(err) + } + } + } + return nil } diff --git a/bundle/config/mutator/resourcemutator/merge_grants.go b/bundle/config/mutator/resourcemutator/merge_grants.go index 3966190a5ee..1011c1c2958 100644 --- a/bundle/config/mutator/resourcemutator/merge_grants.go +++ b/bundle/config/mutator/resourcemutator/merge_grants.go @@ -5,21 +5,11 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/databricks/databricks-sdk-go/service/catalog" ) -// Resource types that support grants. -var grantResourceTypes = []string{ - "catalogs", - "schemas", - "external_locations", - "secrets", - "volumes", - "registered_models", - "vector_search_indexes", -} - type mergeGrants struct{} // MergeGrants returns a mutator that deduplicates grant entries. @@ -33,58 +23,58 @@ func (m *mergeGrants) Name() string { } func (m *mergeGrants) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - if v.Kind() == dyn.KindNil { - return v, nil - } - - for _, resourceType := range grantResourceTypes { - var mapErr error - v, mapErr = dyn.Map(v, "resources."+resourceType, dyn.Foreach(func(_ dyn.Path, resource dyn.Value) (dyn.Value, error) { - // Merge grant entries by principal. This concatenates privileges - // for entries with the same principal via the standard merge semantics. - resource, err := dyn.Map(resource, "grants", merge.ElementsByKey("principal", func(v dyn.Value) string { - s, _ := v.AsString() - return s - })) - if err != nil { - return resource, err - } - - // Deduplicate privileges within each grant entry. - return dyn.Map(resource, "grants", dyn.Foreach(func(_ dyn.Path, grant dyn.Value) (dyn.Value, error) { - return dyn.Map(grant, "privileges", deduplicateSequence) - })) - })) - if mapErr != nil { - return v, mapErr - } - } - - return v, nil + var err error + b.Config.Resources.IterateAllGrants(func(typ, name string, grants *[]catalog.PrivilegeAssignment) bool { + err = mergeGrantsOf(b, typ, name, grants) + return err == nil }) - return diag.FromErr(err) } -// deduplicateSequence removes duplicate values from a dyn sequence, -// preserving the order of first appearance. -func deduplicateSequence(_ dyn.Path, v dyn.Value) (dyn.Value, error) { - elements, ok := v.AsSequence() - if !ok { - return v, nil +// mergeGrantsOf merges the grants of the resource name of type typ by principal, and +// deduplicates the privileges of each grant. +func mergeGrantsOf(b *bundle.Bundle, typ, name string, grants *[]catalog.PrivilegeAssignment) error { + if len(*grants) == 0 { + return nil + } + + // Merge grant entries by principal. This concatenates privileges + // for entries with the same principal via the standard merge semantics. + path := structpath.NewPath(nil, "resources", typ, name, "grants") + err := b.Config.MergeElementsByKey(path, "principal", func(v structvar.View) string { + s, _ := v.AsString() + return s + }, false) + if err != nil { + return err } - seen := make(map[string]bool, len(elements)) - out := make([]dyn.Value, 0, len(elements)) - for _, elem := range elements { - key, _ := elem.AsString() - if seen[key] { + // Deduplicate privileges within each grant entry (the merge replaced the slice). + gs := *grants + for i := range gs { + privileges, sources := deduplicatePrivileges(gs[i].Privileges) + if len(privileges) == len(gs[i].Privileges) { continue } - seen[key] = true - out = append(out, elem) + gs[i].Privileges = privileges + b.Config.UpdateSequence(structpath.NewPath(path, i, "privileges"), sources) } + return nil +} - return dyn.NewValue(out, v.Locations()), nil +// deduplicatePrivileges removes duplicate privileges, preserving the order of first +// appearance. It also returns, for every remaining privilege, its index in the input. +func deduplicatePrivileges(in []catalog.Privilege) ([]catalog.Privilege, [][]int) { + seen := make(map[catalog.Privilege]bool, len(in)) + out := make([]catalog.Privilege, 0, len(in)) + sources := make([][]int, 0, len(in)) + for i, p := range in { + if seen[p] { + continue + } + seen[p] = true + out = append(out, p) + sources = append(sources, []int{i}) + } + return out, sources } diff --git a/bundle/config/mutator/resourcemutator/merge_job_clusters.go b/bundle/config/mutator/resourcemutator/merge_job_clusters.go index c7c46a3fdef..9d63460dc50 100644 --- a/bundle/config/mutator/resourcemutator/merge_job_clusters.go +++ b/bundle/config/mutator/resourcemutator/merge_job_clusters.go @@ -4,9 +4,9 @@ import ( "context" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/databricks-sdk-go/service/jobs" ) type mergeJobClusters struct{} @@ -19,27 +19,10 @@ func (m *mergeJobClusters) Name() string { return "MergeJobClusters" } -func (m *mergeJobClusters) jobClusterKey(v dyn.Value) string { - switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: - return "" - case dyn.KindString: - return v.MustString() - default: - panic("job cluster key must be a string") - } -} - func (m *mergeJobClusters) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - if v.Kind() == dyn.KindNil { - return v, nil - } - - return dyn.Map(v, "resources.jobs", dyn.Foreach(func(_ dyn.Path, job dyn.Value) (dyn.Value, error) { - return dyn.Map(job, "job_clusters", merge.ElementsByKey("job_cluster_key", m.jobClusterKey)) - })) - }) - + err := mergeByKey(b, "jobs", b.Config.Resources.Jobs, "job_clusters", "job_cluster_key", + func(r *resources.Job) []jobs.JobCluster { return r.JobClusters }, + func(c jobs.JobCluster) string { return c.JobClusterKey }, + stringKey("job cluster key"), false) return diag.FromErr(err) } diff --git a/bundle/config/mutator/resourcemutator/merge_job_parameters.go b/bundle/config/mutator/resourcemutator/merge_job_parameters.go index 79458b36a60..8b1413e4bf6 100644 --- a/bundle/config/mutator/resourcemutator/merge_job_parameters.go +++ b/bundle/config/mutator/resourcemutator/merge_job_parameters.go @@ -4,9 +4,9 @@ import ( "context" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/databricks-sdk-go/service/jobs" ) type mergeJobParameters struct{} @@ -19,27 +19,10 @@ func (m *mergeJobParameters) Name() string { return "MergeJobParameters" } -func (m *mergeJobParameters) parameterNameString(v dyn.Value) string { - switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: - return "" - case dyn.KindString: - return v.MustString() - default: - panic("task key must be a string") - } -} - func (m *mergeJobParameters) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - if v.Kind() == dyn.KindNil { - return v, nil - } - - return dyn.Map(v, "resources.jobs", dyn.Foreach(func(_ dyn.Path, job dyn.Value) (dyn.Value, error) { - return dyn.Map(job, "parameters", merge.ElementsByKey("name", m.parameterNameString)) - })) - }) - + err := mergeByKey(b, "jobs", b.Config.Resources.Jobs, "parameters", "name", + func(r *resources.Job) []jobs.JobParameterDefinition { return r.Parameters }, + func(p jobs.JobParameterDefinition) string { return p.Name }, + stringKey("parameter name"), false) return diag.FromErr(err) } diff --git a/bundle/config/mutator/resourcemutator/merge_job_tasks.go b/bundle/config/mutator/resourcemutator/merge_job_tasks.go index b85a863d80f..562157a46a6 100644 --- a/bundle/config/mutator/resourcemutator/merge_job_tasks.go +++ b/bundle/config/mutator/resourcemutator/merge_job_tasks.go @@ -4,9 +4,9 @@ import ( "context" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/databricks-sdk-go/service/jobs" ) type mergeJobTasks struct{} @@ -19,31 +19,14 @@ func (m *mergeJobTasks) Name() string { return "MergeJobTasks" } -func (m *mergeJobTasks) taskKeyString(v dyn.Value) string { - switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: - return "" - case dyn.KindString: - return v.MustString() - default: - panic("task key must be a string") - } -} - func (m *mergeJobTasks) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - if v.Kind() == dyn.KindNil { - return v, nil - } - - return dyn.Map(v, "resources.jobs", dyn.Foreach(func(_ dyn.Path, job dyn.Value) (dyn.Value, error) { - // Sorting keys here since it'll be sorted by TF anyway - // https://github.com/databricks/terraform-provider-databricks/blob/0a932c2/jobs/resource_job.go#L343 - // However, if we don't sort we have a difference between direct and TF and between configs in - // "bundle validate" and configs sent to backend. - return dyn.Map(job, "tasks", merge.ElementsBySortedKey("task_key", m.taskKeyString)) - })) - }) - + // Sorting keys here since it'll be sorted by TF anyway + // https://github.com/databricks/terraform-provider-databricks/blob/0a932c2/jobs/resource_job.go#L343 + // However, if we don't sort we have a difference between direct and TF and between configs in + // "bundle validate" and configs sent to backend. + err := mergeByKey(b, "jobs", b.Config.Resources.Jobs, "tasks", "task_key", + func(r *resources.Job) []jobs.Task { return r.Tasks }, + func(t jobs.Task) string { return t.TaskKey }, + stringKey("task key"), true) return diag.FromErr(err) } diff --git a/bundle/config/mutator/resourcemutator/merge_pipeline_clusters.go b/bundle/config/mutator/resourcemutator/merge_pipeline_clusters.go index 6496a00d90e..622f15bfec4 100644 --- a/bundle/config/mutator/resourcemutator/merge_pipeline_clusters.go +++ b/bundle/config/mutator/resourcemutator/merge_pipeline_clusters.go @@ -5,9 +5,10 @@ import ( "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/databricks/databricks-sdk-go/service/pipelines" ) type mergePipelineClusters struct{} @@ -20,29 +21,31 @@ func (m *mergePipelineClusters) Name() string { return "MergePipelineClusters" } -func (m *mergePipelineClusters) clusterLabel(v dyn.Value) string { +func (m *mergePipelineClusters) clusterLabel(v structvar.View) string { switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: + case structvar.KindInvalid, structvar.KindNil: // Note: the cluster label is optional and defaults to 'default'. // We therefore ALSO merge all clusters without a label. return "default" - case dyn.KindString: - return strings.ToLower(v.MustString()) + case structvar.KindString: + s, _ := v.AsString() + return strings.ToLower(s) default: panic("task key must be a string") } } func (m *mergePipelineClusters) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - if v.Kind() == dyn.KindNil { - return v, nil - } - - return dyn.Map(v, "resources.pipelines", dyn.Foreach(func(_ dyn.Path, pipeline dyn.Value) (dyn.Value, error) { - return dyn.Map(pipeline, "clusters", merge.ElementsByKey("label", m.clusterLabel)) - })) - }) - + // The merge also lowercases labels and sets the "default" label, so only + // lowercase labels are left unchanged. + err := mergeByKey(b, "pipelines", b.Config.Resources.Pipelines, "clusters", "label", + func(r *resources.Pipeline) []pipelines.PipelineCluster { return r.Clusters }, + func(c pipelines.PipelineCluster) string { + if c.Label != strings.ToLower(c.Label) { + return "" + } + return c.Label + }, + m.clusterLabel, false) return diag.FromErr(err) } diff --git a/bundle/config/mutator/resourcemutator/process_static_resources_test.go b/bundle/config/mutator/resourcemutator/process_static_resources_test.go index f23242d58e0..9c4dc40025f 100644 --- a/bundle/config/mutator/resourcemutator/process_static_resources_test.go +++ b/bundle/config/mutator/resourcemutator/process_static_resources_test.go @@ -2,13 +2,12 @@ package resourcemutator import ( "reflect" + "strings" "testing" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -53,11 +52,12 @@ func TestGetAllResourcesMatchesConfigKeys(t *testing.T) { field.Set(m) } - root, err := convert.FromTyped(b.Config, dyn.NilValue) - require.NoError(t, err) + // The key of a resource type is the JSON name of its field in Resources. expected := NewResourceKeySet() - err = expected.AddPattern(dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), root) - require.NoError(t, err) + for i := range rv.NumField() { + typ, _, _ := strings.Cut(rv.Type().Field(i).Tag.Get("json"), ",") + expected.AddResourceKey(ResourceKey{Type: typ, Name: "my_resource"}) + } require.Equal(t, rv.NumField(), expected.Size()) set, err := getAllResources(b) diff --git a/bundle/config/mutator/resourcemutator/resource_key.go b/bundle/config/mutator/resourcemutator/resource_key.go index 699867b4f40..dffc85b59f4 100644 --- a/bundle/config/mutator/resourcemutator/resource_key.go +++ b/bundle/config/mutator/resourcemutator/resource_key.go @@ -1,10 +1,6 @@ package resourcemutator -import ( - "errors" - - "github.com/databricks/cli/libs/dyn" -) +import "github.com/databricks/cli/bundle/config" // ResourceKey uniquely identifies a resource in configuration. type ResourceKey struct { @@ -47,24 +43,22 @@ func (r ResourceKeySet) Size() int { return size } -// AddPattern adds all resource keys that match the pattern. -func (r ResourceKeySet) AddPattern(pattern dyn.Pattern, root dyn.Value) error { - if len(pattern) != 3 { - return errors.New("pattern must have 3 keys") - } +// AddResources adds the keys of all resources in r. +func (r ResourceKeySet) AddResources(resources *config.Resources) { + resources.IterateAllResources(func(typ, name string, _ config.ConfigResource) bool { + r.AddResourceKey(ResourceKey{Type: typ, Name: name}) + return true + }) +} - _, err := dyn.MapByPattern(root, pattern, func(path dyn.Path, v dyn.Value) (dyn.Value, error) { - parsed, err := getResourceKey(path) - if err != nil { - return dyn.InvalidValue, err +// AddResourcesOfType adds the keys of the resources of type typ in r. +func (r ResourceKeySet) AddResourcesOfType(resources *config.Resources, typ string) { + resources.IterateAllResources(func(t, name string, _ config.ConfigResource) bool { + if t == typ { + r.AddResourceKey(ResourceKey{Type: typ, Name: name}) } - - r.AddResourceKey(parsed) - - return v, nil + return true }) - - return err } // Types returns the types of all resources in the set. @@ -104,25 +98,3 @@ func (r ResourceKeySet) ToArray() []ResourceKey { return result } - -func getResourceKey(path dyn.Path) (ResourceKey, error) { - if len(path) < 3 { - return ResourceKey{}, errors.New("can't parse resource key") - } - - if path[0].Key() != "resources" { - return ResourceKey{}, errors.New("can't parse resource key") - } - - resourceType := path[1].Key() - resourceName := path[2].Key() - - if resourceType == "" || resourceName == "" { - return ResourceKey{}, errors.New("can't parse resource key") - } - - return ResourceKey{ - Type: resourceType, - Name: resourceName, - }, nil -} diff --git a/bundle/config/mutator/resourcemutator/resource_key_test.go b/bundle/config/mutator/resourcemutator/resource_key_test.go index 1af72a9ba2d..ebbcc37710d 100644 --- a/bundle/config/mutator/resourcemutator/resource_key_test.go +++ b/bundle/config/mutator/resourcemutator/resource_key_test.go @@ -3,112 +3,36 @@ package resourcemutator import ( "testing" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" - - "github.com/databricks/cli/libs/dyn" ) -type getResourceKeyTestCase struct { - path string - key ResourceKey - err bool -} - -func TestGetResourceKey(t *testing.T) { - testCases := []getResourceKeyTestCase{ - { - path: "resources.jobs.job_1", - key: ResourceKey{ - Type: "jobs", - Name: "job_1", - }, - }, - { - path: "resources.jobs.job_1.name", - key: ResourceKey{ - Type: "jobs", - Name: "job_1", - }, - }, - { - path: "resources.jobs", - err: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.path, func(t *testing.T) { - key, err := getResourceKey(dyn.MustPathFromString(tc.path)) - if tc.err { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tc.key, key) - } - }) +func TestResourceKeySet_AddResources(t *testing.T) { + var r config.Resources + r.Jobs = map[string]*resources.Job{ + "job_1": {JobSettings: jobs.JobSettings{Name: "job_1"}}, + "job_2": nil, } -} - -type resourceKeySetAddTestCase struct { - name string - pattern dyn.Pattern - root dyn.Value - expected []ResourceKey -} - -func TestResourceKeySet_AddPattern(t *testing.T) { - root := dyn.V(map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "jobs": dyn.V(map[string]dyn.Value{ - "job_1": dyn.V(map[string]dyn.Value{ - "name": dyn.V("job_1"), - }), - "job_2": dyn.V(map[string]dyn.Value{ - "name": dyn.V("job_2"), - }), - }), - }), - }) - - testCases := []resourceKeySetAddTestCase{ - { - name: "one job pattern", - pattern: dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.Key("job_1")), - root: root, - expected: []ResourceKey{ - { - Type: "jobs", - Name: "job_1", - }, - }, - }, - { - name: "all resources pattern", - pattern: dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), - root: root, - expected: []ResourceKey{ - { - Type: "jobs", - Name: "job_1", - }, - { - Type: "jobs", - Name: "job_2", - }, - }, - }, + r.Pipelines = map[string]*resources.Pipeline{ + "pipeline_1": {}, } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - set := NewResourceKeySet() - - err := set.AddPattern(tc.pattern, tc.root) - - assert.NoError(t, err) - assert.ElementsMatch(t, tc.expected, set.ToArray()) - }) - } + all := NewResourceKeySet() + all.AddResources(&r) + assert.ElementsMatch(t, []ResourceKey{ + {Type: "jobs", Name: "job_1"}, + {Type: "jobs", Name: "job_2"}, + {Type: "pipelines", Name: "pipeline_1"}, + }, all.ToArray()) + + jobKeys := NewResourceKeySet() + jobKeys.AddResourcesOfType(&r, "jobs") + assert.ElementsMatch(t, []ResourceKey{ + {Type: "jobs", Name: "job_1"}, + {Type: "jobs", Name: "job_2"}, + }, jobKeys.ToArray()) } func TestResourceKeySet_AddResourceKey(t *testing.T) { diff --git a/bundle/config/mutator/resourcemutator/resource_mutator.go b/bundle/config/mutator/resourcemutator/resource_mutator.go index 2262ad6c0a5..eff0ac03aaa 100644 --- a/bundle/config/mutator/resourcemutator/resource_mutator.go +++ b/bundle/config/mutator/resourcemutator/resource_mutator.go @@ -2,17 +2,14 @@ package resourcemutator import ( "context" - "errors" - "fmt" "github.com/databricks/cli/bundle/config/mutator" "github.com/databricks/cli/bundle/config/validate" - "github.com/databricks/cli/libs/dyn/merge" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/libs/dyn" ) // When a new resource is added to configuration, we apply bundle @@ -46,64 +43,12 @@ func applyInitializeMutators(ctx context.Context, b *bundle.Bundle) { return } - defaults := []struct { - pattern string - value any - }{ - {"resources.dashboards.*.parent_path", b.Config.Workspace.ResourcePath}, - {"resources.dashboards.*.embed_credentials", false}, - {"resources.genie_spaces.*.parent_path", b.Config.Workspace.ResourcePath}, - {"resources.volumes.*.volume_type", "MANAGED"}, - - {"resources.alerts.*.parent_path", b.Config.Workspace.ResourcePath}, - - // Jobs: - - // The defaults are the same as for terraform provider latest version (v1.75.0) - // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/jobs/resource_job.go#L532 - {"resources.jobs.*.name", "Untitled"}, - {"resources.jobs.*.max_concurrent_runs", 1}, - {"resources.jobs.*.schedule.pause_status", "UNPAUSED"}, - {"resources.jobs.*.trigger.pause_status", "UNPAUSED"}, - {"resources.jobs.*.continuous.pause_status", "UNPAUSED"}, - - // Enable queueing for jobs by default, following the behavior from API 2.2+. - // As of 2024-04, we're still using API 2.1 which has queueing disabled by default. - {"resources.jobs.*.queue", map[string]dyn.Value{ - "enabled": dyn.V(true), - }}, - - // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/clusters/resource_cluster.go - {"resources.jobs.*.job_clusters[*].new_cluster.workload_type.clients.notebooks", true}, - {"resources.jobs.*.job_clusters[*].new_cluster.workload_type.clients.jobs", true}, - - // Pipelines (same as terraform) - // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/pipelines/resource_pipeline.go#L253 - {"resources.pipelines.*.edition", "ADVANCED"}, - {"resources.pipelines.*.channel", "CURRENT"}, - - // SqlWarehouses (same as terraform) - // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/sql/resource_sql_endpoint.go#L59 - {"resources.sql_warehouses.*.auto_stop_mins", 120}, - {"resources.sql_warehouses.*.enable_photon", true}, - {"resources.sql_warehouses.*.max_num_clusters", 1}, - {"resources.sql_warehouses.*.spot_instance_policy", "COST_OPTIMIZED"}, - - // Apps: - {"resources.apps.*.description", ""}, - - // Clusters (same as terraform) - // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/clusters/resource_cluster.go#L315 - {"resources.clusters.*.autotermination_minutes", 60}, - {"resources.clusters.*.workload_type.clients.notebooks", true}, - {"resources.clusters.*.workload_type.clients.jobs", true}, + // Set the default values of resource fields that are not set. + if err := applyResourceDefaults(b); err != nil { + logdiag.LogError(ctx, err) } - - for _, defaultDef := range defaults { - bundle.SetDefault(ctx, b, defaultDef.pattern, defaultDef.value) - if logdiag.HasError(ctx) { - return - } + if logdiag.HasError(ctx) { + return } bundle.ApplySeqContext(ctx, b, @@ -220,17 +165,7 @@ func NormalizeAndInitializeResources( return } - var snapshot dyn.Value - - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - snapshot = root - - return selectResources(root, addedResources) - }) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to select resources: %w", err)) - return - } + restore := selectResources(b, addedResources) applyNormalizeMutators(ctx, b) if logdiag.HasError(ctx) { @@ -242,13 +177,8 @@ func NormalizeAndInitializeResources( return } - // after mutators, we merge updated resources back to snapshot to preserve non-selected resources - err = b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - return mergeResources(root, snapshot) - }) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to merge resources: %w", err)) - } + // after mutators, we merge updated resources back to the snapshot to preserve non-selected resources + restore() } // NormalizeResources normalizes resources specified resources, @@ -262,17 +192,7 @@ func NormalizeResources( return } - var snapshot dyn.Value - - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - snapshot = root - - return selectResources(root, updatedResources) - }) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to select resources: %w", err)) - return - } + restore := selectResources(b, updatedResources) applyNormalizeMutators(ctx, b) if logdiag.HasError(ctx) { @@ -290,129 +210,32 @@ func NormalizeResources( return } - // after mutators, we merge updated resources back to snapshot to preserve non-selected resources - err = b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - return mergeResources(root, snapshot) - }) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("failed to merge resources: %w", err)) - } -} - -// selectResources returns bundle configuration with resources only present in resourcePaths. -func selectResources(root dyn.Value, resourcePaths ResourceKeySet) (dyn.Value, error) { - resourcesKeyString := "resources" - resourcesPath := dyn.NewPath(dyn.Key(resourcesKeyString)) - - newRoot := root - var err error - - // remove resource types that are not in resourcePaths - newRoot, err = dyn.MapByPath( - newRoot, - resourcesPath, - func(p dyn.Path, resources dyn.Value) (dyn.Value, error) { - return merge.Select(resources, resourcePaths.Types()) - }, - ) - if err != nil { - return dyn.InvalidValue, err - } - - // for each resource type, remove resources by name - for _, resourceType := range resourcePaths.Types() { - resourceTypePath := resourcesPath.Append(dyn.Key(resourceType)) - - newRoot, err = dyn.MapByPath( - newRoot, - resourceTypePath, - func(p dyn.Path, resource dyn.Value) (dyn.Value, error) { - return merge.Select(resource, resourcePaths.Names(resourceType)) - }, - ) - if err != nil { - return dyn.InvalidValue, err - } - } - - return newRoot, err + // after mutators, we merge updated resources back to the snapshot to preserve non-selected resources + restore() } -// mergeResources returns bundle configuration by merging all resources from src into dst, -// overriding existing resources if they exist. -func mergeResources(src, dst dyn.Value) (dyn.Value, error) { - resourcesKey := dyn.Key("resources") - - newDst := dst - - // merge 'resources..' - _, err := dyn.MapByPattern( - src, - dyn.NewPattern(resourcesKey, dyn.AnyKey(), dyn.AnyKey()), - func(path dyn.Path, v dyn.Value) (dyn.Value, error) { - // if parent 'resources.' doesn't exist, handle it on the next pass - updated, _ := dyn.SetByPath(newDst, path, v) - if !updated.IsValid() { - return v, nil - } else { - newDst = updated - } - - return v, nil - }, - ) - if err != nil { - return newDst, err - } - - // merge 'resources.' - _, err = dyn.MapByPattern( - src, - dyn.NewPattern(resourcesKey, dyn.AnyKey()), - func(path dyn.Path, v dyn.Value) (dyn.Value, error) { - // if already exists, we already handled it in the previous pass - existing, _ := dyn.GetByPath(newDst, path) - if existing.IsValid() { - return v, nil - } - - // if parent 'resources' doesn't exist, handle it on the next pass - updated, _ := dyn.SetByPath(newDst, path, v) - if !updated.IsValid() { - return v, nil - } else { - newDst = updated - return v, nil +// selectResources removes all resources except the ones in resourceKeys from the bundle +// configuration. It returns a function that restores the removed resources: it puts the +// configuration back to what it was before, except for the selected resources, which take +// the values they have at that point. Any other changes to the configuration are discarded. +func selectResources(b *bundle.Bundle, resourceKeys ResourceKeySet) func() { + // A shallow copy of the configuration: the resources maps are replaced below, not + // modified, so the copy keeps all resources. + snapshot := b.Config + b.Config.Resources = b.Config.Resources.Select(resourceKeys) + + return func() { + updated := b.Config + b.Config = snapshot + + for resourceType, names := range resourceKeys { + for name := range names { + // Keep the resource that was updated, it may have more than the configuration describes. + if !b.Config.Resources.CopyResource(&updated.Resources, resourceType, name) { + continue + } + b.Config.CopyMetadata(&updated, structpath.NewPath(nil, "resources", resourceType, name)) } - }, - ) - if err != nil { - return newDst, err - } - - // merge 'resources' - _, err = dyn.MapByPattern( - src, - dyn.NewPattern(resourcesKey), - func(path dyn.Path, v dyn.Value) (dyn.Value, error) { - // if already exists, we already handled it in the previous pass - existing, _ := dyn.GetByPath(newDst, path) - if existing.IsValid() { - return v, nil - } - - updated, _ := dyn.SetByPath(newDst, path, v) - if !updated.IsValid() { - return v, errors.New("failed to update resources") - } else { - newDst = updated - return v, nil - } - }, - ) - if err != nil { - return newDst, err + } } - - return newDst, nil } diff --git a/bundle/config/mutator/resourcemutator/resource_mutator_test.go b/bundle/config/mutator/resourcemutator/resource_mutator_test.go index 42aee5bf883..d95020ed113 100644 --- a/bundle/config/mutator/resourcemutator/resource_mutator_test.go +++ b/bundle/config/mutator/resourcemutator/resource_mutator_test.go @@ -3,114 +3,76 @@ package resourcemutator import ( "testing" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynassert" + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -type mergeResourcesTestCase struct { - name string - src dyn.Value - dst dyn.Value - expected dyn.Value +func newJob(name string) *resources.Job { + return &resources.Job{Name: name} } -func TestMergeResources(t *testing.T) { - job1 := dyn.V("job_1") - job2 := dyn.V("job_2") - job3 := dyn.V("job_3") - - testCases := []mergeResourcesTestCase{ - { - name: "add resources to empty bundle (1)", - src: mapOf("resources", mapOf("jobs", mapOf("job_1", job1))), - dst: mapOf("resources", emptyMap()), - expected: mapOf("resources", mapOf("jobs", mapOf("job_1", job1))), - }, - { - name: "add resources to empty bundle (2)", - src: mapOf("resources", mapOf("jobs", mapOf("job_1", job1))), - dst: emptyMap(), - expected: mapOf("resources", mapOf("jobs", mapOf("job_1", job1))), - }, - { - name: "add new resource", - src: mapOf("resources", mapOf("jobs", mapOf("job_1", job1))), - dst: mapOf("resources", mapOf("jobs", mapOf("job_2", job2))), - expected: mapOf("resources", mapOf("jobs", mapOf2("job_1", job1, "job_2", job2))), - }, - { - name: "override resource", - src: mapOf("resources", mapOf("jobs", mapOf("job_1", job3))), - dst: mapOf("resources", mapOf("jobs", mapOf2("job_1", job1, "job_2", job2))), - expected: mapOf("resources", mapOf("jobs", mapOf2("job_1", job3, "job_2", job2))), +func TestSelectResources(t *testing.T) { + job1 := newJob("job_1") + job2 := newJob("job_2") + + b := &bundle.Bundle{Config: config.Root{ + Resources: config.Resources{ + Jobs: map[string]*resources.Job{"job_1": job1, "job_2": job2}, + Pipelines: map[string]*resources.Pipeline{"pipeline_1": {}}, }, - } + }} - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual, err := mergeResources(tc.src, tc.dst) + keys := NewResourceKeySet() + keys.AddResourceKey(ResourceKey{Type: "jobs", Name: "job_1"}) - assert.NoError(t, err) - dynassert.Equal(t, tc.expected, actual) - }) - } -} + restore := selectResources(b, keys) + assert.Equal(t, map[string]*resources.Job{"job_1": job1}, b.Config.Resources.Jobs) + assert.Nil(t, b.Config.Resources.Pipelines) -type selectResourcesTestCase struct { - name string - config dyn.Value - resourcePaths []ResourceKey - expected dyn.Value + restore() + assert.Equal(t, map[string]*resources.Job{"job_1": job1, "job_2": job2}, b.Config.Resources.Jobs) + assert.Len(t, b.Config.Resources.Pipelines, 1) } -func TestSelectResources(t *testing.T) { - job1 := dyn.V("job_1") - job2 := dyn.V("job_2") - - testCases := []selectResourcesTestCase{ - { - name: "extract resources", - config: mapOf("resources", mapOf("jobs", mapOf2("job_1", job1, "job_2", job2))), - resourcePaths: []ResourceKey{ - { - Type: "jobs", - Name: "job_1", - }, - }, - expected: mapOf("resources", mapOf("jobs", mapOf("job_1", job1))), - }, - { - name: "extract no resources", - config: mapOf("resources", mapOf("jobs", mapOf2("job_1", job1, "job_2", job2))), - resourcePaths: []ResourceKey{}, - expected: mapOf("resources", emptyMap()), +func TestSelectResourcesRestoreMergesUpdates(t *testing.T) { + b := &bundle.Bundle{Config: config.Root{ + Resources: config.Resources{ + Jobs: map[string]*resources.Job{"job_1": newJob("job_1"), "job_2": newJob("job_2")}, }, - } + }} + b.Config.Bundle.Name = "before" - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - resourceKeySet := NewResourceKeySet() - for _, key := range tc.resourcePaths { - resourceKeySet.AddResourceKey(key) - } + keys := NewResourceKeySet() + keys.AddResourceKey(ResourceKey{Type: "jobs", Name: "job_1"}) - actual, err := selectResources(tc.config, resourceKeySet) + restore := selectResources(b, keys) - assert.NoError(t, err) - assert.Equal(t, tc.expected, actual) - }) - } -} + // Changes to selected resources are kept, other changes are discarded. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.job_1.name"), "updated")) + b.Config.Bundle.Name = "after" -func mapOf(key string, value dyn.Value) dyn.Value { - return dyn.V(map[string]dyn.Value{key: value}) + restore() + assert.Equal(t, "updated", b.Config.Resources.Jobs["job_1"].Name) + assert.Equal(t, "job_2", b.Config.Resources.Jobs["job_2"].Name) + assert.Equal(t, "before", b.Config.Bundle.Name) } -func mapOf2(k0 string, v0 dyn.Value, k1 string, v1 dyn.Value) dyn.Value { - return dyn.V(map[string]dyn.Value{k0: v0, k1: v1}) -} +func TestSelectResourcesRestoreKeepsResourceType(t *testing.T) { + b := &bundle.Bundle{Config: config.Root{ + Resources: config.Resources{ + Jobs: map[string]*resources.Job{"job_1": newJob("job_1")}, + }, + }} + + keys := NewResourceKeySet() + keys.AddResourceKey(ResourceKey{Type: "jobs", Name: "job_1"}) -func emptyMap() dyn.Value { - return dyn.V(map[string]dyn.Value{}) + restore := selectResources(b, keys) + restore() + assert.Equal(t, "job_1", b.Config.Resources.Jobs["job_1"].Name) + assert.Nil(t, b.Config.Resources.Pipelines) } diff --git a/bundle/config/mutator/resourcemutator/run_as.go b/bundle/config/mutator/resourcemutator/run_as.go index 65279c7f212..21751e32813 100644 --- a/bundle/config/mutator/resourcemutator/run_as.go +++ b/bundle/config/mutator/resourcemutator/run_as.go @@ -8,7 +8,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/pipelines" @@ -220,17 +219,17 @@ func (m *setRunAs) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { b.Metrics.AddBoolValue("experimental.use_legacy_run_as", b.Config.Experimental != nil && b.Config.Experimental.UseLegacyRunAs) // Track whether top level run_as is set. - b.Metrics.AddBoolValue("run_as_set", b.Config.Value().Get("run_as").Kind() != dyn.KindInvalid) + b.Metrics.AddBoolValue("run_as_set", b.Config.RunAs != nil) // Mutator is a no-op if run_as is not specified in the bundle - if b.Config.Value().Get("run_as").Kind() == dyn.KindInvalid { + if b.Config.RunAs == nil { return nil } // User has opted to use the legacy behavior of run_as with the // experimental.use_legacy_run_as flag. if b.Config.Experimental != nil && b.Config.Experimental.UseLegacyRunAs { - if b.Config.Value().Get("run_as").Get("group_name").Kind() != dyn.KindInvalid { + if b.Config.RunAs.GroupName != "" || slices.Contains(b.Config.RunAs.ForceSendFields, "GroupName") { return diag.Errorf("run_as.group_name is not supported with experimental.use_legacy_run_as; disable experimental.use_legacy_run_as to use a group identity") } setPipelineOwnersToRunAsIdentity(b) diff --git a/bundle/config/mutator/resourcemutator/run_as_test.go b/bundle/config/mutator/resourcemutator/run_as_test.go index 200939f2dd0..eaaaa1a64cd 100644 --- a/bundle/config/mutator/resourcemutator/run_as_test.go +++ b/bundle/config/mutator/resourcemutator/run_as_test.go @@ -11,8 +11,6 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/pipelines" @@ -23,18 +21,15 @@ import ( func allResourceTypes(t *testing.T) []string { // Compute supported resource types based on the `Resources{}` struct. - r := &config.Resources{} - rv, err := convert.FromTyped(r, dyn.NilValue) - require.NoError(t, err) - normalized, _ := convert.Normalize(r, rv, convert.IncludeMissingFields) var resourceTypes []string - for _, k := range normalized.MustMap().Keys() { - resourceTypes = append(resourceTypes, k.MustString()) + for f := range reflect.TypeFor[config.Resources]().Fields() { + name, _, _ := strings.Cut(f.Tag.Get("json"), ",") + resourceTypes = append(resourceTypes, name) } slices.Sort(resourceTypes) // Assert the total list of resource supported, as a sanity check that using - // the dyn library gives us the correct list of all resources supported. Please + // the struct gives us the correct list of all resources supported. Please // also update this check when adding a new resource require.Equal( t, []string{ @@ -300,14 +295,6 @@ func TestRunAsErrorForUnsupportedResources(t *testing.T) { }, } - v, err := convert.FromTyped(base, dyn.NilValue) - require.NoError(t, err) - - // Define top level resources key in the bundle configuration. - // This is not part of the typed configuration, so we need to add it manually. - v, err = dyn.Set(v, "resources", dyn.V(map[string]dyn.Value{})) - require.NoError(t, err) - for _, rt := range allResourceTypes(t) { // Skip allowed resources if slices.Contains(allowList, rt) { @@ -316,23 +303,16 @@ func TestRunAsErrorForUnsupportedResources(t *testing.T) { // Add an instance of the resource type that is not on the allow list to // the bundle configuration. - nv, err := dyn.SetByPath(v, dyn.NewPath(dyn.Key("resources"), dyn.Key(rt)), dyn.V(map[string]dyn.Value{ - "foo": dyn.V(map[string]dyn.Value{ - "path": dyn.V("bar"), - }), - })) - require.NoError(t, err) - - // Get back typed configuration from the newly created invalid bundle configuration. - r := &config.Root{} - err = convert.ToTyped(r, nv) - require.NoError(t, err) + r, diags := config.LoadFromBytes("databricks.yml", fmt.Appendf(nil, "resources:\n %s:\n foo:\n path: bar\n", rt)) + require.NoError(t, diags.Error()) + r.Workspace = base.Workspace + r.RunAs = base.RunAs // Assert this invalid bundle configuration fails validation. b := &bundle.Bundle{ Config: *r, } - diags := bundle.Apply(t.Context(), b, SetRunAs()) + diags = bundle.Apply(t.Context(), b, SetRunAs()) require.Error(t, diags.Error()) assert.Contains(t, diags.Error().Error(), "do not support a setting a run_as user that is different from the owner.\n"+ "Current identity: alice. Run as identity: bob.\n"+ @@ -354,14 +334,6 @@ func TestRunAsNoErrorForSupportedResources(t *testing.T) { }, } - v, err := convert.FromTyped(base, dyn.NilValue) - require.NoError(t, err) - - // Define top level resources key in the bundle configuration. - // This is not part of the typed configuration, so we need to add it manually. - v, err = dyn.Set(v, "resources", dyn.V(map[string]dyn.Value{})) - require.NoError(t, err) - for _, rt := range allResourceTypes(t) { // Skip unsupported resources if !slices.Contains(allowList, rt) { @@ -370,23 +342,16 @@ func TestRunAsNoErrorForSupportedResources(t *testing.T) { // Add an instance of the resource type that is not on the allow list to // the bundle configuration. - nv, err := dyn.SetByPath(v, dyn.NewPath(dyn.Key("resources"), dyn.Key(rt)), dyn.V(map[string]dyn.Value{ - "foo": dyn.V(map[string]dyn.Value{ - "name": dyn.V("bar"), - }), - })) - require.NoError(t, err) - - // Get back typed configuration from the newly created invalid bundle configuration. - r := &config.Root{} - err = convert.ToTyped(r, nv) - require.NoError(t, err) + r, diags := config.LoadFromBytes("databricks.yml", fmt.Appendf(nil, "resources:\n %s:\n foo:\n name: bar\n", rt)) + require.NoError(t, diags.Error()) + r.Workspace = base.Workspace + r.RunAs = base.RunAs // Assert this configuration passes validation. b := &bundle.Bundle{ Config: *r, } - diags := bundle.Apply(t.Context(), b, SetRunAs()) + diags = bundle.Apply(t.Context(), b, SetRunAs()) require.NoError(t, diags.Error()) } } @@ -396,7 +361,8 @@ func TestRunAsIdentities(t *testing.T) { runAs string wantError bool }{ - {`null`, true}, + // A null run_as is the same as not specifying it. + {`null`, false}, {`{}`, true}, {`{user_name: ""}`, true}, {`{service_principal_name: ""}`, true}, @@ -455,14 +421,14 @@ func TestRunAsGroupResources(t *testing.T) { r, diags := config.LoadFromBytes("databricks.yml", []byte(yaml)) require.NoError(t, diags.Error()) b := &bundle.Bundle{Config: *r} - before := b.Config.Value().Get("resources") + before := b.Config.View().Get("resources").AsAny() diags = bundle.Apply(t.Context(), b, SetRunAs()) if tc.wantError != "" { require.Error(t, diags.Error()) assert.Contains(t, diags.Error().Error(), tc.wantError) } else { require.NoError(t, diags.Error()) - assert.Equal(t, before, b.Config.Value().Get("resources")) + assert.Equal(t, before, b.Config.View().Get("resources").AsAny()) } }) } @@ -497,9 +463,12 @@ resources: sp: {run_as: {service_principal_name: other_sp}} group: {run_as: {group_name: other_group}} `, tc.root, tc.target) - r, diags := config.LoadFromBytes("databricks.yml", []byte(yaml)) + r, diags := config.LoadDynFromBytes("databricks.yml", []byte(yaml)) require.NoError(t, diags.Error()) require.NoError(t, r.MergeTargetOverrides("test")) + diags, err := r.FinishLoading() + require.NoError(t, err) + require.Empty(t, diags) b := &bundle.Bundle{Config: *r} diags = bundle.Apply(t.Context(), b, SetRunAs()) require.NoError(t, diags.Error()) diff --git a/bundle/config/mutator/rewrite_sync_paths.go b/bundle/config/mutator/rewrite_sync_paths.go index 228c5484def..1aab844c11b 100644 --- a/bundle/config/mutator/rewrite_sync_paths.go +++ b/bundle/config/mutator/rewrite_sync_paths.go @@ -2,13 +2,12 @@ package mutator import ( "context" - "fmt" "path/filepath" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) type rewriteSyncPaths struct{} @@ -21,73 +20,52 @@ func (m *rewriteSyncPaths) Name() string { return "RewriteSyncPaths" } -// makeRelativeTo returns a dyn.MapFunc that joins the relative path -// of the file it was defined in w.r.t. the bundle root path, with -// the contents of the string node. +// makeRelativeTo joins the relative path of the file the value was defined in +// (loc) w.r.t. the bundle root path, with the value s. // // For example: // - The bundle root is /foo -// - The configuration file that defines the string node is at /foo/bar/baz.yml -// - The string node contains "somefile.*" +// - The configuration file that defines the value is at /foo/bar/baz.yml +// - The value is "somefile.*" // // Then the resulting value will be "bar/somefile.*". -func (m *rewriteSyncPaths) makeRelativeTo(root string) dyn.MapFunc { - return func(_ dyn.Path, v dyn.Value) (dyn.Value, error) { - dir := filepath.Dir(v.Location().File) - rel, err := filepath.Rel(root, dir) +func (m *rewriteSyncPaths) makeRelativeTo(root string, loc diag.Location, s string) (string, error) { + rel, err := filepath.Rel(root, filepath.Dir(loc.File)) + if err != nil { + return "", err + } + return filepath.Join(rel, s), nil +} + +// rewrite rewrites the sync paths in values (the field key of sync) relative to the +// bundle root, with Unix-style slashes if toSlash. +func (m *rewriteSyncPaths) rewrite(b *bundle.Bundle, key string, values []string, toSlash bool) error { + for i := range values { + loc := b.Config.GetLocationOfExact(structpath.NewPath(nil, "sync", key, i)) + path, err := m.makeRelativeTo(b.BundleRootPath, loc, values[i]) if err != nil { - return dyn.InvalidValue, err + return err } - - return dyn.NewValue(filepath.Join(rel, v.MustString()), v.Locations()), nil + if toSlash { + path = filepath.ToSlash(path) + } + values[i] = path } + return nil } func (m *rewriteSyncPaths) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Map(v, "sync", func(_ dyn.Path, v dyn.Value) (nv dyn.Value, err error) { - v, err = dyn.Map(v, "paths", dyn.Foreach(m.makeRelativeTo(b.BundleRootPath))) - if err != nil { - return dyn.InvalidValue, err - } - - makeRelativeFn := m.makeRelativeTo(b.BundleRootPath) - - // Makes include and exclude paths relative to the bundle root first. - // Then converts them to use Unix-style slashes. - // This is required for the ignore.GitIgnore we use in libs/fileset to work correctly. - v, err = dyn.Map(v, "include", dyn.Foreach(func(p dyn.Path, val dyn.Value) (dyn.Value, error) { - relPath, err := makeRelativeFn(p, val) - if err != nil { - return dyn.InvalidValue, err - } - str, ok := relPath.AsString() - if !ok { - return dyn.InvalidValue, fmt.Errorf("expected string value but got %s", relPath.Kind()) - } - return dyn.NewValue(filepath.ToSlash(str), relPath.Locations()), nil - })) - if err != nil { - return dyn.InvalidValue, err - } - - v, err = dyn.Map(v, "exclude", dyn.Foreach(func(p dyn.Path, val dyn.Value) (dyn.Value, error) { - relPath, err := makeRelativeFn(p, val) - if err != nil { - return dyn.InvalidValue, err - } - str, ok := relPath.AsString() - if !ok { - return dyn.InvalidValue, fmt.Errorf("expected string value but got %s", relPath.Kind()) - } - return dyn.NewValue(filepath.ToSlash(str), relPath.Locations()), nil - })) - if err != nil { - return dyn.InvalidValue, err - } - return v, nil - }) - }) + // Makes include and exclude paths relative to the bundle root first. + // Then converts them to use Unix-style slashes. + // This is required for the ignore.GitIgnore we use in libs/fileset to work correctly. + sync := &b.Config.Sync + err := m.rewrite(b, "paths", sync.Paths, false) + if err == nil { + err = m.rewrite(b, "include", sync.Include, true) + } + if err == nil { + err = m.rewrite(b, "exclude", sync.Exclude, true) + } return diag.FromErr(err) } diff --git a/bundle/config/mutator/rewrite_workspace_prefix.go b/bundle/config/mutator/rewrite_workspace_prefix.go index 30914fd149e..4546badd6ea 100644 --- a/bundle/config/mutator/rewrite_workspace_prefix.go +++ b/bundle/config/mutator/rewrite_workspace_prefix.go @@ -6,8 +6,9 @@ import ( "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) type rewriteWorkspacePrefix struct{} @@ -44,42 +45,28 @@ func (m *rewriteWorkspacePrefix) Apply(ctx context.Context, b *bundle.Bundle) di {"/Workspace${workspace.resource_path}", "${workspace.resource_path}"}, } - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - // Walk through the bundle configuration, check all the string leafs and - // see if any of the prefixes are used in the remote path. - return dyn.Walk(root, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - vv, ok := v.AsString() - if !ok { - return v, nil + // Walk through the bundle configuration, check all the string leafs and + // see if any of the prefixes are used in the remote path. + walk.Strings(&b.Config, walk.WithReferences, func(n *walk.Node, vv string) (string, bool) { + newPath := vv + for _, rewrite := range paths { + if !strings.Contains(newPath, rewrite.pattern) { + continue } - newPath := vv - for _, rewrite := range paths { - if !strings.Contains(newPath, rewrite.pattern) { - continue - } + newPath = strings.ReplaceAll(newPath, rewrite.pattern, rewrite.replacement) + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: fmt.Sprintf("substring %q found in %q. Please update this to %q.", rewrite.pattern, vv, newPath), + Detail: "For more information, please refer to: https://docs.databricks.com/en/release-notes/dev-tools/bundles.html#workspace-paths", + Locations: n.Locations().Get(), + Paths: []*structpath.PathNode{n.Path()}, + }) + } - newPath = strings.ReplaceAll(newPath, rewrite.pattern, rewrite.replacement) - diags = append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: fmt.Sprintf("substring %q found in %q. Please update this to %q.", rewrite.pattern, vv, newPath), - Detail: "For more information, please refer to: https://docs.databricks.com/en/release-notes/dev-tools/bundles.html#workspace-paths", - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), - }) - } - - if newPath == vv { - return v, nil - } - - // Remove the workspace prefix from the string. - return dyn.NewValue(newPath, v.Locations()), nil - }) + // Remove the workspace prefix from the string. + return newPath, newPath != vv }) - if err != nil { - return diag.FromErr(err) - } return diags } diff --git a/bundle/config/mutator/select_default_target.go b/bundle/config/mutator/select_default_target.go index ad8132a46aa..d84a0b62180 100644 --- a/bundle/config/mutator/select_default_target.go +++ b/bundle/config/mutator/select_default_target.go @@ -2,8 +2,6 @@ package mutator import ( "context" - "maps" - "slices" "strings" "github.com/databricks/cli/bundle" @@ -22,12 +20,13 @@ func (m *selectDefaultTarget) Name() string { } func (m *selectDefaultTarget) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - if len(b.Config.Targets) == 0 { + // This runs while the configuration is loading. + names := targetNames(b) + if len(names) == 0 { return diag.Errorf("no targets defined") } // One target means there's only one default. - names := slices.Collect(maps.Keys(b.Config.Targets)) if len(names) == 1 { bundle.ApplyContext(ctx, b, SelectTarget(names[0])) return nil @@ -35,8 +34,12 @@ func (m *selectDefaultTarget) Apply(ctx context.Context, b *bundle.Bundle) diag. // Multiple targets means we look for the `default` flag. var defaults []string - for name, env := range b.Config.Targets { - if env != nil && env.Default { + for _, name := range names { + var isDefault bool + if err := b.Config.DecodeLoading(&isDefault, "targets", name, "default"); err != nil { + return diag.FromErr(err) + } + if isDefault { defaults = append(defaults, name) } } diff --git a/bundle/config/mutator/select_default_target_test.go b/bundle/config/mutator/select_default_target_test.go index 850bfd88546..0f9fe1e74d1 100644 --- a/bundle/config/mutator/select_default_target_test.go +++ b/bundle/config/mutator/select_default_target_test.go @@ -6,6 +6,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/assert" ) @@ -15,7 +16,7 @@ func TestSelectDefaultTargetNoTargets(t *testing.T) { Targets: map[string]*config.Target{}, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.SelectDefaultTarget()) assert.ErrorContains(t, diags.Error(), "no targets defined") } @@ -27,7 +28,7 @@ func TestSelectDefaultTargetSingleTargets(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.SelectDefaultTarget()) assert.NoError(t, diags.Error()) assert.Equal(t, "foo", b.Config.Bundle.Target) } @@ -42,7 +43,7 @@ func TestSelectDefaultTargetNoDefaults(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.SelectDefaultTarget()) assert.ErrorContains(t, diags.Error(), "please specify target") } @@ -55,7 +56,7 @@ func TestSelectDefaultTargetNoDefaultsWithNil(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.SelectDefaultTarget()) assert.ErrorContains(t, diags.Error(), "please specify target") } @@ -69,7 +70,7 @@ func TestSelectDefaultTargetMultipleDefaults(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.SelectDefaultTarget()) assert.ErrorContains(t, diags.Error(), "multiple targets are marked as default") } @@ -83,7 +84,7 @@ func TestSelectDefaultTargetSingleDefault(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectDefaultTarget()) + diags := bundletest.ApplyLoading(t, b, mutator.SelectDefaultTarget()) assert.NoError(t, diags.Error()) assert.Equal(t, "bar", b.Config.Bundle.Target) } diff --git a/bundle/config/mutator/select_target.go b/bundle/config/mutator/select_target.go index 43764a9ee38..831214c2d27 100644 --- a/bundle/config/mutator/select_target.go +++ b/bundle/config/mutator/select_target.go @@ -3,12 +3,12 @@ package mutator import ( "context" "fmt" - "maps" - "slices" "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" ) type selectTarget struct { @@ -16,7 +16,7 @@ type selectTarget struct { } // SelectTarget merges the specified target into the root configuration. -// After merging, it removes the 'Targets' section from the configuration. +// It runs while the configuration is loading. func SelectTarget(name string) bundle.Mutator { return &selectTarget{ name: name, @@ -27,15 +27,33 @@ func (m *selectTarget) Name() string { return fmt.Sprintf("SelectTarget(%s)", m.name) } +// targetNames returns the names of the targets of the loading configuration +// (nil if there are none). +func targetNames(b *bundle.Bundle) []string { + targets, ok := b.Config.Value().Get("targets").AsMap() + if !ok { + return nil + } + names := make([]string, 0, targets.Len()) + for _, p := range targets.Pairs() { + names = append(names, p.Key.MustString()) + } + return names +} + func (m *selectTarget) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { - if b.Config.Targets == nil { + if k := b.Config.Value().Get("targets").Kind(); k == dyn.KindInvalid || k == dyn.KindNil { return diag.Errorf("no targets defined") } // Get specified target - target, ok := b.Config.Targets[m.name] - if !ok { - return diag.Errorf("%s: no such target. Available targets: %s", m.name, strings.Join(slices.Collect(maps.Keys(b.Config.Targets)), ", ")) + if !b.Config.HasTarget(m.name) { + return diag.Errorf("%s: no such target. Available targets: %s", m.name, strings.Join(targetNames(b), ", ")) + } + + var target *config.Target + if err := b.Config.DecodeLoading(&target, "targets", m.name); err != nil { + return diag.FromErr(err) } // Merge specified target into root configuration structure. @@ -46,21 +64,23 @@ func (m *selectTarget) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnosti // Store specified target in configuration for reference. b.Target = target - b.Config.Bundle.Target = m.name - - // We do this for backward compatibility. - // TODO: remove when Environments section is not supported anymore. - b.Config.Bundle.Environment = b.Config.Bundle.Target + b.Metrics.TargetCount = int64(len(targetNames(b))) - // Record number of targets in the bundle. This is then used downstream during - // telemetry upload. This value is always >= 1 since if no targets are defined - // in YAML, we create a "default" placeholder target upstream. - b.Metrics.TargetCount = int64(len(b.Config.Targets)) + err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { + v, err := dyn.SetByPath(v, dyn.NewPath(dyn.Key("bundle"), dyn.Key("target")), dyn.V(m.name)) + if err != nil { + return v, err + } - // Cleanup the original targets and environments sections since they - // show up in the JSON output of the 'summary' and 'validate' commands. - b.Config.Targets = nil - b.Config.Environments = nil + // We need to keep environment in sync for backwards compatibility. + v, err = dyn.SetByPath(v, dyn.NewPath(dyn.Key("bundle"), dyn.Key("environment")), dyn.V(m.name)) + if err != nil { + return v, err + } - return nil + // Cleanup the original targets and environments sections since they + // show up in the JSON output of the 'summary' and 'validate' commands. + return dyn.DropKeys(v, []string{"targets", "environments"}) + }) + return diag.FromErr(err) } diff --git a/bundle/config/mutator/select_target_test.go b/bundle/config/mutator/select_target_test.go index 14cec05d9b7..e3de623163c 100644 --- a/bundle/config/mutator/select_target_test.go +++ b/bundle/config/mutator/select_target_test.go @@ -6,6 +6,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -25,7 +26,7 @@ func TestSelectTarget(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectTarget("default")) + diags := bundletest.ApplyLoading(t, b, mutator.SelectTarget("default")) require.NoError(t, diags.Error()) assert.Equal(t, "bar", b.Config.Workspace.Host) } @@ -38,6 +39,6 @@ func TestSelectTargetNotFound(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, mutator.SelectTarget("doesnt-exist")) + diags := bundletest.ApplyLoading(t, b, mutator.SelectTarget("doesnt-exist")) require.Error(t, diags.Error(), "no targets defined") } diff --git a/bundle/config/mutator/set_variables.go b/bundle/config/mutator/set_variables.go index f8f3b35394c..be901afb9e8 100644 --- a/bundle/config/mutator/set_variables.go +++ b/bundle/config/mutator/set_variables.go @@ -3,15 +3,18 @@ package mutator import ( "context" "fmt" + "maps" "os" "path/filepath" + "slices" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/variable" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/jsonloader" "github.com/databricks/cli/libs/env" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) const bundleVarPrefix = "BUNDLE_VAR_" @@ -30,90 +33,97 @@ func getDefaultVariableFilePath(target string) string { return ".databricks/bundle/" + target + "/variable-overrides.json" } -func setVariable(ctx context.Context, v dyn.Value, variable *variable.Variable, name string, fileDefault dyn.Value) (dyn.Value, error) { +func setVariable(ctx context.Context, cfg *config.Root, variable *variable.Variable, name string, fileDefault structvar.View) error { // case: variable already has value initialized, so skip if variable.HasValue() { - return v, nil + return nil } + variablePath := structpath.NewPath(nil, "variables", name) + valuePath := structpath.NewStringKey(variablePath, "value") + // case: read and set variable value from process environment envVarName := bundleVarPrefix + name if val, ok := env.Lookup(ctx, envVarName); ok { if variable.IsComplex() { - return dyn.InvalidValue, fmt.Errorf(`setting via environment variables (%s) is not supported for complex variable %s`, envVarName, name) + return fmt.Errorf(`setting via environment variables (%s) is not supported for complex variable %s`, envVarName, name) } - v, err := dyn.Set(v, "value", dyn.V(val)) + err := cfg.Set(valuePath, val) if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to assign value "%s" to variable %s from environment variable %s with error: %v`, val, name, envVarName, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors + return fmt.Errorf(`failed to assign value "%s" to variable %s from environment variable %s with error: %v`, val, name, envVarName, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors } - return v, nil + return nil } // case: Defined a variable for named lookup for a resource // It will be resolved later in ResolveResourceReferences mutator if variable.Lookup != nil { - return v, nil + return nil } // case: Set the variable to the default value from the variable file - if fileDefault.Kind() != dyn.KindInvalid && fileDefault.Kind() != dyn.KindNil { + if fileDefault.Kind() != structvar.KindInvalid && fileDefault.Kind() != structvar.KindNil { hasComplexType := variable.IsComplex() - hasComplexValue := fileDefault.Kind() == dyn.KindMap || fileDefault.Kind() == dyn.KindSequence + hasComplexValue := fileDefault.Kind() == structvar.KindMap || fileDefault.Kind() == structvar.KindSequence if hasComplexType && !hasComplexValue { - return dyn.InvalidValue, fmt.Errorf(`variable %s is of type complex, but the value in the variable file is not a complex type`, name) + return fmt.Errorf(`variable %s is of type complex, but the value in the variable file is not a complex type`, name) } if !hasComplexType && hasComplexValue { - return dyn.InvalidValue, fmt.Errorf(`variable %s is not of type complex, but the value in the variable file is a complex type`, name) + return fmt.Errorf(`variable %s is not of type complex, but the value in the variable file is a complex type`, name) } - v, err := dyn.Set(v, "value", fileDefault) + err := cfg.Assign(valuePath, fileDefault) if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to assign default value from variable file to variable %s with error: %v`, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors + return fmt.Errorf(`failed to assign default value from variable file to variable %s with error: %v`, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors } - return v, nil + return nil } // case: Set the variable to its default value if variable.HasDefault() { - vDefault, err := dyn.Get(v, "default") + // Assign copies the default (maps and slices are resolved in place later) with + // its locations. + defaultLocs := cfg.LocationTree().Sub(structpath.NewStringKey(variablePath, "default")) + err := cfg.Assign(valuePath, structvar.NewView(&variable.Default, nil, defaultLocs)) if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to get default value from config "%s" for variable %s with error: %v`, variable.Default, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors + return fmt.Errorf(`failed to assign default value from config "%s" to variable %s with error: %w`, variable.Default, name, err) } - - v, err := dyn.Set(v, "value", vDefault) - if err != nil { - return dyn.InvalidValue, fmt.Errorf(`failed to assign default value from config "%s" to variable %s with error: %v`, variable.Default, name, err) //nolint:errorlint // runs inside dyn.Map, which drops wrapped no-such-key errors - } - return v, nil + return nil } // We should have had a value to set for the variable at this point. - return dyn.InvalidValue, fmt.Errorf(`no value assigned to required variable %s. Variables are usually assigned in databricks.yml, and they can be overridden using "--var", the %s environment variable, or %s`, name, bundleVarPrefix+name, getDefaultVariableFilePath("")) + return fmt.Errorf(`no value assigned to required variable %s. Variables are usually assigned in databricks.yml, and they can be overridden using "--var", the %s environment variable, or %s`, name, bundleVarPrefix+name, getDefaultVariableFilePath("")) } -func readVariablesFromFile(b *bundle.Bundle) (dyn.Value, diag.Diagnostics) { +func readVariablesFromFile(b *bundle.Bundle) (structvar.View, diag.Diagnostics) { var diags diag.Diagnostics filePath := filepath.Join(b.BundleRootPath, getDefaultVariableFilePath(b.Config.Bundle.Target)) if _, err := os.Stat(filePath); err != nil { - return dyn.InvalidValue, nil + return structvar.View{}, nil } f, err := os.ReadFile(filePath) if err != nil { - return dyn.InvalidValue, diag.FromErr(fmt.Errorf("failed to read variables file: %w", err)) + return structvar.View{}, diag.FromErr(fmt.Errorf("failed to read variables file: %w", err)) } - val, err := jsonloader.LoadJSON(f, filePath) + node, err := structvar.ParseJSON(filePath, f) + if err != nil { + return structvar.View{}, diag.FromErr(fmt.Errorf("failed to parse variables file %s: %w", filePath, err)) + } + var v any + decoded, _, err := structvar.DecodeYAMLNode(filePath, node, &v, nil) if err != nil { - return dyn.InvalidValue, diag.FromErr(fmt.Errorf("failed to parse variables file %s: %w", filePath, err)) + return structvar.View{}, diag.FromErr(fmt.Errorf("failed to parse variables file %s: %w", filePath, err)) } - if val.Kind() != dyn.KindMap { - return dyn.InvalidValue, diags.Append(diag.Diagnostic{ + val := decoded.View() + if val.Kind() != structvar.KindMap { + return structvar.View{}, diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("failed to parse variables file %s: invalid format", filePath), Detail: "Variables file must be a JSON object with the following format:\n{\"var1\": \"value1\", \"var2\": \"value2\"}", @@ -128,18 +138,14 @@ func (m *setVariables) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnos if diags.HasError() { return diags } - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Map(v, "variables", dyn.Foreach(func(p dyn.Path, variable dyn.Value) (dyn.Value, error) { - name := p[1].Key() - v, ok := b.Config.Variables[name] - if !ok { - return dyn.InvalidValue, fmt.Errorf(`variable "%s" is not defined`, name) - } - - fileDefault, _ := dyn.Get(defaults, name) - return setVariable(ctx, variable, v, name, fileDefault) - })) - }) - - return diags.Extend(diag.FromErr(err)) + for _, name := range slices.Sorted(maps.Keys(b.Config.Variables)) { + v := b.Config.Variables[name] + fileDefault := defaults.Get(name) + err := setVariable(ctx, &b.Config, v, name, fileDefault) + if err != nil { + return diags.Extend(diag.FromErr(err)) + } + } + + return diags } diff --git a/bundle/config/mutator/set_variables_test.go b/bundle/config/mutator/set_variables_test.go index ecc67548017..977e7e94529 100644 --- a/bundle/config/mutator/set_variables_test.go +++ b/bundle/config/mutator/set_variables_test.go @@ -6,12 +6,16 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/variable" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/cli/libs/structs/structvar" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +// configWithVariable returns a configuration that has the variable with name "foo". +func configWithVariable(v *variable.Variable) *config.Root { + return &config.Root{Variables: map[string]*variable.Variable{"foo": v}} +} + func TestSetVariableFromProcessEnvVar(t *testing.T) { defaultVal := "default" variable := variable.Variable{ @@ -21,13 +25,9 @@ func TestSetVariableFromProcessEnvVar(t *testing.T) { // set value for variable as an environment variable t.Setenv("BUNDLE_VAR_foo", "process-env") - v, err := convert.FromTyped(variable, dyn.NilValue) - require.NoError(t, err) - - v, err = setVariable(t.Context(), v, &variable, "foo", dyn.NilValue) - require.NoError(t, err) + cfg := configWithVariable(&variable) - err = convert.ToTyped(&variable, v) + err := setVariable(t.Context(), cfg, &variable, "foo", structvar.View{}) require.NoError(t, err) assert.Equal(t, "process-env", variable.Value) } @@ -39,13 +39,9 @@ func TestSetVariableUsingDefaultValue(t *testing.T) { Default: defaultVal, } - v, err := convert.FromTyped(variable, dyn.NilValue) - require.NoError(t, err) - - v, err = setVariable(t.Context(), v, &variable, "foo", dyn.NilValue) - require.NoError(t, err) + cfg := configWithVariable(&variable) - err = convert.ToTyped(&variable, v) + err := setVariable(t.Context(), cfg, &variable, "foo", structvar.View{}) require.NoError(t, err) assert.Equal(t, "default", variable.Value) } @@ -61,13 +57,9 @@ func TestSetVariableWhenAlreadyAValueIsAssigned(t *testing.T) { // since a value is already assigned to the variable, it would not be overridden // by the default value - v, err := convert.FromTyped(variable, dyn.NilValue) - require.NoError(t, err) - - v, err = setVariable(t.Context(), v, &variable, "foo", dyn.NilValue) - require.NoError(t, err) + cfg := configWithVariable(&variable) - err = convert.ToTyped(&variable, v) + err := setVariable(t.Context(), cfg, &variable, "foo", structvar.View{}) require.NoError(t, err) assert.Equal(t, "assigned-value", variable.Value) } @@ -86,13 +78,9 @@ func TestSetVariableEnvVarValueDoesNotOverridePresetValue(t *testing.T) { // since a value is already assigned to the variable, it would not be overridden // by the value from environment - v, err := convert.FromTyped(variable, dyn.NilValue) - require.NoError(t, err) + cfg := configWithVariable(&variable) - v, err = setVariable(t.Context(), v, &variable, "foo", dyn.NilValue) - require.NoError(t, err) - - err = convert.ToTyped(&variable, v) + err := setVariable(t.Context(), cfg, &variable, "foo", structvar.View{}) require.NoError(t, err) assert.Equal(t, "assigned-value", variable.Value) } @@ -103,10 +91,9 @@ func TestSetVariablesErrorsIfAValueCouldNotBeResolved(t *testing.T) { } // fails because we could not resolve a value for the variable - v, err := convert.FromTyped(variable, dyn.NilValue) - require.NoError(t, err) + cfg := configWithVariable(&variable) - _, err = setVariable(t.Context(), v, &variable, "foo", dyn.NilValue) + err := setVariable(t.Context(), cfg, &variable, "foo", structvar.View{}) assert.ErrorContains(t, err, "no value assigned to required variable foo. Variables are usually assigned in databricks.yml, and they can be overridden using \"--var\", the BUNDLE_VAR_foo environment variable, or .databricks/bundle//variable-overrides.json") } @@ -153,9 +140,8 @@ func TestSetComplexVariablesViaEnvVariablesIsNotAllowed(t *testing.T) { // set value for variable as an environment variable t.Setenv("BUNDLE_VAR_foo", "process-env") - v, err := convert.FromTyped(variable, dyn.NilValue) - require.NoError(t, err) + cfg := configWithVariable(&variable) - _, err = setVariable(t.Context(), v, &variable, "foo", dyn.NilValue) + err := setVariable(t.Context(), cfg, &variable, "foo", structvar.View{}) assert.ErrorContains(t, err, "setting via environment variables (BUNDLE_VAR_foo) is not supported for complex variable foo") } diff --git a/bundle/config/mutator/sync_default_path.go b/bundle/config/mutator/sync_default_path.go index 16d4a4d6179..92c27488967 100644 --- a/bundle/config/mutator/sync_default_path.go +++ b/bundle/config/mutator/sync_default_path.go @@ -5,7 +5,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) type syncDefaultPath struct{} @@ -20,24 +20,9 @@ func (m *syncDefaultPath) Name() string { } func (m *syncDefaultPath) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - isset := false - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - pv, _ := dyn.Get(v, "sync.paths") - - // If the sync paths field is already set, do nothing. - // We know it is set if its value is either a nil or a sequence (empty or not). - if pv.Kind() == dyn.KindNil || pv.Kind() == dyn.KindSequence { - isset = true - } - - return v, nil - }) - if err != nil { - return diag.FromErr(err) - } - // If the sync paths field is already set, do nothing. - if isset { + // We know it is set if it is a sequence (empty or not) or a reference. + if b.Config.Sync.Paths != nil || b.Config.IsReferenceAt(structpath.NewPath(nil, "sync", "paths")) { return nil } diff --git a/bundle/config/mutator/sync_default_path_test.go b/bundle/config/mutator/sync_default_path_test.go index c4bd6a65af7..a85821b6cc2 100644 --- a/bundle/config/mutator/sync_default_path_test.go +++ b/bundle/config/mutator/sync_default_path_test.go @@ -7,8 +7,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,22 +28,22 @@ func TestSyncDefaultPath_DefaultIfUnset(t *testing.T) { func TestSyncDefaultPath_SkipIfSet(t *testing.T) { tcases := []struct { name string - paths dyn.Value + paths any expect []string }{ { name: "nil", - paths: dyn.V(nil), + paths: nil, expect: []string{"."}, }, { name: "empty sequence", - paths: dyn.V([]dyn.Value{}), + paths: []string{}, expect: []string{}, }, { name: "non-empty sequence", - paths: dyn.V([]dyn.Value{dyn.V("something")}), + paths: []string{"something"}, expect: []string{"something"}, }, } @@ -58,17 +58,7 @@ func TestSyncDefaultPath_SkipIfSet(t *testing.T) { ctx := logdiag.InitContext(t.Context()) bundle.ApplyFuncContext(ctx, b, func(ctx context.Context, b *bundle.Bundle) { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - v, err := dyn.Set(v, "sync", dyn.V(dyn.NewMapping())) - if err != nil { - return dyn.InvalidValue, err - } - v, err = dyn.Set(v, "sync.paths", tcase.paths) - if err != nil { - return dyn.InvalidValue, err - } - return v, nil - }) + err := b.Config.Set(structpath.MustParsePath("sync.paths"), tcase.paths) require.NoError(t, err) }) require.False(t, logdiag.HasError(ctx)) diff --git a/bundle/config/mutator/translate_paths.go b/bundle/config/mutator/translate_paths.go index 956d010ec95..336962e528a 100644 --- a/bundle/config/mutator/translate_paths.go +++ b/bundle/config/mutator/translate_paths.go @@ -17,8 +17,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/notebook" + "github.com/databricks/cli/libs/structs/structpath" ) // translateOptions control path translation behavior. @@ -299,37 +299,54 @@ func (t *translateContext) translateLocalRelativeWithPrefixPath(ctx context.Cont return localRelPath, nil } -func (t *translateContext) rewriteValue(ctx context.Context, p dyn.Path, v dyn.Value, dir string, opts translateOptions) (dyn.Value, error) { - out, err := t.rewritePath(ctx, dir, v.MustString(), opts) +// rewriteValue rewrites the path input found at p. It returns an empty string if the path was not rewritten. +func (t *translateContext) rewriteValue(ctx context.Context, p *structpath.PathNode, input, dir string, opts translateOptions) (string, error) { + out, err := t.rewritePath(ctx, dir, input, opts) if err != nil { if target, ok := errors.AsType[ErrIsNotebook](err); ok { - return dyn.InvalidValue, fmt.Errorf(`expected a file for "%s" but got a notebook: %w`, p, target) + return "", fmt.Errorf(`expected a file for "%s" but got a notebook: %w`, p, target) } if target, ok := errors.AsType[ErrIsNotNotebook](err); ok { - return dyn.InvalidValue, fmt.Errorf(`expected a notebook for "%s" but got a file: %w`, p, target) + return "", fmt.Errorf(`expected a notebook for "%s" but got a file: %w`, p, target) } - return dyn.InvalidValue, err + return "", err } - // If the path was not rewritten, return the original value. - if out == "" { - return v, nil + return out, nil +} + +// setRewritten stores the rewritten path in s (the value keeps its locations) unless +// the path was not rewritten. +func setRewritten(s *string, out string) { + if out != "" { + *s = out } +} - return dyn.NewValue(out, v.Locations()), nil +// rewriteAt rewrites the path value s found at p relative to the bundle root. +func (t *translateContext) rewriteAt(ctx context.Context, p *structpath.PathNode, s *string, opts translateOptions) error { + out, err := t.rewriteValue(ctx, p, *s, t.b.BundleRootPath, opts) + if err != nil { + return err + } + setRewritten(s, out) + return nil } -func applyTranslations(ctx context.Context, b *bundle.Bundle, t *translateContext, translations []func(context.Context, dyn.Value) (dyn.Value, error)) diag.Diagnostics { +func applyTranslations(ctx context.Context, b *bundle.Bundle, t *translateContext, translations []translateFunc) diag.Diagnostics { switch { case b.IsImmutableFolder(): // Reject an explicit workspace.file_path: immutable bundles set it // automatically to the content-addressed snapshot location. A user-supplied // value would be silently discarded during path translation, so we error early. - if loc := b.Config.GetLocation("workspace.file_path"); loc.File != "" { + // DefineDefaultWorkspacePaths sets the snapshot location, or root/files if + // immutable_folder was a reference not resolved yet; any other value is the user's. + // Clean: the default is joined before root_path's references are resolved. + if fp := path.Clean(b.Config.Workspace.FilePath); fp != resources.SnapshotFullPathRef+"/files" && fp != path.Join(b.Config.Workspace.RootPath, "files") { return diag.Diagnostics{{ Severity: diag.Error, Summary: "workspace.file_path cannot be configured when workspace.sync_mode is immutable", - Locations: []diag.Location{loc}, + Locations: b.Config.GetLocations("workspace.file_path"), }} } t.remoteRoot = resources.SnapshotFullPathRef + "/files" @@ -339,18 +356,13 @@ func applyTranslations(ctx context.Context, b *bundle.Bundle, t *translateContex t.remoteRoot = t.b.Config.Workspace.FilePath } - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - var err error - for _, fn := range translations { - v, err = fn(ctx, v) - if err != nil { - return dyn.InvalidValue, err - } + for _, fn := range translations { + if err := fn(ctx, &b.Config); err != nil { + return diag.FromErr(err) } - return v, nil - }) + } - return diag.FromErr(err) + return nil } func (m *translatePaths) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { @@ -360,7 +372,7 @@ func (m *translatePaths) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn skipLocalFileValidation: b.SkipLocalFileValidation, } - return applyTranslations(ctx, b, t, []func(context.Context, dyn.Value) (dyn.Value, error){ + return applyTranslations(ctx, b, t, []translateFunc{ t.applyJobTranslations(paths.VisitJobPaths, false), t.applyJobTranslations(paths.VisitJobLibrariesPaths, true), t.applyPipelineTranslations(paths.VisitPipelinePaths, false), @@ -377,7 +389,7 @@ func (m *translatePathsDashboards) Apply(ctx context.Context, b *bundle.Bundle) skipLocalFileValidation: b.SkipLocalFileValidation, } - return applyTranslations(ctx, b, t, []func(context.Context, dyn.Value) (dyn.Value, error){ + return applyTranslations(ctx, b, t, []translateFunc{ t.applyDashboardTranslations, t.applyGenieSpaceTranslations, }) diff --git a/bundle/config/mutator/translate_paths_apps.go b/bundle/config/mutator/translate_paths_apps.go index 8daef7e4857..27588b02571 100644 --- a/bundle/config/mutator/translate_paths_apps.go +++ b/bundle/config/mutator/translate_paths_apps.go @@ -3,20 +3,21 @@ package mutator import ( "context" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/paths" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) -func (t *translateContext) applyAppsTranslations(ctx context.Context, v dyn.Value) (dyn.Value, error) { +func (t *translateContext) applyAppsTranslations(ctx context.Context, r *config.Root) error { // Convert the `source_code_path` field to a remote absolute path. // We use this path for app deployment to point to the source code. - return paths.VisitAppPaths(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) { + return paths.VisitAppPaths(r, func(p *structpath.PathNode, mode paths.TranslateMode, s *string) error { opts := translateOptions{ Mode: mode, } - return t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts) + return t.rewriteAt(ctx, p, s, opts) }) } diff --git a/bundle/config/mutator/translate_paths_artifacts.go b/bundle/config/mutator/translate_paths_artifacts.go index 51ae0d8f8f5..533ac9229cb 100644 --- a/bundle/config/mutator/translate_paths_artifacts.go +++ b/bundle/config/mutator/translate_paths_artifacts.go @@ -3,13 +3,14 @@ package mutator import ( "context" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/paths" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) -func (t *translateContext) applyArtifactTranslations(ctx context.Context, v dyn.Value) (dyn.Value, error) { - return paths.VisitArtifactPaths(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) { +func (t *translateContext) applyArtifactTranslations(ctx context.Context, r *config.Root) error { + return paths.VisitArtifactPaths(r, func(p *structpath.PathNode, mode paths.TranslateMode, s *string) error { opts := translateOptions{ Mode: mode, @@ -18,6 +19,6 @@ func (t *translateContext) applyArtifactTranslations(ctx context.Context, v dyn. AllowPathOutsideSyncRoot: true, } - return t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts) + return t.rewriteAt(ctx, p, s, opts) }) } diff --git a/bundle/config/mutator/translate_paths_dashboards.go b/bundle/config/mutator/translate_paths_dashboards.go index 2e93ed3998f..fe095125a82 100644 --- a/bundle/config/mutator/translate_paths_dashboards.go +++ b/bundle/config/mutator/translate_paths_dashboards.go @@ -3,20 +3,21 @@ package mutator import ( "context" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/paths" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) -func (t *translateContext) applyDashboardTranslations(ctx context.Context, v dyn.Value) (dyn.Value, error) { +func (t *translateContext) applyDashboardTranslations(ctx context.Context, r *config.Root) error { // Convert the `file_path` field to a local absolute path. // We load the file at this path and use its contents for the dashboard contents. - return paths.VisitDashboardPaths(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) { + return paths.VisitDashboardPaths(r, func(p *structpath.PathNode, mode paths.TranslateMode, s *string) error { opts := translateOptions{ Mode: mode, } - return t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts) + return t.rewriteAt(ctx, p, s, opts) }) } diff --git a/bundle/config/mutator/translate_paths_genie_spaces.go b/bundle/config/mutator/translate_paths_genie_spaces.go index 4e6d41f1cec..993204f8281 100644 --- a/bundle/config/mutator/translate_paths_genie_spaces.go +++ b/bundle/config/mutator/translate_paths_genie_spaces.go @@ -3,19 +3,20 @@ package mutator import ( "context" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/paths" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) -func (t *translateContext) applyGenieSpaceTranslations(ctx context.Context, v dyn.Value) (dyn.Value, error) { +func (t *translateContext) applyGenieSpaceTranslations(ctx context.Context, r *config.Root) error { // Rewrite the `file_path` field to a path relative to the bundle sync root. // We load the file at this path and use its contents for the genie space contents. - return paths.VisitGenieSpacePaths(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) { + return paths.VisitGenieSpacePaths(r, func(p *structpath.PathNode, mode paths.TranslateMode, s *string) error { opts := translateOptions{ Mode: mode, } - return t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts) + return t.rewriteAt(ctx, p, s, opts) }) } diff --git a/bundle/config/mutator/translate_paths_jobs.go b/bundle/config/mutator/translate_paths_jobs.go index 2c94e21802b..6e14e70876b 100644 --- a/bundle/config/mutator/translate_paths_jobs.go +++ b/bundle/config/mutator/translate_paths_jobs.go @@ -4,18 +4,18 @@ import ( "context" "slices" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/paths" - - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) type ( - visitFunc func(dyn.Value, paths.VisitFunc) (dyn.Value, error) - translateFunc func(ctx context.Context, v dyn.Value) (dyn.Value, error) + visitFunc func(*config.Root, paths.VisitFunc) error + translateFunc func(ctx context.Context, r *config.Root) error ) func (t *translateContext) applyJobTranslations(visitor visitFunc, allowOutsideSyncRoot bool) translateFunc { - return func(ctx context.Context, v dyn.Value) (dyn.Value, error) { + return func(ctx context.Context, r *config.Root) error { // Do not translate job task paths if using Git source var ignore []string for key, job := range t.b.Config.Resources.Jobs { @@ -24,12 +24,13 @@ func (t *translateContext) applyJobTranslations(visitor visitFunc, allowOutsideS } } - return visitor(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) { - key := p[2].Key() + return visitor(r, func(p *structpath.PathNode, mode paths.TranslateMode, s *string) error { + key := p.KeyAt(2) + input := *s // Skip path translation if the job is using git source. if slices.Contains(ignore, key) { - return v, nil + return nil } opts := translateOptions{ @@ -38,12 +39,13 @@ func (t *translateContext) applyJobTranslations(visitor visitFunc, allowOutsideS } // Handle path as if it's relative to the bundle root - nv, err := t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts) + nv, err := t.rewriteValue(ctx, p, input, t.b.BundleRootPath, opts) if err == nil { - return nv, nil + setRewritten(s, nv) + return nil } - return dyn.InvalidValue, err + return err }) } } diff --git a/bundle/config/mutator/translate_paths_pipelines.go b/bundle/config/mutator/translate_paths_pipelines.go index 07c2e58cd93..fe19d923ad8 100644 --- a/bundle/config/mutator/translate_paths_pipelines.go +++ b/bundle/config/mutator/translate_paths_pipelines.go @@ -3,26 +3,28 @@ package mutator import ( "context" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/mutator/paths" - - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) func (t *translateContext) applyPipelineTranslations(visitor visitFunc, allowOutsideSyncRoot bool) translateFunc { - return func(ctx context.Context, v dyn.Value) (dyn.Value, error) { - return visitor(v, func(p dyn.Path, mode paths.TranslateMode, v dyn.Value) (dyn.Value, error) { + return func(ctx context.Context, r *config.Root) error { + return visitor(r, func(p *structpath.PathNode, mode paths.TranslateMode, s *string) error { + input := *s opts := translateOptions{ Mode: mode, AllowPathOutsideSyncRoot: allowOutsideSyncRoot, } // Handle path as if it's relative to the bundle root - nv, err := t.rewriteValue(ctx, p, v, t.b.BundleRootPath, opts) + nv, err := t.rewriteValue(ctx, p, input, t.b.BundleRootPath, opts) if err == nil { - return nv, nil + setRewritten(s, nv) + return nil } - return dyn.InvalidValue, err + return err }) } } diff --git a/bundle/config/mutator/translate_paths_test.go b/bundle/config/mutator/translate_paths_test.go index 8aaf9ce9c71..63569c0f747 100644 --- a/bundle/config/mutator/translate_paths_test.go +++ b/bundle/config/mutator/translate_paths_test.go @@ -15,7 +15,7 @@ import ( "github.com/databricks/cli/bundle/config/variable" "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/vfs" "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" @@ -855,10 +855,8 @@ func TestTranslatePathWithComplexVariables(t *testing.T) { ctx := t.Context() // Assign the variables to the dynamic configuration. bundle.ApplyFuncContext(ctx, b, func(ctx context.Context, b *bundle.Bundle) { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - p := dyn.MustPathFromString("resources.jobs.job.tasks[0]") - return dyn.SetByPath(v, p.Append(dyn.Key("libraries")), dyn.V("${var.cluster_libraries}")) - }) + p := structpath.MustParsePath("resources.jobs.job.tasks[0]") + err := b.Config.SetReference(structpath.NewStringKey(p, "libraries"), "${var.cluster_libraries}") require.NoError(t, err) }) diff --git a/bundle/config/mutator/validate_secret_value_is_variable.go b/bundle/config/mutator/validate_secret_value_is_variable.go index 1f979e15f74..b8f7108aa6e 100644 --- a/bundle/config/mutator/validate_secret_value_is_variable.go +++ b/bundle/config/mutator/validate_secret_value_is_variable.go @@ -5,9 +5,10 @@ import ( "fmt" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type validateSecretValueIsVariable struct{} @@ -24,45 +25,32 @@ func (v *validateSecretValueIsVariable) Apply(ctx context.Context, b *bundle.Bun var diags diag.Diagnostics // Iterate over all secrets in the bundle - for key := range b.Config.Resources.Secrets { - p := dyn.NewPath(dyn.Key("resources"), dyn.Key("secrets"), dyn.Key(key), dyn.Key("value")) - val, err := dyn.GetByPath(b.Config.Value(), p) - if dyn.IsNoSuchKeyError(err) { + for key, secret := range b.Config.Resources.Secrets { + p := structpath.NewPath(nil, "resources", "secrets", key, "value") + locs := b.Config.GetLocationsOfExact(p) + // An explicit empty value is forced (it is not a reference either, below). + if secret == nil || (secret.Value == "" && !walk.Forced(secret.ForceSendFields, "Value")) { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Secret value must be a string", Detail: fmt.Sprintf(`The secret value for "%s" must be a string.`, key), - Locations: val.Locations(), - Paths: dyn.ToStructPaths(p), - }) - continue - } - if err != nil { - return diag.FromErr(err) - } - - valueStr, ok := val.AsString() - if !ok { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Secret value must be a string", - Detail: fmt.Sprintf(`The secret value for "%s" must be a string.`, key), - Locations: val.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: locs, + Paths: []*structpath.PathNode{p}, }) continue } + valueStr := secret.Value // Value must be a variable reference to prevent leaking secrets in config files - if !dynvar.IsPureVariableReference(valueStr) { + if !structvar.IsPureVariableReference(valueStr) { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Secret value must be a variable reference", Detail: fmt.Sprintf(`The secret value for "%s" must be a variable reference (e.g., ${var.my_secret}). Plain text secret values are not allowed to prevent leaking secrets in configuration files. Use bundle variables to pass secret values at deployment time.`, key), - Locations: val.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: locs, + Paths: []*structpath.PathNode{p}, }) continue } @@ -71,7 +59,7 @@ Use bundle variables to pass secret values at deployment time.`, key), // check that the referenced variable does not have a default value set. A default // value would be stored in plain text in the config file, defeating the purpose // of using a variable reference. - diags = append(diags, v.checkVariableDefault(b, key, valueStr, p, val)...) + diags = append(diags, v.checkVariableDefault(b, key, valueStr, p, locs)...) } return diags @@ -79,25 +67,21 @@ Use bundle variables to pass secret values at deployment time.`, key), // checkVariableDefault emits an error if valueStr is a pure ${var.} reference // and the referenced variable has a default value set. -func (v *validateSecretValueIsVariable) checkVariableDefault(b *bundle.Bundle, secretKey, valueStr string, p dyn.Path, val dyn.Value) diag.Diagnostics { - refPath, ok := dynvar.PureReferenceToPath(valueStr) - if !ok || len(refPath) < 2 || refPath[0].Key() != "var" { +func (v *validateSecretValueIsVariable) checkVariableDefault(b *bundle.Bundle, secretKey, valueStr string, p *structpath.PathNode, valueLocs []diag.Location) diag.Diagnostics { + refPath, ok := structvar.PureReferenceToPath(valueStr) + if !ok || refPath.Len() < 2 || refPath.KeyAt(0) != "var" { return nil } - varName := refPath[1].Key() + varName := refPath.KeyAt(1) variable, exists := b.Config.Variables[varName] if !exists || variable == nil || !variable.HasDefault() { return nil } // The default path in the dynamic config for the variable's default field. - defaultPath := dyn.NewPath(dyn.Key("variables"), dyn.Key(varName), dyn.Key("default")) - defaultVal, err := dyn.GetByPath(b.Config.Value(), defaultPath) - locations := val.Locations() - if err == nil { - locations = append(defaultVal.Locations(), locations...) - } + defaultPath := structpath.NewPath(nil, "variables", varName, "default") + locations := append(b.Config.GetLocationsOfExact(defaultPath), valueLocs...) return diag.Diagnostics{{ Severity: diag.Error, @@ -106,6 +90,6 @@ func (v *validateSecretValueIsVariable) checkVariableDefault(b *bundle.Bundle, s A default value is stored in plain text in the configuration file, which defeats the purpose of using a variable reference for a secret. Remove the default value and pass the secret value at deployment time using "--var", the BUNDLE_VAR_%s environment variable, or a variable overrides file.`, varName, secretKey, varName), Locations: locations, - Paths: dyn.ToStructPaths(p), + Paths: []*structpath.PathNode{p}, }} } diff --git a/bundle/config/mutator/verify_cli_version.go b/bundle/config/mutator/verify_cli_version.go index a6d705a6fbb..e04a2762337 100644 --- a/bundle/config/mutator/verify_cli_version.go +++ b/bundle/config/mutator/verify_cli_version.go @@ -18,12 +18,17 @@ func VerifyCliVersion() bundle.Mutator { type verifyCliVersion struct{} func (v *verifyCliVersion) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { + // This runs while the configuration is loading. + var constraint string + if err := b.Config.DecodeLoading(&constraint, "bundle", "databricks_cli_version"); err != nil { + return diag.FromErr(err) + } + // No constraints specified, skip the check. - if b.Config.Bundle.DatabricksCliVersion == "" { + if constraint == "" { return nil } - constraint := b.Config.Bundle.DatabricksCliVersion if err := validateConstraintSyntax(constraint); err != nil { return diag.FromErr(err) } diff --git a/bundle/config/mutator/verify_cli_version_test.go b/bundle/config/mutator/verify_cli_version_test.go index b9f896a72bb..b3de0a20d08 100644 --- a/bundle/config/mutator/verify_cli_version_test.go +++ b/bundle/config/mutator/verify_cli_version_test.go @@ -6,6 +6,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/internal/build" "github.com/stretchr/testify/require" ) @@ -131,7 +132,7 @@ func TestVerifyCliVersion(t *testing.T) { }, }, } - diags := bundle.Apply(t.Context(), b, VerifyCliVersion()) + diags := bundletest.ApplyLoading(t, b, VerifyCliVersion()) if tc.expectedError != "" { require.NotEmpty(t, diags) require.Contains(t, diags[0].Summary, tc.expectedError) diff --git a/bundle/config/resourcedefaults/defaults.go b/bundle/config/resourcedefaults/defaults.go new file mode 100644 index 00000000000..e2ffcf0aac5 --- /dev/null +++ b/bundle/config/resourcedefaults/defaults.go @@ -0,0 +1,81 @@ +// Package resourcedefaults lists the values set on resources where the configuration +// does not set them. The walker that applies them is generated from the patterns +// (bundle/config/mutator/resourcemutator/defaults_walk.go). +package resourcedefaults + +import ( + "github.com/databricks/cli/bundle/config" + "github.com/databricks/databricks-sdk-go/service/jobs" +) + +// Default is a value to set at every path matching Pattern where it is not set yet. +type Default struct { + Pattern string + Value func(r *config.Root) any +} + +func constant(v any) func(*config.Root) any { + return func(*config.Root) any { return v } +} + +func resourcePath(r *config.Root) any { + return r.Workspace.ResourcePath +} + +// Defaults are applied in order. +var Defaults = []Default{ + {Pattern: "resources.dashboards.*.parent_path", Value: resourcePath}, + {Pattern: "resources.dashboards.*.embed_credentials", Value: constant(false)}, + {Pattern: "resources.genie_spaces.*.parent_path", Value: resourcePath}, + {Pattern: "resources.volumes.*.volume_type", Value: constant("MANAGED")}, + + {Pattern: "resources.alerts.*.parent_path", Value: resourcePath}, + + // Jobs: + + // The defaults are the same as for terraform provider latest version (v1.75.0) + // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/jobs/resource_job.go#L532 + {Pattern: "resources.jobs.*.name", Value: constant("Untitled")}, + {Pattern: "resources.jobs.*.max_concurrent_runs", Value: constant(1)}, + {Pattern: "resources.jobs.*.schedule.pause_status", Value: constant("UNPAUSED")}, + {Pattern: "resources.jobs.*.trigger.pause_status", Value: constant("UNPAUSED")}, + {Pattern: "resources.jobs.*.continuous.pause_status", Value: constant("UNPAUSED")}, + + // Enable queueing for jobs by default, following the behavior from API 2.2+. + // As of 2024-04, we're still using API 2.1 which has queueing disabled by default. + {Pattern: "resources.jobs.*.queue", Value: constant(jobs.QueueSettings{Enabled: true})}, + + // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/clusters/resource_cluster.go + {Pattern: "resources.jobs.*.job_clusters[*].new_cluster.workload_type.clients.notebooks", Value: constant(true)}, + {Pattern: "resources.jobs.*.job_clusters[*].new_cluster.workload_type.clients.jobs", Value: constant(true)}, + + // Pipelines (same as terraform) + // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/pipelines/resource_pipeline.go#L253 + {Pattern: "resources.pipelines.*.edition", Value: constant("ADVANCED")}, + {Pattern: "resources.pipelines.*.channel", Value: constant("CURRENT")}, + + // SqlWarehouses (same as terraform) + // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/sql/resource_sql_endpoint.go#L59 + {Pattern: "resources.sql_warehouses.*.auto_stop_mins", Value: constant(120)}, + {Pattern: "resources.sql_warehouses.*.enable_photon", Value: constant(true)}, + {Pattern: "resources.sql_warehouses.*.max_num_clusters", Value: constant(1)}, + {Pattern: "resources.sql_warehouses.*.spot_instance_policy", Value: constant("COST_OPTIMIZED")}, + + // Apps: + {Pattern: "resources.apps.*.description", Value: constant("")}, + + // Clusters (same as terraform) + // https://github.com/databricks/terraform-provider-databricks/blob/v1.75.0/clusters/resource_cluster.go#L315 + {Pattern: "resources.clusters.*.autotermination_minutes", Value: constant(60)}, + {Pattern: "resources.clusters.*.workload_type.clients.notebooks", Value: constant(true)}, + {Pattern: "resources.clusters.*.workload_type.clients.jobs", Value: constant(true)}, +} + +// Patterns returns the patterns of Defaults, in order. +func Patterns() []string { + out := make([]string, len(Defaults)) + for i, d := range Defaults { + out[i] = d.Pattern + } + return out +} diff --git a/bundle/config/resources/job_run.go b/bundle/config/resources/job_run.go index dff2e538cb0..bff6f895b6e 100644 --- a/bundle/config/resources/job_run.go +++ b/bundle/config/resources/job_run.go @@ -41,6 +41,14 @@ type JobRunLifecycle struct { TriggersState *JobRunTriggersState `json:"triggers_state,omitempty" bundle:"internal"` } +// GetLifecycle returns the lifecycle settings, using JobRunLifecycle. +func (r *JobRun) GetLifecycle() LifecycleConfig { + if r.Lifecycle == nil { + return JobRunLifecycle{} + } + return *r.Lifecycle +} + // JobRunTrigger is one lifecycle.triggers entry. type JobRunTrigger struct { OnBundleDeploy *bool `json:"on_bundle_deploy,omitempty"` @@ -123,15 +131,6 @@ func (r *JobRun) ResourceDescription() ResourceDescription { } } -// GetLifecycle returns the lifecycle settings. Lifecycle shadows BaseResource.Lifecycle, -// which therefore stays empty. -func (r *JobRun) GetLifecycle() LifecycleConfig { - if r.Lifecycle == nil { - return Lifecycle{} - } - return r.Lifecycle.Lifecycle -} - // GetName returns the in-product name, which is empty: a run has no name. func (r *JobRun) GetName() string { return "" diff --git a/bundle/config/resources/permission.go b/bundle/config/resources/permission.go index b71194755c0..17e26152f61 100644 --- a/bundle/config/resources/permission.go +++ b/bundle/config/resources/permission.go @@ -94,3 +94,14 @@ func GetMaxLevel(a, b string) string { } return b } + +// PermissionFields is the struct underlying every resource permission type +// (PermissionT and the named types defined from it, SecretScopePermission), so generic +// code can convert between them with P(PermissionFields[L]{...}). +type PermissionFields[L ~string] = struct { + Level L `json:"level"` + + UserName string `json:"user_name,omitempty"` + ServicePrincipalName string `json:"service_principal_name,omitempty"` + GroupName string `json:"group_name,omitempty"` +} diff --git a/bundle/config/resources_iterate.go b/bundle/config/resources_iterate.go new file mode 100644 index 00000000000..3b709c56dfc --- /dev/null +++ b/bundle/config/resources_iterate.go @@ -0,0 +1,2107 @@ +// Code generated by bundle/internal/validation. DO NOT EDIT. + +package config + +import ( + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/apps" + "github.com/databricks/databricks-sdk-go/service/catalog" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/databricks/databricks-sdk-go/service/iam" + "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/ml" + "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/serving" + "github.com/databricks/databricks-sdk-go/service/sql" + "github.com/databricks/databricks-sdk-go/service/vectorsearch" +) + +// IterateAllResources calls fn for every resource, in the order of the resource type +// and then of the name, until fn returns false. Resources defined as null are +// visited with a nil res. +func (r *Resources) IterateAllResources(fn func(typ, name string, res ConfigResource) bool) { + for _, name := range sortedKeys(r.Alerts) { + var res ConfigResource + if x := r.Alerts[name]; x != nil { + res = x + } + if !fn("alerts", name, res) { + return + } + } + for _, name := range sortedKeys(r.Apps) { + var res ConfigResource + if x := r.Apps[name]; x != nil { + res = x + } + if !fn("apps", name, res) { + return + } + } + for _, name := range sortedKeys(r.Catalogs) { + var res ConfigResource + if x := r.Catalogs[name]; x != nil { + res = x + } + if !fn("catalogs", name, res) { + return + } + } + for _, name := range sortedKeys(r.ClusterPolicies) { + var res ConfigResource + if x := r.ClusterPolicies[name]; x != nil { + res = x + } + if !fn("cluster_policies", name, res) { + return + } + } + for _, name := range sortedKeys(r.Clusters) { + var res ConfigResource + if x := r.Clusters[name]; x != nil { + res = x + } + if !fn("clusters", name, res) { + return + } + } + for _, name := range sortedKeys(r.Dashboards) { + var res ConfigResource + if x := r.Dashboards[name]; x != nil { + res = x + } + if !fn("dashboards", name, res) { + return + } + } + for _, name := range sortedKeys(r.DatabaseCatalogs) { + var res ConfigResource + if x := r.DatabaseCatalogs[name]; x != nil { + res = x + } + if !fn("database_catalogs", name, res) { + return + } + } + for _, name := range sortedKeys(r.DatabaseInstances) { + var res ConfigResource + if x := r.DatabaseInstances[name]; x != nil { + res = x + } + if !fn("database_instances", name, res) { + return + } + } + for _, name := range sortedKeys(r.Experiments) { + var res ConfigResource + if x := r.Experiments[name]; x != nil { + res = x + } + if !fn("experiments", name, res) { + return + } + } + for _, name := range sortedKeys(r.ExternalLocations) { + var res ConfigResource + if x := r.ExternalLocations[name]; x != nil { + res = x + } + if !fn("external_locations", name, res) { + return + } + } + for _, name := range sortedKeys(r.Features) { + var res ConfigResource + if x := r.Features[name]; x != nil { + res = x + } + if !fn("features", name, res) { + return + } + } + for _, name := range sortedKeys(r.GenieSpaces) { + var res ConfigResource + if x := r.GenieSpaces[name]; x != nil { + res = x + } + if !fn("genie_spaces", name, res) { + return + } + } + for _, name := range sortedKeys(r.InstancePools) { + var res ConfigResource + if x := r.InstancePools[name]; x != nil { + res = x + } + if !fn("instance_pools", name, res) { + return + } + } + for _, name := range sortedKeys(r.Snapshots) { + var res ConfigResource + if x := r.Snapshots[name]; x != nil { + res = x + } + if !fn("internal_immutable_snapshots", name, res) { + return + } + } + for _, name := range sortedKeys(r.JobRuns) { + var res ConfigResource + if x := r.JobRuns[name]; x != nil { + res = x + } + if !fn("job_runs", name, res) { + return + } + } + for _, name := range sortedKeys(r.Jobs) { + var res ConfigResource + if x := r.Jobs[name]; x != nil { + res = x + } + if !fn("jobs", name, res) { + return + } + } + for _, name := range sortedKeys(r.McpServices) { + var res ConfigResource + if x := r.McpServices[name]; x != nil { + res = x + } + if !fn("mcp_services", name, res) { + return + } + } + for _, name := range sortedKeys(r.ModelProviderServices) { + var res ConfigResource + if x := r.ModelProviderServices[name]; x != nil { + res = x + } + if !fn("model_provider_services", name, res) { + return + } + } + for _, name := range sortedKeys(r.ModelServices) { + var res ConfigResource + if x := r.ModelServices[name]; x != nil { + res = x + } + if !fn("model_services", name, res) { + return + } + } + for _, name := range sortedKeys(r.ModelServingEndpoints) { + var res ConfigResource + if x := r.ModelServingEndpoints[name]; x != nil { + res = x + } + if !fn("model_serving_endpoints", name, res) { + return + } + } + for _, name := range sortedKeys(r.Models) { + var res ConfigResource + if x := r.Models[name]; x != nil { + res = x + } + if !fn("models", name, res) { + return + } + } + for _, name := range sortedKeys(r.Pipelines) { + var res ConfigResource + if x := r.Pipelines[name]; x != nil { + res = x + } + if !fn("pipelines", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresBranches) { + var res ConfigResource + if x := r.PostgresBranches[name]; x != nil { + res = x + } + if !fn("postgres_branches", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresCatalogs) { + var res ConfigResource + if x := r.PostgresCatalogs[name]; x != nil { + res = x + } + if !fn("postgres_catalogs", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresDatabases) { + var res ConfigResource + if x := r.PostgresDatabases[name]; x != nil { + res = x + } + if !fn("postgres_databases", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresEndpoints) { + var res ConfigResource + if x := r.PostgresEndpoints[name]; x != nil { + res = x + } + if !fn("postgres_endpoints", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresProjects) { + var res ConfigResource + if x := r.PostgresProjects[name]; x != nil { + res = x + } + if !fn("postgres_projects", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresRoles) { + var res ConfigResource + if x := r.PostgresRoles[name]; x != nil { + res = x + } + if !fn("postgres_roles", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresSnapshotSchedules) { + var res ConfigResource + if x := r.PostgresSnapshotSchedules[name]; x != nil { + res = x + } + if !fn("postgres_snapshot_schedules", name, res) { + return + } + } + for _, name := range sortedKeys(r.PostgresSyncedTables) { + var res ConfigResource + if x := r.PostgresSyncedTables[name]; x != nil { + res = x + } + if !fn("postgres_synced_tables", name, res) { + return + } + } + for _, name := range sortedKeys(r.QualityMonitors) { + var res ConfigResource + if x := r.QualityMonitors[name]; x != nil { + res = x + } + if !fn("quality_monitors", name, res) { + return + } + } + for _, name := range sortedKeys(r.RegisteredModels) { + var res ConfigResource + if x := r.RegisteredModels[name]; x != nil { + res = x + } + if !fn("registered_models", name, res) { + return + } + } + for _, name := range sortedKeys(r.Schemas) { + var res ConfigResource + if x := r.Schemas[name]; x != nil { + res = x + } + if !fn("schemas", name, res) { + return + } + } + for _, name := range sortedKeys(r.SecretScopes) { + var res ConfigResource + if x := r.SecretScopes[name]; x != nil { + res = x + } + if !fn("secret_scopes", name, res) { + return + } + } + for _, name := range sortedKeys(r.Secrets) { + var res ConfigResource + if x := r.Secrets[name]; x != nil { + res = x + } + if !fn("secrets", name, res) { + return + } + } + for _, name := range sortedKeys(r.SqlWarehouses) { + var res ConfigResource + if x := r.SqlWarehouses[name]; x != nil { + res = x + } + if !fn("sql_warehouses", name, res) { + return + } + } + for _, name := range sortedKeys(r.SyncedDatabaseTables) { + var res ConfigResource + if x := r.SyncedDatabaseTables[name]; x != nil { + res = x + } + if !fn("synced_database_tables", name, res) { + return + } + } + for _, name := range sortedKeys(r.VectorSearchEndpoints) { + var res ConfigResource + if x := r.VectorSearchEndpoints[name]; x != nil { + res = x + } + if !fn("vector_search_endpoints", name, res) { + return + } + } + for _, name := range sortedKeys(r.VectorSearchIndexes) { + var res ConfigResource + if x := r.VectorSearchIndexes[name]; x != nil { + res = x + } + if !fn("vector_search_indexes", name, res) { + return + } + } + for _, name := range sortedKeys(r.Volumes) { + var res ConfigResource + if x := r.Volumes[name]; x != nil { + res = x + } + if !fn("volumes", name, res) { + return + } + } +} + +// Select returns the resources of r whose names are in keys (by type): new maps that +// share the resources. Types not in keys are nil. +func (r *Resources) Select(keys map[string]map[string]struct{}) Resources { + var out Resources + if names, ok := keys["alerts"]; ok { + out.Alerts = make(map[string]*resources.Alert, len(names)) + for name := range names { + if x, ok := r.Alerts[name]; ok { + out.Alerts[name] = x + } + } + } + if names, ok := keys["apps"]; ok { + out.Apps = make(map[string]*resources.App, len(names)) + for name := range names { + if x, ok := r.Apps[name]; ok { + out.Apps[name] = x + } + } + } + if names, ok := keys["catalogs"]; ok { + out.Catalogs = make(map[string]*resources.Catalog, len(names)) + for name := range names { + if x, ok := r.Catalogs[name]; ok { + out.Catalogs[name] = x + } + } + } + if names, ok := keys["cluster_policies"]; ok { + out.ClusterPolicies = make(map[string]*resources.ClusterPolicy, len(names)) + for name := range names { + if x, ok := r.ClusterPolicies[name]; ok { + out.ClusterPolicies[name] = x + } + } + } + if names, ok := keys["clusters"]; ok { + out.Clusters = make(map[string]*resources.Cluster, len(names)) + for name := range names { + if x, ok := r.Clusters[name]; ok { + out.Clusters[name] = x + } + } + } + if names, ok := keys["dashboards"]; ok { + out.Dashboards = make(map[string]*resources.Dashboard, len(names)) + for name := range names { + if x, ok := r.Dashboards[name]; ok { + out.Dashboards[name] = x + } + } + } + if names, ok := keys["database_catalogs"]; ok { + out.DatabaseCatalogs = make(map[string]*resources.DatabaseCatalog, len(names)) + for name := range names { + if x, ok := r.DatabaseCatalogs[name]; ok { + out.DatabaseCatalogs[name] = x + } + } + } + if names, ok := keys["database_instances"]; ok { + out.DatabaseInstances = make(map[string]*resources.DatabaseInstance, len(names)) + for name := range names { + if x, ok := r.DatabaseInstances[name]; ok { + out.DatabaseInstances[name] = x + } + } + } + if names, ok := keys["experiments"]; ok { + out.Experiments = make(map[string]*resources.MlflowExperiment, len(names)) + for name := range names { + if x, ok := r.Experiments[name]; ok { + out.Experiments[name] = x + } + } + } + if names, ok := keys["external_locations"]; ok { + out.ExternalLocations = make(map[string]*resources.ExternalLocation, len(names)) + for name := range names { + if x, ok := r.ExternalLocations[name]; ok { + out.ExternalLocations[name] = x + } + } + } + if names, ok := keys["features"]; ok { + out.Features = make(map[string]*resources.Feature, len(names)) + for name := range names { + if x, ok := r.Features[name]; ok { + out.Features[name] = x + } + } + } + if names, ok := keys["genie_spaces"]; ok { + out.GenieSpaces = make(map[string]*resources.GenieSpace, len(names)) + for name := range names { + if x, ok := r.GenieSpaces[name]; ok { + out.GenieSpaces[name] = x + } + } + } + if names, ok := keys["instance_pools"]; ok { + out.InstancePools = make(map[string]*resources.InstancePool, len(names)) + for name := range names { + if x, ok := r.InstancePools[name]; ok { + out.InstancePools[name] = x + } + } + } + if names, ok := keys["internal_immutable_snapshots"]; ok { + out.Snapshots = make(map[string]*resources.Snapshot, len(names)) + for name := range names { + if x, ok := r.Snapshots[name]; ok { + out.Snapshots[name] = x + } + } + } + if names, ok := keys["job_runs"]; ok { + out.JobRuns = make(map[string]*resources.JobRun, len(names)) + for name := range names { + if x, ok := r.JobRuns[name]; ok { + out.JobRuns[name] = x + } + } + } + if names, ok := keys["jobs"]; ok { + out.Jobs = make(map[string]*resources.Job, len(names)) + for name := range names { + if x, ok := r.Jobs[name]; ok { + out.Jobs[name] = x + } + } + } + if names, ok := keys["mcp_services"]; ok { + out.McpServices = make(map[string]*resources.McpService, len(names)) + for name := range names { + if x, ok := r.McpServices[name]; ok { + out.McpServices[name] = x + } + } + } + if names, ok := keys["model_provider_services"]; ok { + out.ModelProviderServices = make(map[string]*resources.ModelProviderService, len(names)) + for name := range names { + if x, ok := r.ModelProviderServices[name]; ok { + out.ModelProviderServices[name] = x + } + } + } + if names, ok := keys["model_services"]; ok { + out.ModelServices = make(map[string]*resources.ModelService, len(names)) + for name := range names { + if x, ok := r.ModelServices[name]; ok { + out.ModelServices[name] = x + } + } + } + if names, ok := keys["model_serving_endpoints"]; ok { + out.ModelServingEndpoints = make(map[string]*resources.ModelServingEndpoint, len(names)) + for name := range names { + if x, ok := r.ModelServingEndpoints[name]; ok { + out.ModelServingEndpoints[name] = x + } + } + } + if names, ok := keys["models"]; ok { + out.Models = make(map[string]*resources.MlflowModel, len(names)) + for name := range names { + if x, ok := r.Models[name]; ok { + out.Models[name] = x + } + } + } + if names, ok := keys["pipelines"]; ok { + out.Pipelines = make(map[string]*resources.Pipeline, len(names)) + for name := range names { + if x, ok := r.Pipelines[name]; ok { + out.Pipelines[name] = x + } + } + } + if names, ok := keys["postgres_branches"]; ok { + out.PostgresBranches = make(map[string]*resources.PostgresBranch, len(names)) + for name := range names { + if x, ok := r.PostgresBranches[name]; ok { + out.PostgresBranches[name] = x + } + } + } + if names, ok := keys["postgres_catalogs"]; ok { + out.PostgresCatalogs = make(map[string]*resources.PostgresCatalog, len(names)) + for name := range names { + if x, ok := r.PostgresCatalogs[name]; ok { + out.PostgresCatalogs[name] = x + } + } + } + if names, ok := keys["postgres_databases"]; ok { + out.PostgresDatabases = make(map[string]*resources.PostgresDatabase, len(names)) + for name := range names { + if x, ok := r.PostgresDatabases[name]; ok { + out.PostgresDatabases[name] = x + } + } + } + if names, ok := keys["postgres_endpoints"]; ok { + out.PostgresEndpoints = make(map[string]*resources.PostgresEndpoint, len(names)) + for name := range names { + if x, ok := r.PostgresEndpoints[name]; ok { + out.PostgresEndpoints[name] = x + } + } + } + if names, ok := keys["postgres_projects"]; ok { + out.PostgresProjects = make(map[string]*resources.PostgresProject, len(names)) + for name := range names { + if x, ok := r.PostgresProjects[name]; ok { + out.PostgresProjects[name] = x + } + } + } + if names, ok := keys["postgres_roles"]; ok { + out.PostgresRoles = make(map[string]*resources.PostgresRole, len(names)) + for name := range names { + if x, ok := r.PostgresRoles[name]; ok { + out.PostgresRoles[name] = x + } + } + } + if names, ok := keys["postgres_snapshot_schedules"]; ok { + out.PostgresSnapshotSchedules = make(map[string]*resources.PostgresSnapshotSchedule, len(names)) + for name := range names { + if x, ok := r.PostgresSnapshotSchedules[name]; ok { + out.PostgresSnapshotSchedules[name] = x + } + } + } + if names, ok := keys["postgres_synced_tables"]; ok { + out.PostgresSyncedTables = make(map[string]*resources.PostgresSyncedTable, len(names)) + for name := range names { + if x, ok := r.PostgresSyncedTables[name]; ok { + out.PostgresSyncedTables[name] = x + } + } + } + if names, ok := keys["quality_monitors"]; ok { + out.QualityMonitors = make(map[string]*resources.QualityMonitor, len(names)) + for name := range names { + if x, ok := r.QualityMonitors[name]; ok { + out.QualityMonitors[name] = x + } + } + } + if names, ok := keys["registered_models"]; ok { + out.RegisteredModels = make(map[string]*resources.RegisteredModel, len(names)) + for name := range names { + if x, ok := r.RegisteredModels[name]; ok { + out.RegisteredModels[name] = x + } + } + } + if names, ok := keys["schemas"]; ok { + out.Schemas = make(map[string]*resources.Schema, len(names)) + for name := range names { + if x, ok := r.Schemas[name]; ok { + out.Schemas[name] = x + } + } + } + if names, ok := keys["secret_scopes"]; ok { + out.SecretScopes = make(map[string]*resources.SecretScope, len(names)) + for name := range names { + if x, ok := r.SecretScopes[name]; ok { + out.SecretScopes[name] = x + } + } + } + if names, ok := keys["secrets"]; ok { + out.Secrets = make(map[string]*resources.Secret, len(names)) + for name := range names { + if x, ok := r.Secrets[name]; ok { + out.Secrets[name] = x + } + } + } + if names, ok := keys["sql_warehouses"]; ok { + out.SqlWarehouses = make(map[string]*resources.SqlWarehouse, len(names)) + for name := range names { + if x, ok := r.SqlWarehouses[name]; ok { + out.SqlWarehouses[name] = x + } + } + } + if names, ok := keys["synced_database_tables"]; ok { + out.SyncedDatabaseTables = make(map[string]*resources.SyncedDatabaseTable, len(names)) + for name := range names { + if x, ok := r.SyncedDatabaseTables[name]; ok { + out.SyncedDatabaseTables[name] = x + } + } + } + if names, ok := keys["vector_search_endpoints"]; ok { + out.VectorSearchEndpoints = make(map[string]*resources.VectorSearchEndpoint, len(names)) + for name := range names { + if x, ok := r.VectorSearchEndpoints[name]; ok { + out.VectorSearchEndpoints[name] = x + } + } + } + if names, ok := keys["vector_search_indexes"]; ok { + out.VectorSearchIndexes = make(map[string]*resources.VectorSearchIndex, len(names)) + for name := range names { + if x, ok := r.VectorSearchIndexes[name]; ok { + out.VectorSearchIndexes[name] = x + } + } + } + if names, ok := keys["volumes"]; ok { + out.Volumes = make(map[string]*resources.Volume, len(names)) + for name := range names { + if x, ok := r.Volumes[name]; ok { + out.Volumes[name] = x + } + } + } + return out +} + +// CopyResource sets the resource name of type typ to the one in src (shared, not +// copied). It returns false if src has no such resource. +func (r *Resources) CopyResource(src *Resources, typ, name string) bool { + switch typ { + case "alerts": + x, ok := src.Alerts[name] + if !ok { + return false + } + if r.Alerts == nil { + r.Alerts = map[string]*resources.Alert{} + } + r.Alerts[name] = x + return true + case "apps": + x, ok := src.Apps[name] + if !ok { + return false + } + if r.Apps == nil { + r.Apps = map[string]*resources.App{} + } + r.Apps[name] = x + return true + case "catalogs": + x, ok := src.Catalogs[name] + if !ok { + return false + } + if r.Catalogs == nil { + r.Catalogs = map[string]*resources.Catalog{} + } + r.Catalogs[name] = x + return true + case "cluster_policies": + x, ok := src.ClusterPolicies[name] + if !ok { + return false + } + if r.ClusterPolicies == nil { + r.ClusterPolicies = map[string]*resources.ClusterPolicy{} + } + r.ClusterPolicies[name] = x + return true + case "clusters": + x, ok := src.Clusters[name] + if !ok { + return false + } + if r.Clusters == nil { + r.Clusters = map[string]*resources.Cluster{} + } + r.Clusters[name] = x + return true + case "dashboards": + x, ok := src.Dashboards[name] + if !ok { + return false + } + if r.Dashboards == nil { + r.Dashboards = map[string]*resources.Dashboard{} + } + r.Dashboards[name] = x + return true + case "database_catalogs": + x, ok := src.DatabaseCatalogs[name] + if !ok { + return false + } + if r.DatabaseCatalogs == nil { + r.DatabaseCatalogs = map[string]*resources.DatabaseCatalog{} + } + r.DatabaseCatalogs[name] = x + return true + case "database_instances": + x, ok := src.DatabaseInstances[name] + if !ok { + return false + } + if r.DatabaseInstances == nil { + r.DatabaseInstances = map[string]*resources.DatabaseInstance{} + } + r.DatabaseInstances[name] = x + return true + case "experiments": + x, ok := src.Experiments[name] + if !ok { + return false + } + if r.Experiments == nil { + r.Experiments = map[string]*resources.MlflowExperiment{} + } + r.Experiments[name] = x + return true + case "external_locations": + x, ok := src.ExternalLocations[name] + if !ok { + return false + } + if r.ExternalLocations == nil { + r.ExternalLocations = map[string]*resources.ExternalLocation{} + } + r.ExternalLocations[name] = x + return true + case "features": + x, ok := src.Features[name] + if !ok { + return false + } + if r.Features == nil { + r.Features = map[string]*resources.Feature{} + } + r.Features[name] = x + return true + case "genie_spaces": + x, ok := src.GenieSpaces[name] + if !ok { + return false + } + if r.GenieSpaces == nil { + r.GenieSpaces = map[string]*resources.GenieSpace{} + } + r.GenieSpaces[name] = x + return true + case "instance_pools": + x, ok := src.InstancePools[name] + if !ok { + return false + } + if r.InstancePools == nil { + r.InstancePools = map[string]*resources.InstancePool{} + } + r.InstancePools[name] = x + return true + case "internal_immutable_snapshots": + x, ok := src.Snapshots[name] + if !ok { + return false + } + if r.Snapshots == nil { + r.Snapshots = map[string]*resources.Snapshot{} + } + r.Snapshots[name] = x + return true + case "job_runs": + x, ok := src.JobRuns[name] + if !ok { + return false + } + if r.JobRuns == nil { + r.JobRuns = map[string]*resources.JobRun{} + } + r.JobRuns[name] = x + return true + case "jobs": + x, ok := src.Jobs[name] + if !ok { + return false + } + if r.Jobs == nil { + r.Jobs = map[string]*resources.Job{} + } + r.Jobs[name] = x + return true + case "mcp_services": + x, ok := src.McpServices[name] + if !ok { + return false + } + if r.McpServices == nil { + r.McpServices = map[string]*resources.McpService{} + } + r.McpServices[name] = x + return true + case "model_provider_services": + x, ok := src.ModelProviderServices[name] + if !ok { + return false + } + if r.ModelProviderServices == nil { + r.ModelProviderServices = map[string]*resources.ModelProviderService{} + } + r.ModelProviderServices[name] = x + return true + case "model_services": + x, ok := src.ModelServices[name] + if !ok { + return false + } + if r.ModelServices == nil { + r.ModelServices = map[string]*resources.ModelService{} + } + r.ModelServices[name] = x + return true + case "model_serving_endpoints": + x, ok := src.ModelServingEndpoints[name] + if !ok { + return false + } + if r.ModelServingEndpoints == nil { + r.ModelServingEndpoints = map[string]*resources.ModelServingEndpoint{} + } + r.ModelServingEndpoints[name] = x + return true + case "models": + x, ok := src.Models[name] + if !ok { + return false + } + if r.Models == nil { + r.Models = map[string]*resources.MlflowModel{} + } + r.Models[name] = x + return true + case "pipelines": + x, ok := src.Pipelines[name] + if !ok { + return false + } + if r.Pipelines == nil { + r.Pipelines = map[string]*resources.Pipeline{} + } + r.Pipelines[name] = x + return true + case "postgres_branches": + x, ok := src.PostgresBranches[name] + if !ok { + return false + } + if r.PostgresBranches == nil { + r.PostgresBranches = map[string]*resources.PostgresBranch{} + } + r.PostgresBranches[name] = x + return true + case "postgres_catalogs": + x, ok := src.PostgresCatalogs[name] + if !ok { + return false + } + if r.PostgresCatalogs == nil { + r.PostgresCatalogs = map[string]*resources.PostgresCatalog{} + } + r.PostgresCatalogs[name] = x + return true + case "postgres_databases": + x, ok := src.PostgresDatabases[name] + if !ok { + return false + } + if r.PostgresDatabases == nil { + r.PostgresDatabases = map[string]*resources.PostgresDatabase{} + } + r.PostgresDatabases[name] = x + return true + case "postgres_endpoints": + x, ok := src.PostgresEndpoints[name] + if !ok { + return false + } + if r.PostgresEndpoints == nil { + r.PostgresEndpoints = map[string]*resources.PostgresEndpoint{} + } + r.PostgresEndpoints[name] = x + return true + case "postgres_projects": + x, ok := src.PostgresProjects[name] + if !ok { + return false + } + if r.PostgresProjects == nil { + r.PostgresProjects = map[string]*resources.PostgresProject{} + } + r.PostgresProjects[name] = x + return true + case "postgres_roles": + x, ok := src.PostgresRoles[name] + if !ok { + return false + } + if r.PostgresRoles == nil { + r.PostgresRoles = map[string]*resources.PostgresRole{} + } + r.PostgresRoles[name] = x + return true + case "postgres_snapshot_schedules": + x, ok := src.PostgresSnapshotSchedules[name] + if !ok { + return false + } + if r.PostgresSnapshotSchedules == nil { + r.PostgresSnapshotSchedules = map[string]*resources.PostgresSnapshotSchedule{} + } + r.PostgresSnapshotSchedules[name] = x + return true + case "postgres_synced_tables": + x, ok := src.PostgresSyncedTables[name] + if !ok { + return false + } + if r.PostgresSyncedTables == nil { + r.PostgresSyncedTables = map[string]*resources.PostgresSyncedTable{} + } + r.PostgresSyncedTables[name] = x + return true + case "quality_monitors": + x, ok := src.QualityMonitors[name] + if !ok { + return false + } + if r.QualityMonitors == nil { + r.QualityMonitors = map[string]*resources.QualityMonitor{} + } + r.QualityMonitors[name] = x + return true + case "registered_models": + x, ok := src.RegisteredModels[name] + if !ok { + return false + } + if r.RegisteredModels == nil { + r.RegisteredModels = map[string]*resources.RegisteredModel{} + } + r.RegisteredModels[name] = x + return true + case "schemas": + x, ok := src.Schemas[name] + if !ok { + return false + } + if r.Schemas == nil { + r.Schemas = map[string]*resources.Schema{} + } + r.Schemas[name] = x + return true + case "secret_scopes": + x, ok := src.SecretScopes[name] + if !ok { + return false + } + if r.SecretScopes == nil { + r.SecretScopes = map[string]*resources.SecretScope{} + } + r.SecretScopes[name] = x + return true + case "secrets": + x, ok := src.Secrets[name] + if !ok { + return false + } + if r.Secrets == nil { + r.Secrets = map[string]*resources.Secret{} + } + r.Secrets[name] = x + return true + case "sql_warehouses": + x, ok := src.SqlWarehouses[name] + if !ok { + return false + } + if r.SqlWarehouses == nil { + r.SqlWarehouses = map[string]*resources.SqlWarehouse{} + } + r.SqlWarehouses[name] = x + return true + case "synced_database_tables": + x, ok := src.SyncedDatabaseTables[name] + if !ok { + return false + } + if r.SyncedDatabaseTables == nil { + r.SyncedDatabaseTables = map[string]*resources.SyncedDatabaseTable{} + } + r.SyncedDatabaseTables[name] = x + return true + case "vector_search_endpoints": + x, ok := src.VectorSearchEndpoints[name] + if !ok { + return false + } + if r.VectorSearchEndpoints == nil { + r.VectorSearchEndpoints = map[string]*resources.VectorSearchEndpoint{} + } + r.VectorSearchEndpoints[name] = x + return true + case "vector_search_indexes": + x, ok := src.VectorSearchIndexes[name] + if !ok { + return false + } + if r.VectorSearchIndexes == nil { + r.VectorSearchIndexes = map[string]*resources.VectorSearchIndex{} + } + r.VectorSearchIndexes[name] = x + return true + case "volumes": + x, ok := src.Volumes[name] + if !ok { + return false + } + if r.Volumes == nil { + r.Volumes = map[string]*resources.Volume{} + } + r.Volumes[name] = x + return true + } + return false +} + +// GetResource returns the resource name of type typ. ok is false if there is no such +// resource or it is defined as null. +func (r *Resources) GetResource(typ, name string) (res ConfigResource, ok bool) { + switch typ { + case "alerts": + if x := r.Alerts[name]; x != nil { + return x, true + } + case "apps": + if x := r.Apps[name]; x != nil { + return x, true + } + case "catalogs": + if x := r.Catalogs[name]; x != nil { + return x, true + } + case "cluster_policies": + if x := r.ClusterPolicies[name]; x != nil { + return x, true + } + case "clusters": + if x := r.Clusters[name]; x != nil { + return x, true + } + case "dashboards": + if x := r.Dashboards[name]; x != nil { + return x, true + } + case "database_catalogs": + if x := r.DatabaseCatalogs[name]; x != nil { + return x, true + } + case "database_instances": + if x := r.DatabaseInstances[name]; x != nil { + return x, true + } + case "experiments": + if x := r.Experiments[name]; x != nil { + return x, true + } + case "external_locations": + if x := r.ExternalLocations[name]; x != nil { + return x, true + } + case "features": + if x := r.Features[name]; x != nil { + return x, true + } + case "genie_spaces": + if x := r.GenieSpaces[name]; x != nil { + return x, true + } + case "instance_pools": + if x := r.InstancePools[name]; x != nil { + return x, true + } + case "internal_immutable_snapshots": + if x := r.Snapshots[name]; x != nil { + return x, true + } + case "job_runs": + if x := r.JobRuns[name]; x != nil { + return x, true + } + case "jobs": + if x := r.Jobs[name]; x != nil { + return x, true + } + case "mcp_services": + if x := r.McpServices[name]; x != nil { + return x, true + } + case "model_provider_services": + if x := r.ModelProviderServices[name]; x != nil { + return x, true + } + case "model_services": + if x := r.ModelServices[name]; x != nil { + return x, true + } + case "model_serving_endpoints": + if x := r.ModelServingEndpoints[name]; x != nil { + return x, true + } + case "models": + if x := r.Models[name]; x != nil { + return x, true + } + case "pipelines": + if x := r.Pipelines[name]; x != nil { + return x, true + } + case "postgres_branches": + if x := r.PostgresBranches[name]; x != nil { + return x, true + } + case "postgres_catalogs": + if x := r.PostgresCatalogs[name]; x != nil { + return x, true + } + case "postgres_databases": + if x := r.PostgresDatabases[name]; x != nil { + return x, true + } + case "postgres_endpoints": + if x := r.PostgresEndpoints[name]; x != nil { + return x, true + } + case "postgres_projects": + if x := r.PostgresProjects[name]; x != nil { + return x, true + } + case "postgres_roles": + if x := r.PostgresRoles[name]; x != nil { + return x, true + } + case "postgres_snapshot_schedules": + if x := r.PostgresSnapshotSchedules[name]; x != nil { + return x, true + } + case "postgres_synced_tables": + if x := r.PostgresSyncedTables[name]; x != nil { + return x, true + } + case "quality_monitors": + if x := r.QualityMonitors[name]; x != nil { + return x, true + } + case "registered_models": + if x := r.RegisteredModels[name]; x != nil { + return x, true + } + case "schemas": + if x := r.Schemas[name]; x != nil { + return x, true + } + case "secret_scopes": + if x := r.SecretScopes[name]; x != nil { + return x, true + } + case "secrets": + if x := r.Secrets[name]; x != nil { + return x, true + } + case "sql_warehouses": + if x := r.SqlWarehouses[name]; x != nil { + return x, true + } + case "synced_database_tables": + if x := r.SyncedDatabaseTables[name]; x != nil { + return x, true + } + case "vector_search_endpoints": + if x := r.VectorSearchEndpoints[name]; x != nil { + return x, true + } + case "vector_search_indexes": + if x := r.VectorSearchIndexes[name]; x != nil { + return x, true + } + case "volumes": + if x := r.Volumes[name]; x != nil { + return x, true + } + } + return nil, false +} + +// IterateAllPermissions calls fn for the permissions of every resource that can have +// them, in the order of IterateAllResources, until fn returns false. +func (r *Resources) IterateAllPermissions(fn func(typ, name string, perms PermissionList) bool) { + for _, name := range sortedKeys(r.Alerts) { + if x := r.Alerts[name]; x != nil && !fn("alerts", name, permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Apps) { + if x := r.Apps[name]; x != nil && !fn("apps", name, permissionList[resources.AppPermission, apps.AppPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.ClusterPolicies) { + if x := r.ClusterPolicies[name]; x != nil && !fn("cluster_policies", name, permissionList[resources.ClusterPolicyPermission, compute.ClusterPolicyPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Clusters) { + if x := r.Clusters[name]; x != nil && !fn("clusters", name, permissionList[resources.ClusterPermission, compute.ClusterPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Dashboards) { + if x := r.Dashboards[name]; x != nil && !fn("dashboards", name, permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.DatabaseInstances) { + if x := r.DatabaseInstances[name]; x != nil && !fn("database_instances", name, permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Experiments) { + if x := r.Experiments[name]; x != nil && !fn("experiments", name, permissionList[resources.MlflowExperimentPermission, ml.ExperimentPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.GenieSpaces) { + if x := r.GenieSpaces[name]; x != nil && !fn("genie_spaces", name, permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.InstancePools) { + if x := r.InstancePools[name]; x != nil && !fn("instance_pools", name, permissionList[resources.InstancePoolPermission, compute.InstancePoolPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Jobs) { + if x := r.Jobs[name]; x != nil && !fn("jobs", name, permissionList[resources.JobPermission, jobs.JobPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.ModelServingEndpoints) { + if x := r.ModelServingEndpoints[name]; x != nil && !fn("model_serving_endpoints", name, permissionList[resources.ModelServingEndpointPermission, serving.ServingEndpointPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Models) { + if x := r.Models[name]; x != nil && !fn("models", name, permissionList[resources.MlflowModelPermission, ml.RegisteredModelPermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.Pipelines) { + if x := r.Pipelines[name]; x != nil && !fn("pipelines", name, permissionList[resources.PipelinePermission, pipelines.PipelinePermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.PostgresProjects) { + if x := r.PostgresProjects[name]; x != nil && !fn("postgres_projects", name, permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.SecretScopes) { + if x := r.SecretScopes[name]; x != nil && !fn("secret_scopes", name, permissionList[resources.SecretScopePermission, resources.SecretScopePermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.SqlWarehouses) { + if x := r.SqlWarehouses[name]; x != nil && !fn("sql_warehouses", name, permissionList[resources.SqlWarehousePermission, sql.WarehousePermissionLevel]{&x.Permissions}) { + return + } + } + for _, name := range sortedKeys(r.VectorSearchEndpoints) { + if x := r.VectorSearchEndpoints[name]; x != nil && !fn("vector_search_endpoints", name, permissionList[resources.VectorSearchEndpointPermission, vectorsearch.VectorSearchEndpointPermissionLevel]{&x.Permissions}) { + return + } + } +} + +// PermissionsOf returns the permissions of the resource name of type typ. ok is false +// if there is no such resource or resources of type typ have no permissions. +func (r *Resources) PermissionsOf(typ, name string) (perms PermissionList, ok bool) { + switch typ { + case "alerts": + if x := r.Alerts[name]; x != nil { + return permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}, true + } + case "apps": + if x := r.Apps[name]; x != nil { + return permissionList[resources.AppPermission, apps.AppPermissionLevel]{&x.Permissions}, true + } + case "cluster_policies": + if x := r.ClusterPolicies[name]; x != nil { + return permissionList[resources.ClusterPolicyPermission, compute.ClusterPolicyPermissionLevel]{&x.Permissions}, true + } + case "clusters": + if x := r.Clusters[name]; x != nil { + return permissionList[resources.ClusterPermission, compute.ClusterPermissionLevel]{&x.Permissions}, true + } + case "dashboards": + if x := r.Dashboards[name]; x != nil { + return permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}, true + } + case "database_instances": + if x := r.DatabaseInstances[name]; x != nil { + return permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}, true + } + case "experiments": + if x := r.Experiments[name]; x != nil { + return permissionList[resources.MlflowExperimentPermission, ml.ExperimentPermissionLevel]{&x.Permissions}, true + } + case "genie_spaces": + if x := r.GenieSpaces[name]; x != nil { + return permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}, true + } + case "instance_pools": + if x := r.InstancePools[name]; x != nil { + return permissionList[resources.InstancePoolPermission, compute.InstancePoolPermissionLevel]{&x.Permissions}, true + } + case "jobs": + if x := r.Jobs[name]; x != nil { + return permissionList[resources.JobPermission, jobs.JobPermissionLevel]{&x.Permissions}, true + } + case "model_serving_endpoints": + if x := r.ModelServingEndpoints[name]; x != nil { + return permissionList[resources.ModelServingEndpointPermission, serving.ServingEndpointPermissionLevel]{&x.Permissions}, true + } + case "models": + if x := r.Models[name]; x != nil { + return permissionList[resources.MlflowModelPermission, ml.RegisteredModelPermissionLevel]{&x.Permissions}, true + } + case "pipelines": + if x := r.Pipelines[name]; x != nil { + return permissionList[resources.PipelinePermission, pipelines.PipelinePermissionLevel]{&x.Permissions}, true + } + case "postgres_projects": + if x := r.PostgresProjects[name]; x != nil { + return permissionList[resources.Permission, iam.PermissionLevel]{&x.Permissions}, true + } + case "secret_scopes": + if x := r.SecretScopes[name]; x != nil { + return permissionList[resources.SecretScopePermission, resources.SecretScopePermissionLevel]{&x.Permissions}, true + } + case "sql_warehouses": + if x := r.SqlWarehouses[name]; x != nil { + return permissionList[resources.SqlWarehousePermission, sql.WarehousePermissionLevel]{&x.Permissions}, true + } + case "vector_search_endpoints": + if x := r.VectorSearchEndpoints[name]; x != nil { + return permissionList[resources.VectorSearchEndpointPermission, vectorsearch.VectorSearchEndpointPermissionLevel]{&x.Permissions}, true + } + } + return nil, false +} + +// IterateAllResourceStates calls fn for the deployed id and modified status of every +// resource that has them, in the order of IterateAllResources, until fn returns false. +func (r *Resources) IterateAllResourceStates(fn func(typ, name string, id *string, status *string) bool) { + for _, name := range sortedKeys(r.Alerts) { + if x := r.Alerts[name]; x != nil && !fn("alerts", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Apps) { + if x := r.Apps[name]; x != nil && !fn("apps", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Catalogs) { + if x := r.Catalogs[name]; x != nil && !fn("catalogs", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.ClusterPolicies) { + if x := r.ClusterPolicies[name]; x != nil && !fn("cluster_policies", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Clusters) { + if x := r.Clusters[name]; x != nil && !fn("clusters", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Dashboards) { + if x := r.Dashboards[name]; x != nil && !fn("dashboards", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.DatabaseCatalogs) { + if x := r.DatabaseCatalogs[name]; x != nil && !fn("database_catalogs", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.DatabaseInstances) { + if x := r.DatabaseInstances[name]; x != nil && !fn("database_instances", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Experiments) { + if x := r.Experiments[name]; x != nil && !fn("experiments", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.ExternalLocations) { + if x := r.ExternalLocations[name]; x != nil && !fn("external_locations", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Features) { + if x := r.Features[name]; x != nil && !fn("features", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.GenieSpaces) { + if x := r.GenieSpaces[name]; x != nil && !fn("genie_spaces", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.InstancePools) { + if x := r.InstancePools[name]; x != nil && !fn("instance_pools", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.JobRuns) { + if x := r.JobRuns[name]; x != nil && !fn("job_runs", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Jobs) { + if x := r.Jobs[name]; x != nil && !fn("jobs", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.McpServices) { + if x := r.McpServices[name]; x != nil && !fn("mcp_services", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.ModelProviderServices) { + if x := r.ModelProviderServices[name]; x != nil && !fn("model_provider_services", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.ModelServices) { + if x := r.ModelServices[name]; x != nil && !fn("model_services", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.ModelServingEndpoints) { + if x := r.ModelServingEndpoints[name]; x != nil && !fn("model_serving_endpoints", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Models) { + if x := r.Models[name]; x != nil && !fn("models", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Pipelines) { + if x := r.Pipelines[name]; x != nil && !fn("pipelines", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresBranches) { + if x := r.PostgresBranches[name]; x != nil && !fn("postgres_branches", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresCatalogs) { + if x := r.PostgresCatalogs[name]; x != nil && !fn("postgres_catalogs", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresDatabases) { + if x := r.PostgresDatabases[name]; x != nil && !fn("postgres_databases", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresEndpoints) { + if x := r.PostgresEndpoints[name]; x != nil && !fn("postgres_endpoints", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresProjects) { + if x := r.PostgresProjects[name]; x != nil && !fn("postgres_projects", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresRoles) { + if x := r.PostgresRoles[name]; x != nil && !fn("postgres_roles", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresSnapshotSchedules) { + if x := r.PostgresSnapshotSchedules[name]; x != nil && !fn("postgres_snapshot_schedules", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.PostgresSyncedTables) { + if x := r.PostgresSyncedTables[name]; x != nil && !fn("postgres_synced_tables", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.QualityMonitors) { + if x := r.QualityMonitors[name]; x != nil && !fn("quality_monitors", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.RegisteredModels) { + if x := r.RegisteredModels[name]; x != nil && !fn("registered_models", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Schemas) { + if x := r.Schemas[name]; x != nil && !fn("schemas", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.SecretScopes) { + if x := r.SecretScopes[name]; x != nil && !fn("secret_scopes", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Secrets) { + if x := r.Secrets[name]; x != nil && !fn("secrets", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.SqlWarehouses) { + if x := r.SqlWarehouses[name]; x != nil && !fn("sql_warehouses", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.SyncedDatabaseTables) { + if x := r.SyncedDatabaseTables[name]; x != nil && !fn("synced_database_tables", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.VectorSearchEndpoints) { + if x := r.VectorSearchEndpoints[name]; x != nil && !fn("vector_search_endpoints", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.VectorSearchIndexes) { + if x := r.VectorSearchIndexes[name]; x != nil && !fn("vector_search_indexes", name, &x.ID, &x.ModifiedStatus) { + return + } + } + for _, name := range sortedKeys(r.Volumes) { + if x := r.Volumes[name]; x != nil && !fn("volumes", name, &x.ID, &x.ModifiedStatus) { + return + } + } +} + +// GetOrCreateState returns the deployed id and modified status of the resource name of +// type typ, creating the resource if it does not exist (created is then true). ok is +// false if resources of type typ have no state. +func (r *Resources) GetOrCreateState(typ, name string) (id *string, status *string, created, ok bool) { + switch typ { + case "alerts": + x, exists := r.Alerts[name] + if x == nil { + if r.Alerts == nil { + r.Alerts = map[string]*resources.Alert{} + } + x = &resources.Alert{} + r.Alerts[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "apps": + x, exists := r.Apps[name] + if x == nil { + if r.Apps == nil { + r.Apps = map[string]*resources.App{} + } + x = &resources.App{} + r.Apps[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "catalogs": + x, exists := r.Catalogs[name] + if x == nil { + if r.Catalogs == nil { + r.Catalogs = map[string]*resources.Catalog{} + } + x = &resources.Catalog{} + r.Catalogs[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "cluster_policies": + x, exists := r.ClusterPolicies[name] + if x == nil { + if r.ClusterPolicies == nil { + r.ClusterPolicies = map[string]*resources.ClusterPolicy{} + } + x = &resources.ClusterPolicy{} + r.ClusterPolicies[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "clusters": + x, exists := r.Clusters[name] + if x == nil { + if r.Clusters == nil { + r.Clusters = map[string]*resources.Cluster{} + } + x = &resources.Cluster{} + r.Clusters[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "dashboards": + x, exists := r.Dashboards[name] + if x == nil { + if r.Dashboards == nil { + r.Dashboards = map[string]*resources.Dashboard{} + } + x = &resources.Dashboard{} + r.Dashboards[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "database_catalogs": + x, exists := r.DatabaseCatalogs[name] + if x == nil { + if r.DatabaseCatalogs == nil { + r.DatabaseCatalogs = map[string]*resources.DatabaseCatalog{} + } + x = &resources.DatabaseCatalog{} + r.DatabaseCatalogs[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "database_instances": + x, exists := r.DatabaseInstances[name] + if x == nil { + if r.DatabaseInstances == nil { + r.DatabaseInstances = map[string]*resources.DatabaseInstance{} + } + x = &resources.DatabaseInstance{} + r.DatabaseInstances[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "experiments": + x, exists := r.Experiments[name] + if x == nil { + if r.Experiments == nil { + r.Experiments = map[string]*resources.MlflowExperiment{} + } + x = &resources.MlflowExperiment{} + r.Experiments[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "external_locations": + x, exists := r.ExternalLocations[name] + if x == nil { + if r.ExternalLocations == nil { + r.ExternalLocations = map[string]*resources.ExternalLocation{} + } + x = &resources.ExternalLocation{} + r.ExternalLocations[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "features": + x, exists := r.Features[name] + if x == nil { + if r.Features == nil { + r.Features = map[string]*resources.Feature{} + } + x = &resources.Feature{} + r.Features[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "genie_spaces": + x, exists := r.GenieSpaces[name] + if x == nil { + if r.GenieSpaces == nil { + r.GenieSpaces = map[string]*resources.GenieSpace{} + } + x = &resources.GenieSpace{} + r.GenieSpaces[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "instance_pools": + x, exists := r.InstancePools[name] + if x == nil { + if r.InstancePools == nil { + r.InstancePools = map[string]*resources.InstancePool{} + } + x = &resources.InstancePool{} + r.InstancePools[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "job_runs": + x, exists := r.JobRuns[name] + if x == nil { + if r.JobRuns == nil { + r.JobRuns = map[string]*resources.JobRun{} + } + x = &resources.JobRun{} + r.JobRuns[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "jobs": + x, exists := r.Jobs[name] + if x == nil { + if r.Jobs == nil { + r.Jobs = map[string]*resources.Job{} + } + x = &resources.Job{} + r.Jobs[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "mcp_services": + x, exists := r.McpServices[name] + if x == nil { + if r.McpServices == nil { + r.McpServices = map[string]*resources.McpService{} + } + x = &resources.McpService{} + r.McpServices[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "model_provider_services": + x, exists := r.ModelProviderServices[name] + if x == nil { + if r.ModelProviderServices == nil { + r.ModelProviderServices = map[string]*resources.ModelProviderService{} + } + x = &resources.ModelProviderService{} + r.ModelProviderServices[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "model_services": + x, exists := r.ModelServices[name] + if x == nil { + if r.ModelServices == nil { + r.ModelServices = map[string]*resources.ModelService{} + } + x = &resources.ModelService{} + r.ModelServices[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "model_serving_endpoints": + x, exists := r.ModelServingEndpoints[name] + if x == nil { + if r.ModelServingEndpoints == nil { + r.ModelServingEndpoints = map[string]*resources.ModelServingEndpoint{} + } + x = &resources.ModelServingEndpoint{} + r.ModelServingEndpoints[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "models": + x, exists := r.Models[name] + if x == nil { + if r.Models == nil { + r.Models = map[string]*resources.MlflowModel{} + } + x = &resources.MlflowModel{} + r.Models[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "pipelines": + x, exists := r.Pipelines[name] + if x == nil { + if r.Pipelines == nil { + r.Pipelines = map[string]*resources.Pipeline{} + } + x = &resources.Pipeline{} + r.Pipelines[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_branches": + x, exists := r.PostgresBranches[name] + if x == nil { + if r.PostgresBranches == nil { + r.PostgresBranches = map[string]*resources.PostgresBranch{} + } + x = &resources.PostgresBranch{} + r.PostgresBranches[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_catalogs": + x, exists := r.PostgresCatalogs[name] + if x == nil { + if r.PostgresCatalogs == nil { + r.PostgresCatalogs = map[string]*resources.PostgresCatalog{} + } + x = &resources.PostgresCatalog{} + r.PostgresCatalogs[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_databases": + x, exists := r.PostgresDatabases[name] + if x == nil { + if r.PostgresDatabases == nil { + r.PostgresDatabases = map[string]*resources.PostgresDatabase{} + } + x = &resources.PostgresDatabase{} + r.PostgresDatabases[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_endpoints": + x, exists := r.PostgresEndpoints[name] + if x == nil { + if r.PostgresEndpoints == nil { + r.PostgresEndpoints = map[string]*resources.PostgresEndpoint{} + } + x = &resources.PostgresEndpoint{} + r.PostgresEndpoints[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_projects": + x, exists := r.PostgresProjects[name] + if x == nil { + if r.PostgresProjects == nil { + r.PostgresProjects = map[string]*resources.PostgresProject{} + } + x = &resources.PostgresProject{} + r.PostgresProjects[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_roles": + x, exists := r.PostgresRoles[name] + if x == nil { + if r.PostgresRoles == nil { + r.PostgresRoles = map[string]*resources.PostgresRole{} + } + x = &resources.PostgresRole{} + r.PostgresRoles[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_snapshot_schedules": + x, exists := r.PostgresSnapshotSchedules[name] + if x == nil { + if r.PostgresSnapshotSchedules == nil { + r.PostgresSnapshotSchedules = map[string]*resources.PostgresSnapshotSchedule{} + } + x = &resources.PostgresSnapshotSchedule{} + r.PostgresSnapshotSchedules[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "postgres_synced_tables": + x, exists := r.PostgresSyncedTables[name] + if x == nil { + if r.PostgresSyncedTables == nil { + r.PostgresSyncedTables = map[string]*resources.PostgresSyncedTable{} + } + x = &resources.PostgresSyncedTable{} + r.PostgresSyncedTables[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "quality_monitors": + x, exists := r.QualityMonitors[name] + if x == nil { + if r.QualityMonitors == nil { + r.QualityMonitors = map[string]*resources.QualityMonitor{} + } + x = &resources.QualityMonitor{} + r.QualityMonitors[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "registered_models": + x, exists := r.RegisteredModels[name] + if x == nil { + if r.RegisteredModels == nil { + r.RegisteredModels = map[string]*resources.RegisteredModel{} + } + x = &resources.RegisteredModel{} + r.RegisteredModels[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "schemas": + x, exists := r.Schemas[name] + if x == nil { + if r.Schemas == nil { + r.Schemas = map[string]*resources.Schema{} + } + x = &resources.Schema{} + r.Schemas[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "secret_scopes": + x, exists := r.SecretScopes[name] + if x == nil { + if r.SecretScopes == nil { + r.SecretScopes = map[string]*resources.SecretScope{} + } + x = &resources.SecretScope{} + r.SecretScopes[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "secrets": + x, exists := r.Secrets[name] + if x == nil { + if r.Secrets == nil { + r.Secrets = map[string]*resources.Secret{} + } + x = &resources.Secret{} + r.Secrets[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "sql_warehouses": + x, exists := r.SqlWarehouses[name] + if x == nil { + if r.SqlWarehouses == nil { + r.SqlWarehouses = map[string]*resources.SqlWarehouse{} + } + x = &resources.SqlWarehouse{} + r.SqlWarehouses[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "synced_database_tables": + x, exists := r.SyncedDatabaseTables[name] + if x == nil { + if r.SyncedDatabaseTables == nil { + r.SyncedDatabaseTables = map[string]*resources.SyncedDatabaseTable{} + } + x = &resources.SyncedDatabaseTable{} + r.SyncedDatabaseTables[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "vector_search_endpoints": + x, exists := r.VectorSearchEndpoints[name] + if x == nil { + if r.VectorSearchEndpoints == nil { + r.VectorSearchEndpoints = map[string]*resources.VectorSearchEndpoint{} + } + x = &resources.VectorSearchEndpoint{} + r.VectorSearchEndpoints[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "vector_search_indexes": + x, exists := r.VectorSearchIndexes[name] + if x == nil { + if r.VectorSearchIndexes == nil { + r.VectorSearchIndexes = map[string]*resources.VectorSearchIndex{} + } + x = &resources.VectorSearchIndex{} + r.VectorSearchIndexes[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + case "volumes": + x, exists := r.Volumes[name] + if x == nil { + if r.Volumes == nil { + r.Volumes = map[string]*resources.Volume{} + } + x = &resources.Volume{} + r.Volumes[name] = x + } + return &x.ID, &x.ModifiedStatus, !exists, true + default: + return nil, nil, false, false + } +} + +// IterateAllGrants calls fn for the grants of every resource that can have them, in +// the order of IterateAllResources, until fn returns false. +func (r *Resources) IterateAllGrants(fn func(typ, name string, grants *[]catalog.PrivilegeAssignment) bool) { + for _, name := range sortedKeys(r.Catalogs) { + if x := r.Catalogs[name]; x != nil && !fn("catalogs", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.ExternalLocations) { + if x := r.ExternalLocations[name]; x != nil && !fn("external_locations", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.McpServices) { + if x := r.McpServices[name]; x != nil && !fn("mcp_services", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.ModelProviderServices) { + if x := r.ModelProviderServices[name]; x != nil && !fn("model_provider_services", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.ModelServices) { + if x := r.ModelServices[name]; x != nil && !fn("model_services", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.RegisteredModels) { + if x := r.RegisteredModels[name]; x != nil && !fn("registered_models", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.Schemas) { + if x := r.Schemas[name]; x != nil && !fn("schemas", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.Secrets) { + if x := r.Secrets[name]; x != nil && !fn("secrets", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.VectorSearchIndexes) { + if x := r.VectorSearchIndexes[name]; x != nil && !fn("vector_search_indexes", name, &x.Grants) { + return + } + } + for _, name := range sortedKeys(r.Volumes) { + if x := r.Volumes[name]; x != nil && !fn("volumes", name, &x.Grants) { + return + } + } +} diff --git a/bundle/config/resources_permissions.go b/bundle/config/resources_permissions.go new file mode 100644 index 00000000000..0b4bddb8fd9 --- /dev/null +++ b/bundle/config/resources_permissions.go @@ -0,0 +1,96 @@ +package config + +import ( + "maps" + "slices" + + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/catalog" + "github.com/databricks/databricks-sdk-go/service/iam" +) + +// PermissionList is the permissions of a resource. Their types differ between +// resources, but all have the fields of [resources.PermissionFields]. +type PermissionList interface { + // IsNil reports whether the permissions are not set. + IsNil() bool + Len() int + // At returns permission i. + At(i int) resources.Permission + // Append appends p. + Append(p resources.Permission) + // Set replaces the permissions with ps. + Set(ps []resources.Permission) +} + +// permissionList is the [PermissionList] of a resource's permissions of type []P. +type permissionList[P ~resources.PermissionFields[L], L ~string] struct { + p *[]P +} + +func (l permissionList[P, L]) IsNil() bool { + return *l.p == nil +} + +func (l permissionList[P, L]) Len() int { + return len(*l.p) +} + +func (l permissionList[P, L]) At(i int) resources.Permission { + f := resources.PermissionFields[L]((*l.p)[i]) + return resources.Permission{ + Level: iam.PermissionLevel(f.Level), + UserName: f.UserName, + ServicePrincipalName: f.ServicePrincipalName, + GroupName: f.GroupName, + } +} + +func (l permissionList[P, L]) Set(ps []resources.Permission) { + *l.p = make([]P, 0, len(ps)) + for _, p := range ps { + l.Append(p) + } +} + +func (l permissionList[P, L]) Append(p resources.Permission) { + *l.p = append(*l.p, P(resources.PermissionFields[L]{ + Level: L(p.Level), + UserName: p.UserName, + ServicePrincipalName: p.ServicePrincipalName, + GroupName: p.GroupName, + })) +} + +// sortedKeys returns the keys of a resource map in iteration order. +func sortedKeys[V any](m map[string]V) []string { + return slices.Sorted(maps.Keys(m)) +} + +// ResourceNodes returns the keys of all resources and of their permissions and grants +// where those are present ("resources.jobs.foo", "resources.jobs.foo.permissions"): +// first all resources, then all permissions, then all grants. Permissions and grants +// are present if they are set or hold a pure reference. +func (r *Root) ResourceNodes() []string { + var nodes []string + r.Resources.IterateAllResources(func(typ, name string, _ ConfigResource) bool { + nodes = append(nodes, structpath.NewPath(nil, "resources", typ, name).String()) + return true + }) + r.Resources.IterateAllPermissions(func(typ, name string, perms PermissionList) bool { + p := structpath.NewPath(nil, "resources", typ, name, "permissions") + if !perms.IsNil() || r.IsReferenceAt(p) { + nodes = append(nodes, p.String()) + } + return true + }) + r.Resources.IterateAllGrants(func(typ, name string, grants *[]catalog.PrivilegeAssignment) bool { + p := structpath.NewPath(nil, "resources", typ, name, "grants") + if *grants != nil || r.IsReferenceAt(p) { + nodes = append(nodes, p.String()) + } + return true + }) + return nodes +} diff --git a/bundle/config/resources_types_test.go b/bundle/config/resources_types_test.go index 7a698604612..d2aadd7f530 100644 --- a/bundle/config/resources_types_test.go +++ b/bundle/config/resources_types_test.go @@ -3,6 +3,7 @@ package config import ( "encoding/json" "fmt" + "maps" "reflect" "slices" "strings" @@ -12,10 +13,9 @@ import ( "github.com/stretchr/testify/require" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/structs/structtag" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/cli/libs/structs/structwalk" ) @@ -32,14 +32,14 @@ func TestResourcesTypesMap(t *testing.T) { } // TestResourceTypesZeroValueFieldsSerialize guards against the ForceSendFields -// routing bug fixed in libs/dyn/convert: a field declared in a struct embedded +// routing bug: a field declared in a struct embedded // more than one level deep (e.g. PostgresProject -> PostgresProjectConfig -> // ProjectSpec) had its zero value recorded in the wrong struct's ForceSendFields, // which the SDK marshaler rejects with "field X cannot be found in struct Y". // The direct engine hits this path when it serializes planned state to JSON. // // For every registered resource type it sets every omitempty scalar field (at any -// depth) to its zero value, converts via ToTyped, and marshals - the same round +// depth) to its zero value, decodes it into the typed value, and marshals - the same round // trip the direct engine performs. Any newly added resource whose wrapper embeds // an SDK spec is covered automatically. func TestResourceTypesZeroValueFieldsSerialize(t *testing.T) { @@ -53,41 +53,38 @@ func TestResourceTypesZeroValueFieldsSerialize(t *testing.T) { t.Run(name, func(t *testing.T) { typ := ResourcesTypes[name] zeros := zeroValueScalars(typ, 0, map[reflect.Type]bool{}) - if zeros.Kind() != dyn.KindMap { + if zeros == nil { return } ptr := reflect.New(typ) - require.NoError(t, convert.ToTyped(ptr.Interface(), zeros)) + _, err := (&structvar.StructVar{Value: ptr.Interface()}).Assign(nil, structvar.NewView(&zeros, nil, nil)) + require.NoError(t, err) - _, err := json.Marshal(ptr.Interface()) + _, err = json.Marshal(ptr.Interface()) require.NoError(t, err) }) } } -// zeroValueScalars builds a [dyn.Value] map that sets every omitempty scalar field +// zeroValueScalars builds a map that sets every omitempty scalar field // reachable through embedded anonymous structs to its zero value. Those are exactly // the fields the convert layer records in ForceSendFields, so they exercise the // routing logic. depth and seen bound recursion against deep or recursive types. -func zeroValueScalars(t reflect.Type, depth int, seen map[reflect.Type]bool) dyn.Value { +func zeroValueScalars(t reflect.Type, depth int, seen map[reflect.Type]bool) map[string]any { for t.Kind() == reflect.Pointer { t = t.Elem() } if t.Kind() != reflect.Struct || depth > 6 || seen[t] { - return dyn.NilValue + return nil } seen[t] = true defer delete(seen, t) - m := dyn.NewMapping() + m := map[string]any{} for f := range t.Fields() { if f.Anonymous { - if sub := zeroValueScalars(f.Type, depth+1, seen); sub.Kind() == dyn.KindMap { - for _, p := range sub.MustMap().Pairs() { - m.SetLoc(p.Key.MustString(), nil, p.Value) - } - } + maps.Copy(m, zeroValueScalars(f.Type, depth+1, seen)) continue } @@ -99,18 +96,18 @@ func zeroValueScalars(t reflect.Type, depth int, seen map[reflect.Type]bool) dyn switch f.Type.Kind() { case reflect.Bool: - m.SetLoc(name, nil, dyn.V(false)) + m[name] = false case reflect.String: - m.SetLoc(name, nil, dyn.V("")) + m[name] = "" case reflect.Int, reflect.Int32, reflect.Int64: - m.SetLoc(name, nil, dyn.V(int64(0))) + m[name] = int64(0) case reflect.Float32, reflect.Float64: - m.SetLoc(name, nil, dyn.V(float64(0))) + m[name] = float64(0) default: // Only basic types are eligible for ForceSendFields; skip the rest. } } - return dyn.V(m) + return m } // TestNoSameDepthJSONShadows uses structwalk.WalkType — the dumb walker that diff --git a/bundle/config/root.go b/bundle/config/root.go index a64b742630e..b1a61a4a49f 100644 --- a/bundle/config/root.go +++ b/bundle/config/root.go @@ -2,23 +2,25 @@ package config import ( "bytes" - "context" "errors" "fmt" + "io" + "iter" "os" "reflect" + "slices" "strings" + "github.com/databricks/cli/bundle/config/loctable" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/config/variable" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/dynloc" "github.com/databricks/cli/libs/dyn/merge" "github.com/databricks/cli/libs/dyn/yamlloader" - "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -35,8 +37,16 @@ type Script struct { } type Root struct { //nolint:recvcheck // value receivers for read-only accessors, pointer for mutators + // value holds the configuration tree while the configuration is loading + // (see [Root.Loading]); the fields below are zero until then. value dyn.Value - depth int + + // refs holds the pure variable references in fields that cannot hold a string + // (e.g. `max_retries: ${var.n}`, where the typed field is zero), keyed by path. + refs map[string]string + + // locations holds the source locations of the values. + locations *structvar.Locations // Contains user defined variables Variables map[string]*variable.Variable `json:"variables,omitempty"` @@ -88,7 +98,7 @@ type Root struct { //nolint:recvcheck // value receivers for read-only accessors // Locations is an output-only field that holds configuration location // information for every path in the configuration tree. - Locations *dynloc.Locations `json:"__locations,omitempty" bundle:"internal"` + Locations *loctable.Locations `json:"__locations,omitempty" bundle:"internal"` Scripts map[string]Script `json:"scripts,omitempty"` @@ -99,7 +109,8 @@ type Root struct { //nolint:recvcheck // value receivers for read-only accessors Definitions any `json:"definitions,omitempty" bundle:"internal"` } -// Load loads the bundle configuration file at the specified path. +// Load loads the bundle configuration file at the specified path into a typed +// configuration (see [LoadDyn] to load it for merging). func Load(path string) (*Root, diag.Diagnostics) { raw, err := os.ReadFile(path) if err != nil { @@ -109,9 +120,33 @@ func Load(path string) (*Root, diag.Diagnostics) { return LoadFromBytes(path, raw) } +// LoadFromBytes is [Load] for the contents of a file. func LoadFromBytes(path string, raw []byte) (*Root, diag.Diagnostics) { - r := Root{} + r, diags := LoadDynFromBytes(path, raw) + if diags.HasError() { + return nil, diags + } + finishDiags, err := r.FinishLoading() + diags = diags.Extend(finishDiags) + if err != nil { + return nil, diags.Extend(diag.Errorf("failed to load %s: %v", path, err)) + } + return r, diags +} + +// LoadDyn loads the bundle configuration file at the specified path into a +// configuration that is loading (see [Root.Loading]), to be merged with others. +func LoadDyn(path string) (*Root, diag.Diagnostics) { + raw, err := os.ReadFile(path) + if err != nil { + return nil, diag.FromErr(err) + } + + return LoadDynFromBytes(path, raw) +} +// LoadDynFromBytes is [LoadDyn] for the contents of a file. +func LoadDynFromBytes(path string, raw []byte) (*Root, diag.Diagnostics) { // Load configuration tree from YAML. v, err := yamlloader.LoadYAML(path, bytes.NewBuffer(raw)) if err != nil { @@ -132,139 +167,101 @@ func LoadFromBytes(path string, raw []byte) (*Root, diag.Diagnostics) { } // Normalize dynamic configuration tree according to configuration type. - v, diags := convert.Normalize(r, v) - - // Convert normalized configuration tree to typed configuration. - err = r.updateWithDynamicValue(v) - if err != nil { - diags = diags.Extend(diag.Errorf("failed to load %s: %v", path, err)) - return nil, diags - } - return &r, diags + nv, diags := convert.Normalize(Root{}, v) + if !nv.IsValid() { + // The document is not a map: report the error main's conversion to the + // typed configuration reported. + err := convert.ToTyped(&Root{}, v) + return nil, diags.Extend(diag.Errorf("failed to load %s: %v", path, err)) + } + return &Root{value: nv}, diags } -func (r *Root) initializeDynamicValue() error { - // Many test cases initialize a config as a Go struct literal. - // The value will be invalid and we need to populate it from the typed configuration. - if r.value.IsValid() { - return nil +// LoadFromReader decodes the configuration in r, recording the locations mapLocations +// returns (if not nil) instead of the locations in the file. +func LoadFromReader(path string, r io.Reader, mapLocations structvar.LocationMapper) (*Root, diag.Diagnostics, error) { + node, err := structvar.ParseYAML(r) + if err != nil { + return nil, nil, err } - - nv, err := convert.FromTyped(r, dyn.NilValue) + var root Root + sv, diags, err := structvar.DecodeYAMLNode(path, node, &root, mapLocations) if err != nil { - return err + return nil, diags, err } - - r.value = nv - return nil + root.store(sv) + return &root, diags, nil } -func (r *Root) updateWithDynamicValue(nv dyn.Value) error { - // Hack: restore state; it may be cleared by [ToTyped] if - // the configuration equals nil (happens in tests). - depth := r.depth +// Loading reports whether the configuration is loading: it is a configuration tree +// that the load phase merges (includes, target overrides) and only [Root.Value], +// [Root.Mutate], [Root.Merge] and [Root.MergeTargetOverrides] may be used. The typed +// fields are zero until [Root.FinishLoading] converts the tree into them. +func (r *Root) Loading() bool { + return r.value.IsValid() +} - defer func() { - r.depth = depth - }() +// Value returns the configuration tree of a loading configuration +// (invalid if it is not loading). +func (r *Root) Value() dyn.Value { + return r.value +} - // Convert normalized configuration tree to typed configuration. - err := convert.ToTyped(r, nv) +// StartLoading makes the configuration loading, converting the typed configuration +// into a configuration tree if it is not (like main did on entry of the first +// mutator). A bundle's configuration starts out empty and the entry point merges the +// loaded configuration into it; tests may start with a typed configuration. +func (r *Root) StartLoading() error { + if r.Loading() { + return nil + } + v, err := convert.FromTyped(r, dyn.NilValue) if err != nil { return err } - - // Assign the normalized configuration tree. - r.value = nv + *r = Root{value: v} return nil } -// Mutate applies a transformation to the dynamic configuration value of a Root object. -// -// Parameters: -// - fn: A function that mutates a dyn.Value object -// -// Example usage, setting bundle.deployment.lock.enabled to false: -// -// err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { -// return dyn.Map(v, "bundle.deployment.lock", func(_ dyn.Path, v dyn.Value) (dyn.Value, error) { -// return dyn.Set(v, "enabled", dyn.V(false)) -// }) -// }) +// Mutate applies fn to the configuration tree of a loading configuration. func (r *Root) Mutate(fn func(dyn.Value) (dyn.Value, error)) error { - err := r.initializeDynamicValue() - if err != nil { + if err := r.StartLoading(); err != nil { return err } nv, err := fn(r.value) if err != nil { return err } - err = r.updateWithDynamicValue(nv) - if err != nil { - return err - } + r.value = nv return nil } -func (r *Root) MarkMutatorEntry(ctx context.Context) error { - err := r.initializeDynamicValue() - if err != nil { - return err +// FinishLoading converts the configuration tree of a loading configuration into the +// typed configuration, its references and locations, once. The diagnostics explain +// values that could not be converted and were dropped. +func (r *Root) FinishLoading() (diag.Diagnostics, error) { + if !r.Loading() { + return nil, nil } - - r.depth++ - - // If we are entering a mutator at depth 1, we need to convert - // the dynamic configuration tree to typed configuration. - if r.depth == 1 { - // Always run ToTyped upon entering a mutator. - // Convert normalized configuration tree to typed configuration. - err := r.updateWithDynamicValue(r.value) - if err != nil { - log.Warnf(ctx, "unable to convert dynamic configuration to typed configuration: %v", err) - return err - } - - } else { - nv, err := convert.FromTyped(r, r.value) - if err != nil { - log.Warnf(ctx, "unable to convert typed configuration to dynamic configuration: %v", err) - return err - } - - // Re-run ToTyped to ensure that no state is piggybacked - err = r.updateWithDynamicValue(nv) - if err != nil { - log.Warnf(ctx, "unable to convert dynamic configuration to typed configuration: %v", err) - return err - } + var out Root + sv, diags, err := structvar.DecodeDyn(r.value, &out) + if err != nil { + return diags, err } - - return nil + *r = out + r.store(sv) + return diags, nil } -func (r *Root) MarkMutatorExit(ctx context.Context) error { - r.depth-- - - // If we are exiting a mutator at depth 0, we need to convert - // the typed configuration to a dynamic configuration tree. - if r.depth == 0 { - nv, err := convert.FromTyped(r, r.value) - if err != nil { - log.Warnf(ctx, "unable to convert typed configuration to dynamic configuration: %v", err) - return err - } - - // Re-run ToTyped to ensure that no state is piggybacked - err = r.updateWithDynamicValue(nv) - if err != nil { - log.Warnf(ctx, "unable to convert dynamic configuration to typed configuration: %v", err) - return err - } - } +// vars returns the configuration as a [structvar.StructVar]; use [Root.store] to +// keep changes made to its references and locations. +func (r *Root) vars() *structvar.StructVar { + return &structvar.StructVar{Value: r, Refs: r.refs, Locations: r.locations} +} - return nil +func (r *Root) store(sv *structvar.StructVar) { + r.refs = sv.Refs + r.locations = sv.Locations } // Initializes variables using values passed from the command line flag @@ -295,11 +292,25 @@ func (r *Root) InitializeVariables(vars []string) error { return nil } -func (r *Root) Merge(other *Root) error { - // Merge dynamic configuration values. - return r.Mutate(func(root dyn.Value) (dyn.Value, error) { - return merge.Merge(root, other.value) - }) +// Merge merges the other loading configurations into this one, in order. +func (r *Root) Merge(others ...*Root) error { + if err := r.StartLoading(); err != nil { + return err + } + for _, other := range others { + nv, err := merge.Merge(r.value, other.value) + if err != nil { + return err + } + r.value = nv + } + return nil +} + +// HasTarget reports whether the loading configuration defines the target name. +func (r *Root) HasTarget(name string) bool { + _, err := dyn.GetByPath(r.value, dyn.NewPath(dyn.Key("targets"), dyn.Key(name))) + return err == nil } func mergeField(rv, ov dyn.Value, name string) (dyn.Value, error) { @@ -333,6 +344,14 @@ func (r *Root) MergeTargetOverrides(name string) error { return err } + // Main converted the configuration to the typed configuration and back around + // every mutator, so the target only held what its typed form holds (e.g. not an + // explicitly null run_as, which would otherwise replace the root's run_as below). + target, err = roundTripTarget(target) + if err != nil { + return err + } + // Confirm validity of variable overrides. err = validateVariableOverrides(root, target) if err != nil { @@ -446,8 +465,17 @@ func (r *Root) MergeTargetOverrides(name string) error { } } - // Convert normalized configuration tree to typed configuration. - return r.updateWithDynamicValue(root) + r.value = root + return nil +} + +// roundTripTarget converts the target to the typed [Target] and back. +func roundTripTarget(v dyn.Value) (dyn.Value, error) { + var t *Target + if err := convert.ToTyped(&t, v); err != nil { + return dyn.InvalidValue, err + } + return convert.FromTyped(t, v) } var allowedVariableDefinitions = []([]string){ @@ -554,83 +582,173 @@ func rewriteShorthands(v dyn.Value) (dyn.Value, error) { // validateVariableOverrides checks that all variables specified // in the target override are also defined in the root. -func validateVariableOverrides(root, target dyn.Value) (err error) { - var rv map[string]variable.Variable - var tv map[string]variable.Variable - - // Collect variables from the root. - if v := root.Get("variables"); v.Kind() != dyn.KindInvalid { - err = convert.ToTyped(&rv, v) - if err != nil { - return fmt.Errorf("unable to collect variables from root: %w", err) - } - } - - // Collect variables from the target. - if v := target.Get("variables"); v.Kind() != dyn.KindInvalid { - err = convert.ToTyped(&tv, v) - if err != nil { - return fmt.Errorf("unable to collect variables from target: %w", err) - } - } +func validateVariableOverrides(root, target dyn.Value) error { + rv, _ := root.Get("variables").AsMap() + tv, _ := target.Get("variables").AsMap() // Check that all variables in the target exist in the root. - for k := range tv { - if _, ok := rv[k]; !ok { - return fmt.Errorf("variable %s is not defined but is assigned a value", k) + for _, p := range tv.Pairs() { + if _, ok := rv.GetPair(p.Key); !ok { + return fmt.Errorf("variable %s is not defined but is assigned a value", p.Key.MustString()) } } return nil } -// Best effort to get the location of configuration value at the specified path. -// This function is useful to annotate error messages with the location, because -// we don't want to fail with a different error message if we cannot retrieve the location. +// Set sets value at path (see [structvar.StructVar.Set]). +func (r *Root) Set(path *structpath.PathNode, value any) error { + sv := r.vars() + defer r.store(sv) + return sv.Set(path, value) +} + +// SetReference records the pure reference ref at path, a field that cannot hold a string. +func (r *Root) SetReference(path *structpath.PathNode, ref string) error { + sv := r.vars() + defer r.store(sv) + return sv.SetReference(path, ref) +} + +// Assign sets the value described by v at path, with its locations and references. +func (r *Root) Assign(path *structpath.PathNode, v structvar.View) error { + _, err := r.Decode(path, v) + return err +} + +// Decode sets the value described by v at path, converting it to the type at path; +// the diagnostics explain values that could not be converted and were dropped. +func (r *Root) Decode(path *structpath.PathNode, v structvar.View) (diag.Diagnostics, error) { + sv := r.vars() + defer r.store(sv) + return sv.Assign(path, v) +} + +// Delete removes the value at path (see [structvar.StructVar.Delete]). +func (r *Root) Delete(path *structpath.PathNode) error { + sv := r.vars() + defer r.store(sv) + return sv.Delete(path) +} + +// MergeAt merges the value described by v into the value at path (see [structvar.StructVar.Merge]). +func (r *Root) MergeAt(path *structpath.PathNode, v structvar.View) error { + sv := r.vars() + defer r.store(sv) + return sv.Merge(path, v) +} + +// MergeElementsByKey merges the elements of the sequence at path that have the same +// key (see [structvar.StructVar.MergeElementsByKey]). +func (r *Root) MergeElementsByKey(path *structpath.PathNode, keyField string, keyFn func(structvar.View) string, sortKeys bool) error { + sv := r.vars() + defer r.store(sv) + return sv.MergeElementsByKey(path, keyField, keyFn, sortKeys) +} + +// SetLocations sets the locations of the value at path and all values below it. +func (r *Root) SetLocations(path *structpath.PathNode, locs []diag.Location) { + sv := r.vars() + sv.SetLocations(path, locs) + r.store(sv) +} + +// CopyMetadata replaces the locations and references of the value at path with those in +// src (see [structvar.StructVar.CopyMetadata]). +func (r *Root) CopyMetadata(src *Root, path *structpath.PathNode) { + sv := r.vars() + sv.CopyMetadata(src.vars(), path) + r.store(sv) +} + +// MoveMetadata moves the locations and references of the value at path from to the +// path to (see [structvar.StructVar.MoveMetadata]). +func (r *Root) MoveMetadata(from, to *structpath.PathNode) { + sv := r.vars() + sv.MoveMetadata(from, to) + r.store(sv) +} + +// CopyMetadataAt copies the locations and references of the value at path from to the +// path to (see [structvar.StructVar.CopyMetadataAt]). +func (r *Root) CopyMetadataAt(from, to *structpath.PathNode) { + sv := r.vars() + sv.CopyMetadataAt(from, to) + r.store(sv) +} + +// UpdateSequence records that the elements of the sequence at path were rebuilt from +// the old ones (see [structvar.StructVar.UpdateSequence]). +func (r *Root) UpdateSequence(path *structpath.PathNode, sources [][]int) { + sv := r.vars() + sv.UpdateSequence(path, sources) + r.store(sv) +} + +// GetLocation is [Root.GetLocationOf] for a path string, like main's. func (r Root) GetLocation(path string) diag.Location { - v, err := dyn.Get(r.value, path) - if err != nil { - return diag.Location{} - } - return v.Location() + return firstLocation(r.GetLocations(path)) } -// Get all locations of the configuration value at the specified path. We need both -// this function and it's singular version (GetLocation) because some diagnostics just need -// the primary location and some need all locations associated with a configuration value. +// GetLocations is [Root.GetLocationsOf] for a path string, like main's. func (r Root) GetLocations(path string) []diag.Location { - v, err := dyn.Get(r.value, path) + if r.Loading() { + v, err := dyn.Get(r.value, path) + if err != nil { + return nil + } + return v.Locations() + } + p, err := structpath.ParsePath(path) if err != nil { return nil } - return v.Locations() + return r.GetLocationsOf(p) } -// GetLocationOf is [Root.GetLocation] for a path node. -func (r Root) GetLocationOf(path *structpath.PathNode) diag.Location { - v, ok := r.valueOf(path) - if !ok { - return diag.Location{} +// GetLocationsOf returns the locations of the configuration value at path or, if it +// has none (e.g. it was set by a mutator), those of its closest ancestor that has some. +// Use it to point diagnostics at the most specific place there is; use +// [Root.GetLocationsOfExact] to find where a value is written. +func (r Root) GetLocationsOf(path *structpath.PathNode) []diag.Location { + if r.Loading() { + return r.GetLocationsOfExact(path) } - return v.Location() + return r.locations.Nearest(path) } -// GetLocationsOf is [Root.GetLocations] for a path node. -func (r Root) GetLocationsOf(path *structpath.PathNode) []diag.Location { - v, ok := r.valueOf(path) - if !ok { - return nil +// GetLocationOf returns the primary location of [Root.GetLocationsOf]. +func (r Root) GetLocationOf(path *structpath.PathNode) diag.Location { + return firstLocation(r.GetLocationsOf(path)) +} + +// GetLocationsOfExact returns the locations the configuration value at path is written at, +// or none if it was set by a mutator. +func (r Root) GetLocationsOfExact(path *structpath.PathNode) []diag.Location { + if r.Loading() { + p, ok := dyn.FromStructPath(path) + if !ok { + return nil + } + v, err := dyn.GetByPath(r.value, p) + if err != nil { + return nil + } + return v.Locations() } - return v.Locations() + return r.locations.Exact(path) } -func (r Root) valueOf(path *structpath.PathNode) (dyn.Value, bool) { - p, ok := dyn.FromStructPath(path) - if !ok { - return dyn.InvalidValue, false +// GetLocationOfExact returns the primary location of [Root.GetLocationsOfExact]. +func (r Root) GetLocationOfExact(path *structpath.PathNode) diag.Location { + return firstLocation(r.GetLocationsOfExact(path)) +} + +func firstLocation(locs []diag.Location) diag.Location { + if len(locs) == 0 { + return diag.Location{} } - v, err := dyn.GetByPath(r.value, p) - return v, err == nil + return locs[0] } // GetNodeAndType and returns parent resource node and type of the resource in direct backend. @@ -638,33 +756,33 @@ func (r Root) valueOf(path *structpath.PathNode) (dyn.Value, bool) { // // "resources.jobs.foo.name" -> ("resources.jobs.foo", "jobs") // "resources.jobs.foo.permissions[0].level -> ("resources.jobs.foo.permissions", "jobs.permissions") -func GetNodeAndType(path dyn.Path) (dyn.Path, string) { - if len(path) < 3 { +func GetNodeAndType(path *structpath.PathNode) (*structpath.PathNode, string) { + if path.Len() < 3 { return nil, "" } - if path[0].Key() != "resources" { + if path.KeyAt(0) != "resources" { return nil, "" } - if len(path) >= 4 { - if path[3].Key() == "permissions" || path[3].Key() == "grants" { - return path[:4], path[1].Key() + "." + path[3].Key() + if path.Len() >= 4 { + if k := path.KeyAt(3); k == "permissions" || k == "grants" { + return path.Prefix(4), path.KeyAt(1) + "." + k } } - return path[:3], path[1].Key() + return path.Prefix(3), path.KeyAt(1) } // GetResourceTypeFromKey extracts the resource group from a resource path. // For example, "resources.jobs.foo" returns "jobs". // Returns empty string if the path is not in the expected format. func GetResourceTypeFromKey(path string) string { - dp, err := dyn.NewPathFromString(path) + p, err := structpath.ParsePath(path) if err != nil { return "" } - _, rType := GetNodeAndType(dp) + _, rType := GetNodeAndType(p) return rType } @@ -673,13 +791,13 @@ func GetResourceTypeFromKey(path string) string { // The returned value is a pointer to the concrete struct that represents that resource type. // When the path is invalid or resource is not found, the second return value is false. func (r *Root) GetResourceConfig(path string) (any, error) { - dynPath, err := dyn.NewPathFromString(path) + p, err := structpath.ParsePath(path) if err != nil { return nil, err } // Extract and validate the resource group from the path - node, resourceType := GetNodeAndType(dynPath) + node, resourceType := GetNodeAndType(p) if resourceType == "" { return nil, fmt.Errorf("path does not correspond to resource: %q", path) } @@ -690,18 +808,15 @@ func (r *Root) GetResourceConfig(path string) (any, error) { return nil, fmt.Errorf("no such resource type in the config: %q", resourceType) } - // Fetch the raw value from the dynamic representation of the bundle config. - v, err := dyn.GetByPath(r.Value(), node) - if err != nil { - if dyn.IsNoSuchKeyError(err) { - return nil, nil - } - return nil, fmt.Errorf("cannot fetch config for %s: %w", node, err) + // Copy the value, so that the caller can't change the configuration through it. + v := r.View().Lookup(node) + if !v.IsValid() { + return nil, nil } typedConfigPtr := reflect.New(typ) - err = convert.ToTyped(typedConfigPtr.Interface(), v) + _, err = (&structvar.StructVar{Value: typedConfigPtr.Interface()}).Assign(nil, v) if err != nil { return nil, fmt.Errorf("cannot convert config to %s: %w", typ.String(), err) } @@ -709,8 +824,84 @@ func (r *Root) GetResourceConfig(path string) (any, error) { return typedConfigPtr.Interface(), nil } -// Value returns the dynamic configuration value of the root object. This value -// is the source of truth and is kept in sync with values in the typed configuration. -func (r Root) Value() dyn.Value { - return r.value +// IsReferenceAt is [Root.IsReference] for a path node. +func (r Root) IsReferenceAt(path *structpath.PathNode) bool { + _, ok := r.refs[path.String()] + return ok +} + +// ReferenceAt returns the pure variable reference held by the field at path that +// cannot hold a string, or "" if there is none. +func (r Root) ReferenceAt(path *structpath.PathNode) string { + return r.refs[path.String()] +} + +// References returns all pure variable references in fields that cannot hold a string, +// keyed by path. References in string fields are part of their value. +func (r Root) References() iter.Seq2[*structpath.PathNode, string] { + return r.ReferencesUnder(nil) +} + +// ReferencesUnder returns the pure variable references (see [Root.References]) at path +// and below it, in path order. References below a value that a mutator removed (e.g. a +// cleared new_cluster) are skipped: only those in the typed value are returned. +func (r Root) ReferencesUnder(path *structpath.PathNode) iter.Seq2[*structpath.PathNode, string] { + return func(yield func(*structpath.PathNode, string) bool) { + prefix := path.String() + var keys []string + for k := range r.refs { + if prefix == "" || k == prefix || strings.HasPrefix(k, prefix+".") || strings.HasPrefix(k, prefix+"[") { + keys = append(keys, k) + } + } + slices.Sort(keys) + view := r.View() + for _, k := range keys { + p, err := structpath.ParsePath(k) + if err != nil { + continue + } + // The view shows a present reference as a string. A reference below or at a + // nil pointer (even a forced one), a missing map key or an index out of range + // is gone. + if view.Lookup(p).Kind() != structvar.KindString { + continue + } + if !yield(p, r.refs[k]) { + return + } + } + } +} + +// LocationTree returns the tree of the source locations of the configuration values. +func (r *Root) LocationTree() *structvar.Locations { + return r.locations +} + +// View returns the read-only view of the configuration tree. +func (r *Root) View() structvar.View { + return structvar.NewView(r, r.refs, r.locations) +} + +// Override replaces the configuration with the result of plan (see [structvar.PlanOverride]). +func (r *Root) Override(plan *structvar.OverridePlan) error { + sv := r.vars() + defer r.store(sv) + return sv.Override(plan) +} + +// DecodeLoading converts the value at the path of keys in the configuration tree of a +// loading configuration into dst, a pointer to a typed value; dst is left unchanged +// if there is no value there. +func (r *Root) DecodeLoading(dst any, keys ...string) error { + v := r.value + for _, k := range keys { + v = v.Get(k) + } + if !v.IsValid() { + return nil + } + _, _, err := structvar.DecodeDyn(v, dst) + return err } diff --git a/bundle/config/root_test.go b/bundle/config/root_test.go index f735c44405e..4de19717daf 100644 --- a/bundle/config/root_test.go +++ b/bundle/config/root_test.go @@ -7,6 +7,7 @@ import ( "github.com/databricks/cli/bundle/config/variable" "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -100,8 +101,9 @@ func TestRootMergeTargetOverridesWithMode(t *testing.T) { }, }, } - require.NoError(t, root.initializeDynamicValue()) + startLoadingFromTyped(t, root) require.NoError(t, root.MergeTargetOverrides("development")) + finishLoading(t, root) assert.Equal(t, Development, root.Bundle.Mode) } @@ -156,8 +158,9 @@ func TestRootMergeTargetOverridesWithVariables(t *testing.T) { }, }, } - require.NoError(t, root.initializeDynamicValue()) + startLoadingFromTyped(t, root) require.NoError(t, root.MergeTargetOverrides("development")) + finishLoading(t, root) assert.Equal(t, "bar", root.Variables["foo"].Default) assert.Equal(t, "foo var", root.Variables["foo"].Description) @@ -252,3 +255,72 @@ func TestIsFullVariableOverrideDef(t *testing.T) { assert.Equal(t, tc.expected, isFullVariableOverrideDef(tc.value), "test case %d", i) } } + +func startLoadingFromTyped(t *testing.T, root *Root) { + require.NoError(t, root.StartLoading()) +} + +func finishLoading(t *testing.T, root *Root) { + diags, err := root.FinishLoading() + require.NoError(t, err) + require.Empty(t, diags) +} + +func loadDyn(t *testing.T, path, content string) *Root { + r, diags := LoadDynFromBytes(path, []byte(content)) + require.NoError(t, diags.Error()) + return r +} + +func TestLoadFromBytesNotAMap(t *testing.T) { + for _, content := range []string{"hello\n", "- a\n- b\n"} { + r, diags := LoadFromBytes("databricks.yml", []byte(content)) + assert.Nil(t, r) + assert.ErrorContains(t, diags.Error(), "failed to load databricks.yml") + } +} + +func TestMergeComplexVariableSequenceDefaults(t *testing.T) { + a := loadDyn(t, "a.yml", "variables:\n v:\n type: complex\n default: [1, 2]\n") + b := loadDyn(t, "b.yml", "variables:\n v:\n type: complex\n default: [3]\n") + require.NoError(t, a.Merge(b)) + finishLoading(t, a) + assert.Equal(t, []any{1, 2, 3}, a.Variables["v"].Default) +} + +func TestTargetVariableShorthandThroughAliases(t *testing.T) { + r := loadDyn(t, "a.yml", ` +x: &dev dev_value +y: &common {b: from_anchor, c: from_anchor} +variables: + a: {default: d} + b: {default: d} + c: {default: d} +targets: + dev: + variables: + <<: *common + a: *dev + c: explicit +`) + require.NoError(t, r.MergeTargetOverrides("dev")) + finishLoading(t, r) + assert.Equal(t, "dev_value", r.Variables["a"].Default) + assert.Equal(t, "from_anchor", r.Variables["b"].Default) + assert.Equal(t, "explicit", r.Variables["c"].Default) +} + +func TestSetReferenceAtPointerField(t *testing.T) { + r, diags := LoadFromBytes("a.yml", []byte("resources:\n jobs:\n j:\n name: j\n")) + require.NoError(t, diags.Error()) + path := structpath.MustParsePath("resources.jobs.j.trigger") + require.NoError(t, r.SetReference(path, "${var.t}")) + s, ok := r.View().Lookup(path).AsString() + require.True(t, ok) + assert.Equal(t, "${var.t}", s) +} + +func TestNestedYAMLAnchorCycle(t *testing.T) { + _, diags := LoadFromBytes("a.yml", []byte("variables:\n v:\n default: &x {a: [*x]}\n")) + assert.ErrorContains(t, diags.Error(), `cyclic reference to anchor "x"`) +} diff --git a/bundle/config/validate/all_resources_have_values.go b/bundle/config/validate/all_resources_have_values.go index 713ec0f83af..40158bde431 100644 --- a/bundle/config/validate/all_resources_have_values.go +++ b/bundle/config/validate/all_resources_have_values.go @@ -6,8 +6,9 @@ import ( "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) func AllResourcesHaveValues() bundle.Mutator { @@ -23,34 +24,25 @@ func (m *allResourcesHaveValues) Name() string { func (m *allResourcesHaveValues) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { diags := diag.Diagnostics{} - _, err := dyn.MapByPattern( - b.Config.Value(), - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), - func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - if v.Kind() != dyn.KindNil { - return v, nil - } - - // Type of the resource, stripped of the trailing 's' to make it - // singular. - rType := strings.TrimSuffix(p[1].Key(), "s") - - // Name of the resource. Eg: "foo" in "jobs.foo". - rName := p[2].Key() - - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: fmt.Sprintf("%s %s is not defined", rType, rName), - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), - }) - - return v, nil - }, - ) - if err != nil { - diags = append(diags, diag.FromErr(err)...) - } + b.Config.Resources.IterateAllResources(func(typ, rName string, res config.ConfigResource) bool { + // A resource declared without a body is visited with a nil res. + if res != nil { + return true + } + + // Type of the resource, stripped of the trailing 's' to make it + // singular. + rType := strings.TrimSuffix(typ, "s") + + p := structpath.NewPath(nil, "resources", typ, rName) + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("%s %s is not defined", rType, rName), + Locations: b.Config.GetLocationsOfExact(p), + Paths: []*structpath.PathNode{p}, + }) + return true + }) return diags } diff --git a/bundle/config/validate/enum.go b/bundle/config/validate/enum.go index 8173cc806ec..46364add693 100644 --- a/bundle/config/validate/enum.go +++ b/bundle/config/validate/enum.go @@ -1,10 +1,7 @@ package validate import ( - "cmp" "context" - "fmt" - "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" @@ -20,28 +17,10 @@ func (f *enum) Name() string { return "validate:enum" } -// Apply reports enum fields with invalid values. The walker is generated (enum_walk.go) -// by bundle/internal/validation. +// Apply reports enum fields with invalid values. The walker is generated +// (enum_walk.go) by bundle/internal/validation. func (f *enum) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - w := &walker{diags: diag.Diagnostics{}} - n := rootNode(b.Config.Value()) - enumConfigRoot(w, &n, &b.Config) - diags := w.diags - - // Start from the order of a walk of the configuration tree, so that the sort - // below breaks ties between diagnostics as it did with that walk. - sortByWalkOrder(b.Config.Value(), diags) - - // Sort diagnostics to make them deterministic - slices.SortFunc(diags, func(a, b diag.Diagnostic) int { - // First sort by summary - if n := cmp.Compare(a.Summary, b.Summary); n != 0 { - return n - } - - // Then sort by locations as a tie breaker if summaries are the same. - return cmp.Compare(fmt.Sprintf("%v", a.Locations), fmt.Sprintf("%v", b.Locations)) - }) - + diags := walkConfig(&b.Config, false, enumConfigRoot) + sortDiagnostics(diags) return diags } diff --git a/bundle/config/validate/enum_walk.go b/bundle/config/validate/enum_walk.go index 468e82a49d5..53a5fa51184 100644 --- a/bundle/config/validate/enum_walk.go +++ b/bundle/config/validate/enum_walk.go @@ -1,6 +1,6 @@ // Code generated by bundle/internal/validation. DO NOT EDIT. -// enumConfigRoot reports the enum fields with invalid values. +// enumWalk reports enum fields with invalid values. package validate @@ -8,6 +8,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/config/variable" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/databricks-sdk-go/service/apps" "github.com/databricks/databricks-sdk-go/service/catalog" "github.com/databricks/databricks-sdk-go/service/compute" @@ -21,2855 +22,3467 @@ import ( "github.com/databricks/databricks-sdk-go/service/vectorsearch" ) -var enumValuesAppsAppDeploymentMode = []string{"AUTO_SYNC", "SNAPSHOT"} +var enumValues0 = []string{"bash", "sh", "cmd"} -var enumValuesAppsAppDeploymentState = []string{"CANCELLED", "FAILED", "IN_PROGRESS", "SUCCEEDED"} +var enumValues1 = []string{"whl", "jar", "tgz"} -var enumValuesAppsAppPermissionLevel = []string{"CAN_MANAGE", "CAN_USE"} +var enumValues10 = []string{"CLASSIC_PREVIEW"} -var enumValuesAppsAppResourceAppAppPermission = []string{"CAN_USE"} +var enumValues100 = []string{"RATE_LIMIT_KEY_SERVICE", "RATE_LIMIT_KEY_SERVICE_PRINCIPAL", "RATE_LIMIT_KEY_USER", "RATE_LIMIT_KEY_USER_DEFAULT", "RATE_LIMIT_KEY_USER_GROUP"} -var enumValuesAppsAppResourceDatabaseDatabasePermission = []string{"CAN_CONNECT_AND_CREATE"} +var enumValues101 = []string{"RATE_LIMIT_RENEWAL_PERIOD_HOUR", "RATE_LIMIT_RENEWAL_PERIOD_MINUTE"} -var enumValuesAppsAppResourceExperimentExperimentPermission = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_READ"} +var enumValues102 = []string{"endpoint", "service_principal", "user", "user_group"} -var enumValuesAppsAppResourceGenieSpaceGenieSpacePermission = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_RUN", "CAN_VIEW"} +var enumValues103 = []string{"CPU", "CPU_LARGE", "CPU_MEDIUM", "GPU_LARGE", "GPU_MEDIUM", "GPU_SMALL", "GPU_XLARGE", "GPU_XLARGE_8", "MULTIGPU_MEDIUM"} -var enumValuesAppsAppResourceJobJobPermission = []string{"CAN_MANAGE", "CAN_MANAGE_RUN", "CAN_VIEW", "IS_OWNER"} +var enumValues104 = []string{"ENHANCED", "LEGACY"} -var enumValuesAppsAppResourcePostgresPostgresPermission = []string{"CAN_CONNECT_AND_CREATE"} +var enumValues105 = []string{"APPEND_ONLY", "SCD_TYPE_1", "SCD_TYPE_2"} -var enumValuesAppsAppResourceSecretSecretPermission = []string{"MANAGE", "READ", "WRITE"} +var enumValues106 = []string{"OAUTH", "PAT"} -var enumValuesAppsAppResourceServingEndpointServingEndpointPermission = []string{"CAN_MANAGE", "CAN_QUERY", "CAN_VIEW"} +var enumValues107 = []string{"DIRECT_QUERY", "DUAL", "IMPORT"} -var enumValuesAppsAppResourceSqlWarehouseSqlWarehousePermission = []string{"CAN_MANAGE", "CAN_USE", "IS_OWNER"} +var enumValues108 = []string{"DESTINATION_TYPE_EXTERNAL_FOUNDATION_MODEL", "DESTINATION_TYPE_PAY_PER_TOKEN_FOUNDATION_MODEL", "DESTINATION_TYPE_PROVISIONED_THROUGHPUT_FOUNDATION_MODEL"} -var enumValuesAppsAppResourceUcSecurableUcSecurablePermission = []string{"EXECUTE", "MODIFY", "READ_VOLUME", "SELECT", "USE_CONNECTION", "WRITE_VOLUME"} +var enumValues109 = []string{"ai21labs", "amazon-bedrock", "anthropic", "cohere", "custom", "databricks-model-serving", "google-cloud-vertex-ai", "openai", "palm"} -var enumValuesAppsAppResourceUcSecurableUcSecurableType = []string{"CONNECTION", "FUNCTION", "MODEL_SERVICE", "TABLE", "VOLUME"} +var enumValues11 = []string{"NULL", "PHOTON", "STANDARD"} -var enumValuesAppsApplicationState = []string{"CRASHED", "DEPLOYING", "RUNNING", "UNAVAILABLE"} +var enumValues110 = []string{"PROVISIONING_PHASE_INDEX_SCAN", "PROVISIONING_PHASE_INDEX_SORT", "PROVISIONING_PHASE_MAIN"} -var enumValuesAppsComputeSize = []string{"LARGE", "MEDIUM", "XLARGE"} +var enumValues111 = []string{"BINARY", "BOOLEAN", "DATE", "DECIMAL", "DOUBLE", "FLOAT", "INTEGER", "LONG", "SHORT", "STRING", "TIMESTAMP"} -var enumValuesAppsComputeState = []string{"ACTIVE", "DELETING", "ERROR", "STARTING", "STOPPED", "STOPPING", "UPDATING"} +var enumValues112 = []string{"BLOCK", "MASK", "NONE"} -var enumValuesCatalogModelProviderServiceConfigExternalModelProviderType = []string{"EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK", "EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC", "EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI", "EXTERNAL_MODEL_PROVIDER_TYPE_BEDROCK_MANTLE", "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", "EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE", "EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY", "EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI"} +var enumValues113 = []string{"ai21labs", "amazon", "anthropic", "cohere"} -var enumValuesCatalogModelServiceConfigDestinationConfigDestinationType = []string{"DESTINATION_TYPE_EXTERNAL_FOUNDATION_MODEL", "DESTINATION_TYPE_PAY_PER_TOKEN_FOUNDATION_MODEL", "DESTINATION_TYPE_PROVISIONED_THROUGHPUT_FOUNDATION_MODEL"} +var enumValues114 = []string{"FILE_FORMAT_CSV", "FILE_FORMAT_EXCEL", "FILE_FORMAT_TSV"} -var enumValuesCatalogMonitorCronSchedulePauseStatus = []string{"PAUSED", "UNPAUSED", "UNSPECIFIED"} +var enumValues115 = []string{"PAGE_SELECTION_ALL", "PAGE_SELECTION_SUBSET"} -var enumValuesCatalogMonitorInferenceLogProblemType = []string{"PROBLEM_TYPE_CLASSIFICATION", "PROBLEM_TYPE_REGRESSION"} +var enumValues116 = []string{"FILE", "FILE_METADATA", "PERMISSION"} -var enumValuesCatalogMonitorMetricType = []string{"CUSTOM_METRIC_TYPE_AGGREGATE", "CUSTOM_METRIC_TYPE_DERIVED", "CUSTOM_METRIC_TYPE_DRIFT"} +var enumValues117 = []string{"ALL", "INLINE_ONLY", "NONE", "NON_INLINE_ONLY"} -var enumValuesCatalogPrivilege = []string{"ACCESS", "ALL_PRIVILEGES", "APPLY_TAG", "BROWSE", "CREATE", "CREATE_CATALOG", "CREATE_CLEAN_ROOM", "CREATE_CONNECTION", "CREATE_EXTERNAL_LOCATION", "CREATE_EXTERNAL_TABLE", "CREATE_EXTERNAL_VOLUME", "CREATE_FOREIGN_CATALOG", "CREATE_FOREIGN_SECURABLE", "CREATE_FUNCTION", "CREATE_MANAGED_STORAGE", "CREATE_MATERIALIZED_VIEW", "CREATE_MODEL", "CREATE_PROVIDER", "CREATE_RECIPIENT", "CREATE_SCHEMA", "CREATE_SERVICE_CREDENTIAL", "CREATE_SHARE", "CREATE_STORAGE_CREDENTIAL", "CREATE_TABLE", "CREATE_VIEW", "CREATE_VOLUME", "EXECUTE", "EXECUTE_CLEAN_ROOM_TASK", "EXTERNAL_USE_LOCATION", "EXTERNAL_USE_SCHEMA", "MANAGE", "MANAGE_ALLOWLIST", "MODIFY", "MODIFY_CLEAN_ROOM", "READ_FILES", "READ_METADATA", "READ_PRIVATE_FILES", "READ_VOLUME", "REFRESH", "SELECT", "SET_SHARE_PERMISSION", "USAGE", "USE_CATALOG", "USE_CONNECTION", "USE_MARKETPLACE_ASSETS", "USE_PROVIDER", "USE_RECIPIENT", "USE_SCHEMA", "USE_SHARE", "WRITE_FILES", "WRITE_PRIVATE_FILES", "WRITE_VOLUME"} +var enumValues118 = []string{"TEXT_HTML", "TEXT_PLAIN"} -var enumValuesCatalogRateLimitRateLimitKey = []string{"RATE_LIMIT_KEY_SERVICE", "RATE_LIMIT_KEY_SERVICE_PRINCIPAL", "RATE_LIMIT_KEY_USER", "RATE_LIMIT_KEY_USER_DEFAULT", "RATE_LIMIT_KEY_USER_GROUP"} +var enumValues119 = []string{"FILE", "FILE_METADATA", "LIST", "PERMISSION"} -var enumValuesCatalogRateLimitRateLimitRenewalPeriod = []string{"RATE_LIMIT_RENEWAL_PERIOD_HOUR", "RATE_LIMIT_RENEWAL_PERIOD_MINUTE"} +var enumValues12 = []string{"ACTIVE", "TRASHED"} -var enumValuesCatalogSseEncryptionDetailsAlgorithm = []string{"AWS_SSE_KMS", "AWS_SSE_S3"} +var enumValues120 = []string{"AUCTION_AD", "AUCTION_ADGROUP", "AUCTION_ADVERTISER", "AUCTION_CAMPAIGN"} -var enumValuesCatalogVolumeType = []string{"EXTERNAL", "MANAGED"} +var enumValues121 = []string{"AUDIENCE", "BASIC", "BUSINESS_CENTER", "DSA", "GMV_MAX", "PLAYABLE_AD"} -var enumValuesComputeAwsAvailability = []string{"ON_DEMAND", "SPOT", "SPOT_WITH_FALLBACK"} +var enumValues122 = []string{"AVRO", "JSON", "PROTOBUF", "STRING"} -var enumValuesComputeAzureAvailability = []string{"ON_DEMAND_AZURE", "SPOT_AZURE", "SPOT_WITH_FALLBACK_AZURE"} +var enumValues123 = []string{"AVRO", "BINARYFILE", "CSV", "EXCEL", "JSON", "ORC", "PARQUET", "XML"} -var enumValuesComputeClusterPermissionLevel = []string{"CAN_ATTACH_TO", "CAN_MANAGE", "CAN_RESTART"} +var enumValues124 = []string{"ADD_NEW_COLUMNS", "ADD_NEW_COLUMNS_WITH_TYPE_WIDENING", "FAIL_ON_NEW_COLUMNS", "NONE", "RESCUE"} -var enumValuesComputeClusterPolicyPermissionLevel = []string{"CAN_USE"} +var enumValues125 = []string{"CAMPAIGN", "CAMPAIGN_GROUP", "CREATIVE"} -var enumValuesComputeConfidentialComputeType = []string{"CONFIDENTIAL_COMPUTE_TYPE_NONE", "SEV_SNP"} +var enumValues126 = []string{"ANALYTICS", "ATTRIBUTED_REVENUE_METRICS", "STATISTICS"} -var enumValuesComputeDataSecurityMode = []string{"DATA_SECURITY_MODE_AUTO", "DATA_SECURITY_MODE_DEDICATED", "DATA_SECURITY_MODE_STANDARD", "LEGACY_PASSTHROUGH", "LEGACY_SINGLE_USER", "LEGACY_SINGLE_USER_STANDARD", "LEGACY_TABLE_ACL", "NONE", "SINGLE_USER", "USER_ISOLATION"} +var enumValues127 = []string{"ALL", "DAILY", "MONTHLY", "YEARLY"} -var enumValuesComputeDependencyMode = []string{"DEPENDENCY_MODE_AUTO", "DEPENDENCY_MODE_CLUSTER_LIBRARIES", "DEPENDENCY_MODE_ENVIRONMENTS"} +var enumValues128 = []string{"FAILFAST", "PERMISSIVE"} -var enumValuesComputeDiskTypeAzureDiskVolumeType = []string{"PREMIUM_LRS", "STANDARD_LRS"} +var enumValues13 = []string{"AVAILABLE", "DELETING", "FAILING_OVER", "STARTING", "STOPPED", "UPDATING"} -var enumValuesComputeDiskTypeEbsVolumeType = []string{"GENERAL_PURPOSE_SSD", "THROUGHPUT_OPTIMIZED_HDD"} +var enumValues14 = []string{"PERFORMANCE_OPTIMIZED", "STANDARD"} -var enumValuesComputeEbsVolumeType = []string{"GENERAL_PURPOSE_SSD", "THROUGHPUT_OPTIMIZED_HDD"} +var enumValues15 = []string{"EDITABLE", "UI_LOCKED"} -var enumValuesComputeGcpAvailability = []string{"ON_DEMAND_GCP", "PREEMPTIBLE_GCP", "PREEMPTIBLE_WITH_FALLBACK_GCP"} +var enumValues16 = []string{"MULTI_TASK", "SINGLE_TASK"} -var enumValuesComputeHardwareAcceleratorType = []string{"GPU_1xA10", "GPU_1xH100", "GPU_8xB300", "GPU_8xH100"} +var enumValues17 = []string{"ENDPOINT_TYPE_READ_ONLY", "ENDPOINT_TYPE_READ_WRITE"} -var enumValuesComputeInstancePoolAwsAttributesAvailability = []string{"ON_DEMAND", "SPOT"} +var enumValues18 = []string{"LAKEBASE_OAUTH_V1", "NO_LOGIN", "PG_PASSWORD_SCRAM_SHA_256"} -var enumValuesComputeInstancePoolAzureAttributesAvailability = []string{"ON_DEMAND_AZURE", "SPOT_AZURE"} +var enumValues19 = []string{"GROUP", "SERVICE_PRINCIPAL", "USER"} -var enumValuesComputeInstancePoolPermissionLevel = []string{"CAN_ATTACH_TO", "CAN_MANAGE"} +var enumValues2 = []string{"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_CREATE_APP", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"} -var enumValuesComputeKind = []string{"CLASSIC_PREVIEW"} +var enumValues20 = []string{"DATABRICKS_SUPERUSER"} -var enumValuesComputeRuntimeEngine = []string{"NULL", "PHOTON", "STANDARD"} +var enumValues21 = []string{"CONTINUOUS", "SNAPSHOT", "TRIGGERED"} -var enumValuesConfigArtifactType = []string{"whl", "jar", "tgz"} +var enumValues22 = []string{"AZURE_KEYVAULT", "DATABRICKS"} -var enumValuesConfigSyncMode = []string{"overwrite", "in-place", "immutable"} +var enumValues23 = []string{"COST_OPTIMIZED", "POLICY_UNSPECIFIED", "RELIABILITY_OPTIMIZED"} -var enumValuesDashboardsLifecycleState = []string{"ACTIVE", "TRASHED"} +var enumValues24 = []string{"CLASSIC", "PRO", "REALTIME", "TYPE_UNSPECIFIED"} -var enumValuesDatabaseDatabaseInstanceState = []string{"AVAILABLE", "DELETING", "FAILING_OVER", "STARTING", "STOPPED", "UPDATING"} +var enumValues25 = []string{"ACTIVE", "DEGRADED", "DELETING", "FAILED", "PROVISIONING", "UPDATING"} -var enumValuesDatabaseProvisioningInfoState = []string{"ACTIVE", "DEGRADED", "DELETING", "FAILED", "PROVISIONING", "UPDATING"} +var enumValues26 = []string{"STANDARD", "STORAGE_OPTIMIZED"} -var enumValuesDatabaseProvisioningPhase = []string{"PROVISIONING_PHASE_INDEX_SCAN", "PROVISIONING_PHASE_INDEX_SORT", "PROVISIONING_PHASE_MAIN"} +var enumValues27 = []string{"FULL_TEXT", "HYBRID", "VECTOR"} -var enumValuesDatabaseSyncedTableSchedulingPolicy = []string{"CONTINUOUS", "SNAPSHOT", "TRIGGERED"} +var enumValues28 = []string{"DELTA_SYNC", "DIRECT_ACCESS"} -var enumValuesDatabaseSyncedTableSpecPgSpecificType = []string{"PG_SPECIFIC_TYPE_HALFVEC", "PG_SPECIFIC_TYPE_VARCHAR", "PG_SPECIFIC_TYPE_VECTOR"} +var enumValues29 = []string{"EXTERNAL", "MANAGED"} -var enumValuesDatabaseSyncedTableState = []string{"SYNCED_TABLED_OFFLINE", "SYNCED_TABLE_OFFLINE_FAILED", "SYNCED_TABLE_ONLINE", "SYNCED_TABLE_ONLINE_CONTINUOUS_UPDATE", "SYNCED_TABLE_ONLINE_NO_PENDING_UPDATE", "SYNCED_TABLE_ONLINE_PIPELINE_FAILED", "SYNCED_TABLE_ONLINE_TRIGGERED_UPDATE", "SYNCED_TABLE_ONLINE_UPDATING_PIPELINE_RESOURCES", "SYNCED_TABLE_PROVISIONING", "SYNCED_TABLE_PROVISIONING_INITIAL_SNAPSHOT", "SYNCED_TABLE_PROVISIONING_PIPELINE_RESOURCES"} +var enumValues3 = []string{"complex"} -var enumValuesExecExecutableType = []string{"bash", "sh", "cmd"} +var enumValues30 = []string{"EQUAL", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "IS_NOT_NULL", "IS_NULL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "NOT_EQUAL"} -var enumValuesIamPermissionLevel = []string{"CAN_ATTACH_TO", "CAN_BIND", "CAN_CREATE", "CAN_CREATE_APP", "CAN_EDIT", "CAN_EDIT_METADATA", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_RUN", "CAN_MANAGE_STAGING_VERSIONS", "CAN_MONITOR", "CAN_MONITOR_ONLY", "CAN_QUERY", "CAN_READ", "CAN_RESTART", "CAN_RUN", "CAN_USE", "CAN_VIEW", "CAN_VIEW_METADATA", "IS_OWNER"} +var enumValues31 = []string{"ERROR", "OK", "TRIGGERED", "UNKNOWN"} -var enumValuesJobsAiRuntimeTaskPriorityClass = []string{"BEST_EFFORT", "CRITICAL", "NORMAL"} +var enumValues32 = []string{"PAUSED", "UNPAUSED"} -var enumValuesJobsAuthenticationMethod = []string{"OAUTH", "PAT"} +var enumValues33 = []string{"AUTO_SYNC", "SNAPSHOT"} -var enumValuesJobsComputeSpecAcceleratorType = []string{"GPU_1xA10", "GPU_1xH100", "GPU_8xB300", "GPU_8xH100"} +var enumValues34 = []string{"CRASHED", "DEPLOYING", "RUNNING", "UNAVAILABLE"} -var enumValuesJobsCondition = []string{"ALL_UPDATED", "ANY_UPDATED"} +var enumValues35 = []string{"ACTIVE", "DELETING", "ERROR", "STARTING", "STOPPED", "STOPPING", "UPDATING"} -var enumValuesJobsConditionTaskOp = []string{"EQUAL_TO", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "NOT_EQUAL"} +var enumValues36 = []string{"CAN_MANAGE", "CAN_USE"} -var enumValuesJobsDayOfWeek = []string{"FRIDAY", "MONDAY", "SATURDAY", "SUNDAY", "THURSDAY", "TUESDAY", "WEDNESDAY"} +var enumValues37 = []string{"ACCESS", "ALL_PRIVILEGES", "APPLY_TAG", "BROWSE", "CREATE", "CREATE_CATALOG", "CREATE_CLEAN_ROOM", "CREATE_CONNECTION", "CREATE_EXTERNAL_LOCATION", "CREATE_EXTERNAL_TABLE", "CREATE_EXTERNAL_VOLUME", "CREATE_FOREIGN_CATALOG", "CREATE_FOREIGN_SECURABLE", "CREATE_FUNCTION", "CREATE_MANAGED_STORAGE", "CREATE_MATERIALIZED_VIEW", "CREATE_MODEL", "CREATE_PROVIDER", "CREATE_RECIPIENT", "CREATE_SCHEMA", "CREATE_SERVICE_CREDENTIAL", "CREATE_SHARE", "CREATE_STORAGE_CREDENTIAL", "CREATE_TABLE", "CREATE_VIEW", "CREATE_VOLUME", "EXECUTE", "EXECUTE_CLEAN_ROOM_TASK", "EXTERNAL_USE_LOCATION", "EXTERNAL_USE_SCHEMA", "MANAGE", "MANAGE_ALLOWLIST", "MODIFY", "MODIFY_CLEAN_ROOM", "READ_FILES", "READ_METADATA", "READ_PRIVATE_FILES", "READ_VOLUME", "REFRESH", "SELECT", "SET_SHARE_PERMISSION", "USAGE", "USE_CATALOG", "USE_CONNECTION", "USE_MARKETPLACE_ASSETS", "USE_PROVIDER", "USE_RECIPIENT", "USE_SCHEMA", "USE_SHARE", "WRITE_FILES", "WRITE_PRIVATE_FILES", "WRITE_VOLUME"} -var enumValuesJobsFormat = []string{"MULTI_TASK", "SINGLE_TASK"} +var enumValues38 = []string{"CAN_USE"} -var enumValuesJobsGitProvider = []string{"awsCodeCommit", "azureDevOpsServices", "bitbucketCloud", "bitbucketServer", "gitHub", "gitHubEnterprise", "gitLab", "gitLabEnterpriseEdition"} +var enumValues39 = []string{"ON_DEMAND", "SPOT", "SPOT_WITH_FALLBACK"} -var enumValuesJobsJobDeploymentKind = []string{"BUNDLE", "SYSTEM_MANAGED"} +var enumValues4 = []string{"overwrite", "in-place", "immutable"} -var enumValuesJobsJobEditMode = []string{"EDITABLE", "UI_LOCKED"} +var enumValues40 = []string{"GENERAL_PURPOSE_SSD", "THROUGHPUT_OPTIMIZED_HDD"} -var enumValuesJobsJobPermissionLevel = []string{"CAN_MANAGE", "CAN_MANAGE_RUN", "CAN_VIEW", "IS_OWNER"} +var enumValues41 = []string{"ON_DEMAND_AZURE", "SPOT_AZURE", "SPOT_WITH_FALLBACK_AZURE"} -var enumValuesJobsJobSourceDirtyState = []string{"DISCONNECTED", "NOT_SYNCED"} +var enumValues42 = []string{"ON_DEMAND_GCP", "PREEMPTIBLE_GCP", "PREEMPTIBLE_WITH_FALLBACK_GCP"} -var enumValuesJobsJobsHealthMetric = []string{"RUN_DURATION_SECONDS", "STREAMING_BACKLOG_BYTES", "STREAMING_BACKLOG_FILES", "STREAMING_BACKLOG_RECORDS", "STREAMING_BACKLOG_SECONDS"} +var enumValues43 = []string{"CONFIDENTIAL_COMPUTE_TYPE_NONE", "SEV_SNP"} -var enumValuesJobsJobsHealthOperator = []string{"GREATER_THAN"} +var enumValues44 = []string{"CAN_ATTACH_TO", "CAN_MANAGE", "CAN_RESTART"} -var enumValuesJobsModelTriggerConfigurationCondition = []string{"MODEL_ALIAS_SET", "MODEL_CREATED", "MODEL_VERSION_READY"} +var enumValues45 = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_READ"} -var enumValuesJobsPauseStatus = []string{"PAUSED", "UNPAUSED"} +var enumValues46 = []string{"APPROX_COUNT_DISTINCT", "APPROX_PERCENTILE", "AVG", "COUNT", "FIRST", "FUNCTION_TYPE_UNSPECIFIED", "LAST", "MAX", "MIN", "STDDEV_POP", "STDDEV_SAMP", "SUM", "VAR_POP", "VAR_SAMP"} -var enumValuesJobsPerformanceTarget = []string{"PERFORMANCE_OPTIMIZED", "STANDARD"} +var enumValues47 = []string{"ON_DEMAND", "SPOT"} -var enumValuesJobsPeriodicTriggerConfigurationTimeUnit = []string{"DAYS", "HOURS", "MINUTES", "WEEKS"} +var enumValues48 = []string{"ON_DEMAND_AZURE", "SPOT_AZURE"} -var enumValuesJobsRunIf = []string{"ALL_DONE", "ALL_FAILED", "ALL_SUCCESS", "AT_LEAST_ONE_FAILED", "AT_LEAST_ONE_SUCCESS", "NONE_FAILED"} +var enumValues49 = []string{"CAN_ATTACH_TO", "CAN_MANAGE"} -var enumValuesJobsSource = []string{"GIT", "WORKSPACE"} +var enumValues5 = []string{"HTML", "MARKDOWN"} -var enumValuesJobsSqlConditionTriggerMode = []string{"QUERY_RETURNS_ROWS", "RESULT_VALUE_CHANGES"} +var enumValues50 = []string{"NEVER", "ON_FAILURE"} -var enumValuesJobsStorageMode = []string{"DIRECT_QUERY", "DUAL", "IMPORT"} +var enumValues51 = []string{"BUNDLE", "SYSTEM_MANAGED"} -var enumValuesJobsSubscriptionAttachmentDataAttachmentFileFormat = []string{"FILE_FORMAT_CSV", "FILE_FORMAT_EXCEL", "FILE_FORMAT_TSV"} +var enumValues52 = []string{"awsCodeCommit", "azureDevOpsServices", "bitbucketCloud", "bitbucketServer", "gitHub", "gitHubEnterprise", "gitLab", "gitLabEnterpriseEdition"} -var enumValuesJobsSubscriptionAttachmentPageAttachmentPageSelection = []string{"PAGE_SELECTION_ALL", "PAGE_SELECTION_SUBSET"} +var enumValues53 = []string{"CAN_MANAGE", "CAN_MANAGE_RUN", "CAN_VIEW", "IS_OWNER"} -var enumValuesJobsTaskRetryMode = []string{"NEVER", "ON_FAILURE"} +var enumValues54 = []string{"ALL_DONE", "ALL_FAILED", "ALL_SUCCESS", "AT_LEAST_ONE_FAILED", "AT_LEAST_ONE_SUCCESS", "NONE_FAILED"} -var enumValuesMlExperimentPermissionLevel = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_READ"} +var enumValues55 = []string{"EXTERNAL_MODEL_PROVIDER_TYPE_AMAZON_BEDROCK", "EXTERNAL_MODEL_PROVIDER_TYPE_ANTHROPIC", "EXTERNAL_MODEL_PROVIDER_TYPE_AZURE_OPENAI", "EXTERNAL_MODEL_PROVIDER_TYPE_BEDROCK_MANTLE", "EXTERNAL_MODEL_PROVIDER_TYPE_CUSTOM", "EXTERNAL_MODEL_PROVIDER_TYPE_GEMINI_ENTERPRISE", "EXTERNAL_MODEL_PROVIDER_TYPE_MICROSOFT_FOUNDRY", "EXTERNAL_MODEL_PROVIDER_TYPE_OPENAI"} -var enumValuesMlFunctionFunctionType = []string{"APPROX_COUNT_DISTINCT", "APPROX_PERCENTILE", "AVG", "COUNT", "FIRST", "FUNCTION_TYPE_UNSPECIFIED", "LAST", "MAX", "MIN", "STDDEV_POP", "STDDEV_SAMP", "SUM", "VAR_POP", "VAR_SAMP"} +var enumValues56 = []string{"CAN_MANAGE", "CAN_QUERY", "CAN_VIEW"} -var enumValuesMlRegisteredModelPermissionLevel = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_STAGING_VERSIONS", "CAN_READ"} +var enumValues57 = []string{"endpoint", "user"} -var enumValuesMlScalarDataType = []string{"BINARY", "BOOLEAN", "DATE", "DECIMAL", "DOUBLE", "FLOAT", "INTEGER", "LONG", "SHORT", "STRING", "TIMESTAMP"} +var enumValues58 = []string{"minute"} -var enumValuesMlSdpSourceState = []string{"ERROR", "PENDING", "RESOLVED"} +var enumValues59 = []string{"TELEMETRY_FEATURE_INFERENCE_TABLE", "TELEMETRY_FEATURE_LOGS", "TELEMETRY_FEATURE_METRICS", "TELEMETRY_FEATURE_TRACES"} -var enumValuesPipelinesConnectorType = []string{"CDC", "QUERY_BASED"} +var enumValues6 = []string{"ACTIVE", "DELETED"} -var enumValuesPipelinesDayOfWeek = []string{"FRIDAY", "MONDAY", "SATURDAY", "SUNDAY", "THURSDAY", "TUESDAY", "WEDNESDAY"} +var enumValues60 = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_MANAGE_PRODUCTION_VERSIONS", "CAN_MANAGE_STAGING_VERSIONS", "CAN_READ"} -var enumValuesPipelinesDeploymentKind = []string{"BUNDLE"} +var enumValues61 = []string{"BUNDLE"} -var enumValuesPipelinesFileIngestionOptionsFileFormat = []string{"AVRO", "BINARYFILE", "CSV", "EXCEL", "JSON", "ORC", "PARQUET", "XML"} +var enumValues62 = []string{"CDC", "QUERY_BASED"} -var enumValuesPipelinesFileIngestionOptionsSchemaEvolutionMode = []string{"ADD_NEW_COLUMNS", "ADD_NEW_COLUMNS_WITH_TYPE_WIDENING", "FAIL_ON_NEW_COLUMNS", "NONE", "RESCUE"} +var enumValues63 = []string{"BIGQUERY", "CONFLUENCE", "DYNAMICS365", "FOREIGN_CATALOG", "GA4_RAW_DATA", "GOOGLE_DRIVE", "JIRA", "MANAGED_POSTGRESQL", "META_MARKETING", "MYSQL", "NETSUITE", "ORACLE", "POSTGRESQL", "RABBITMQ", "SALESFORCE", "SERVICENOW", "SHAREPOINT", "SMARTSHEET", "SQLSERVER", "TERADATA", "TIKTOK_ADS", "WORKDAY_RAAS", "ZENDESK"} -var enumValuesPipelinesGoogleDriveOptionsGoogleDriveEntityType = []string{"FILE", "FILE_METADATA", "PERMISSION"} +var enumValues64 = []string{"CAN_MANAGE", "CAN_RUN", "CAN_VIEW", "IS_OWNER"} -var enumValuesPipelinesIngestionSourceType = []string{"BIGQUERY", "CONFLUENCE", "DYNAMICS365", "FOREIGN_CATALOG", "GA4_RAW_DATA", "GOOGLE_DRIVE", "JIRA", "MANAGED_POSTGRESQL", "META_MARKETING", "MYSQL", "NETSUITE", "ORACLE", "POSTGRESQL", "RABBITMQ", "SALESFORCE", "SERVICENOW", "SHAREPOINT", "SMARTSHEET", "SQLSERVER", "TERADATA", "TIKTOK_ADS", "WORKDAY_RAAS", "ZENDESK"} +var enumValues65 = []string{"FRIDAY", "MONDAY", "SATURDAY", "SUNDAY", "THURSDAY", "TUESDAY", "WEDNESDAY"} -var enumValuesPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsEntityGranularity = []string{"CAMPAIGN", "CAMPAIGN_GROUP", "CREATIVE"} +var enumValues66 = []string{"STORED_GENERATED"} -var enumValuesPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsFinder = []string{"ANALYTICS", "ATTRIBUTED_REVENUE_METRICS", "STATISTICS"} +var enumValues67 = []string{"CURRENT", "PREVIEW"} -var enumValuesPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsTimeGranularity = []string{"ALL", "DAILY", "MONTHLY", "YEARLY"} +var enumValues68 = []string{"PG_SPECIFIC_TYPE_HALFVEC", "PG_SPECIFIC_TYPE_VARCHAR", "PG_SPECIFIC_TYPE_VECTOR"} -var enumValuesPipelinesOutlookAttachmentMode = []string{"ALL", "INLINE_ONLY", "NONE", "NON_INLINE_ONLY"} +var enumValues69 = []string{"CUSTOM_METRIC_TYPE_AGGREGATE", "CUSTOM_METRIC_TYPE_DERIVED", "CUSTOM_METRIC_TYPE_DRIFT"} -var enumValuesPipelinesOutlookBodyFormat = []string{"TEXT_HTML", "TEXT_PLAIN"} +var enumValues7 = []string{"LARGE", "MEDIUM", "XLARGE"} -var enumValuesPipelinesParseMode = []string{"FAILFAST", "PERMISSIVE"} +var enumValues70 = []string{"PROBLEM_TYPE_CLASSIFICATION", "PROBLEM_TYPE_REGRESSION"} -var enumValuesPipelinesPipelineClusterAutoscaleMode = []string{"ENHANCED", "LEGACY"} +var enumValues71 = []string{"PAUSED", "UNPAUSED", "UNSPECIFIED"} -var enumValuesPipelinesPipelinePermissionLevel = []string{"CAN_MANAGE", "CAN_RUN", "CAN_VIEW", "IS_OWNER"} +var enumValues72 = []string{"READ", "WRITE", "MANAGE"} -var enumValuesPipelinesSharepointOptionsSharepointEntityType = []string{"FILE", "FILE_METADATA", "LIST", "PERMISSION"} +var enumValues73 = []string{"CHANNEL_NAME_CURRENT", "CHANNEL_NAME_CUSTOM", "CHANNEL_NAME_PREVIEW", "CHANNEL_NAME_PREVIOUS"} -var enumValuesPipelinesTableSpecificConfigScdType = []string{"APPEND_ONLY", "SCD_TYPE_1", "SCD_TYPE_2"} +var enumValues74 = []string{"CAN_MANAGE", "CAN_MONITOR", "CAN_USE", "CAN_VIEW", "IS_OWNER"} -var enumValuesPipelinesTikTokAdsOptionsTikTokDataLevel = []string{"AUCTION_AD", "AUCTION_ADGROUP", "AUCTION_ADVERTISER", "AUCTION_CAMPAIGN"} +var enumValues75 = []string{"SYNCED_TABLED_OFFLINE", "SYNCED_TABLE_OFFLINE_FAILED", "SYNCED_TABLE_ONLINE", "SYNCED_TABLE_ONLINE_CONTINUOUS_UPDATE", "SYNCED_TABLE_ONLINE_NO_PENDING_UPDATE", "SYNCED_TABLE_ONLINE_PIPELINE_FAILED", "SYNCED_TABLE_ONLINE_TRIGGERED_UPDATE", "SYNCED_TABLE_ONLINE_UPDATING_PIPELINE_RESOURCES", "SYNCED_TABLE_PROVISIONING", "SYNCED_TABLE_PROVISIONING_INITIAL_SNAPSHOT", "SYNCED_TABLE_PROVISIONING_PIPELINE_RESOURCES"} -var enumValuesPipelinesTikTokAdsOptionsTikTokReportType = []string{"AUDIENCE", "BASIC", "BUSINESS_CENTER", "DSA", "GMV_MAX", "PLAYABLE_AD"} +var enumValues76 = []string{"CAN_CREATE", "CAN_MANAGE", "CAN_USE"} -var enumValuesPipelinesTransformerFormat = []string{"AVRO", "JSON", "PROTOBUF", "STRING"} +var enumValues77 = []string{"CONTINUOUS", "TRIGGERED"} -var enumValuesPostgresDayOfWeek = []string{"FRIDAY", "MONDAY", "SATURDAY", "SUNDAY", "THURSDAY", "TUESDAY", "WEDNESDAY"} +var enumValues78 = []string{"AVG", "COUNT", "COUNT_DISTINCT", "MAX", "MEDIAN", "MIN", "STDDEV", "SUM"} -var enumValuesPostgresEndpointType = []string{"ENDPOINT_TYPE_READ_ONLY", "ENDPOINT_TYPE_READ_WRITE"} +var enumValues79 = []string{"CANCELLED", "FAILED", "IN_PROGRESS", "SUCCEEDED"} -var enumValuesPostgresNewPipelineSpecPipelineChannel = []string{"CURRENT", "PREVIEW"} +var enumValues8 = []string{"DATA_SECURITY_MODE_AUTO", "DATA_SECURITY_MODE_DEDICATED", "DATA_SECURITY_MODE_STANDARD", "LEGACY_PASSTHROUGH", "LEGACY_SINGLE_USER", "LEGACY_SINGLE_USER_STANDARD", "LEGACY_TABLE_ACL", "NONE", "SINGLE_USER", "USER_ISOLATION"} -var enumValuesPostgresRoleAuthMethod = []string{"LAKEBASE_OAUTH_V1", "NO_LOGIN", "PG_PASSWORD_SCRAM_SHA_256"} +var enumValues80 = []string{"CAN_CONNECT_AND_CREATE"} -var enumValuesPostgresRoleIdentityType = []string{"GROUP", "SERVICE_PRINCIPAL", "USER"} +var enumValues81 = []string{"CAN_EDIT", "CAN_MANAGE", "CAN_RUN", "CAN_VIEW"} -var enumValuesPostgresRoleMembershipRole = []string{"DATABRICKS_SUPERUSER"} +var enumValues82 = []string{"MANAGE", "READ", "WRITE"} -var enumValuesPostgresSyncedTableSyncedTableSpecExtraColumnMaintenance = []string{"STORED_GENERATED"} +var enumValues83 = []string{"CAN_MANAGE", "CAN_USE", "IS_OWNER"} -var enumValuesPostgresSyncedTableSyncedTableSpecPgSpecificType = []string{"PG_SPECIFIC_TYPE_HALFVEC", "PG_SPECIFIC_TYPE_VARCHAR", "PG_SPECIFIC_TYPE_VECTOR"} +var enumValues84 = []string{"EXECUTE", "MODIFY", "READ_VOLUME", "SELECT", "USE_CONNECTION", "WRITE_VOLUME"} -var enumValuesPostgresSyncedTableSyncedTableSpecSyncedTableSchedulingPolicy = []string{"CONTINUOUS", "SNAPSHOT", "TRIGGERED"} +var enumValues85 = []string{"CONNECTION", "FUNCTION", "MODEL_SERVICE", "TABLE", "VOLUME"} -var enumValuesResourcesSecretScopePermissionLevel = []string{"READ", "WRITE", "MANAGE"} +var enumValues86 = []string{"AWS_SSE_KMS", "AWS_SSE_S3"} -var enumValuesServingAiGatewayGuardrailPiiBehaviorBehavior = []string{"BLOCK", "MASK", "NONE"} +var enumValues87 = []string{"ERROR", "PENDING", "RESOLVED"} -var enumValuesServingAiGatewayRateLimitKey = []string{"endpoint", "service_principal", "user", "user_group"} +var enumValues88 = []string{"PREMIUM_LRS", "STANDARD_LRS"} -var enumValuesServingAiGatewayRateLimitRenewalPeriod = []string{"minute"} +var enumValues89 = []string{"DISCONNECTED", "NOT_SYNCED"} -var enumValuesServingAmazonBedrockConfigBedrockProvider = []string{"ai21labs", "amazon", "anthropic", "cohere"} +var enumValues9 = []string{"DEPENDENCY_MODE_AUTO", "DEPENDENCY_MODE_CLUSTER_LIBRARIES", "DEPENDENCY_MODE_ENVIRONMENTS"} -var enumValuesServingExternalModelProvider = []string{"ai21labs", "amazon-bedrock", "anthropic", "cohere", "custom", "databricks-model-serving", "google-cloud-vertex-ai", "openai", "palm"} +var enumValues90 = []string{"RUN_DURATION_SECONDS", "STREAMING_BACKLOG_BYTES", "STREAMING_BACKLOG_FILES", "STREAMING_BACKLOG_RECORDS", "STREAMING_BACKLOG_SECONDS"} -var enumValuesServingRateLimitKey = []string{"endpoint", "user"} +var enumValues91 = []string{"GREATER_THAN"} -var enumValuesServingRateLimitRenewalPeriod = []string{"minute"} +var enumValues92 = []string{"QUERY_RETURNS_ROWS", "RESULT_VALUE_CHANGES"} -var enumValuesServingServedModelInputWorkloadType = []string{"CPU", "CPU_LARGE", "CPU_MEDIUM", "GPU_LARGE", "GPU_MEDIUM", "GPU_SMALL", "GPU_XLARGE", "GPU_XLARGE_8", "MULTIGPU_MEDIUM"} +var enumValues93 = []string{"BEST_EFFORT", "CRITICAL", "NORMAL"} -var enumValuesServingServingEndpointPermissionLevel = []string{"CAN_MANAGE", "CAN_QUERY", "CAN_VIEW"} +var enumValues94 = []string{"GPU_1xA10", "GPU_1xH100", "GPU_8xB300", "GPU_8xH100"} -var enumValuesServingServingModelWorkloadType = []string{"CPU", "CPU_LARGE", "CPU_MEDIUM", "GPU_LARGE", "GPU_MEDIUM", "GPU_SMALL", "GPU_XLARGE", "GPU_XLARGE_8", "MULTIGPU_MEDIUM"} +var enumValues95 = []string{"EQUAL_TO", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "NOT_EQUAL"} -var enumValuesServingTelemetryFeature = []string{"TELEMETRY_FEATURE_INFERENCE_TABLE", "TELEMETRY_FEATURE_LOGS", "TELEMETRY_FEATURE_METRICS", "TELEMETRY_FEATURE_TRACES"} +var enumValues96 = []string{"GIT", "WORKSPACE"} -var enumValuesSqlAggregation = []string{"AVG", "COUNT", "COUNT_DISTINCT", "MAX", "MEDIAN", "MIN", "STDDEV", "SUM"} +var enumValues97 = []string{"MODEL_ALIAS_SET", "MODEL_CREATED", "MODEL_VERSION_READY"} -var enumValuesSqlAlertEvaluationState = []string{"ERROR", "OK", "TRIGGERED", "UNKNOWN"} +var enumValues98 = []string{"DAYS", "HOURS", "MINUTES", "WEEKS"} -var enumValuesSqlAlertLifecycleState = []string{"ACTIVE", "DELETED"} +var enumValues99 = []string{"ALL_UPDATED", "ANY_UPDATED"} -var enumValuesSqlChannelName = []string{"CHANNEL_NAME_CURRENT", "CHANNEL_NAME_CUSTOM", "CHANNEL_NAME_PREVIEW", "CHANNEL_NAME_PREVIOUS"} - -var enumValuesSqlComparisonOperator = []string{"EQUAL", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "IS_NOT_NULL", "IS_NULL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "NOT_EQUAL"} - -var enumValuesSqlCreateWarehouseRequestWarehouseType = []string{"CLASSIC", "PRO", "REALTIME", "TYPE_UNSPECIFIED"} - -var enumValuesSqlCustomTemplateFormat = []string{"HTML", "MARKDOWN"} - -var enumValuesSqlSchedulePauseStatus = []string{"PAUSED", "UNPAUSED"} - -var enumValuesSqlSpotInstancePolicy = []string{"COST_OPTIMIZED", "POLICY_UNSPECIFIED", "RELIABILITY_OPTIMIZED"} - -var enumValuesSqlWarehousePermissionLevel = []string{"CAN_MANAGE", "CAN_MONITOR", "CAN_USE", "CAN_VIEW", "IS_OWNER"} - -var enumValuesVariableVariableType = []string{"complex"} - -var enumValuesVectorsearchEndpointType = []string{"STANDARD", "STORAGE_OPTIMIZED"} - -var enumValuesVectorsearchIndexSubtype = []string{"FULL_TEXT", "HYBRID", "VECTOR"} - -var enumValuesVectorsearchPipelineType = []string{"CONTINUOUS", "TRIGGERED"} - -var enumValuesVectorsearchVectorIndexType = []string{"DELTA_SYNC", "DIRECT_ACCESS"} - -var enumValuesVectorsearchVectorSearchEndpointPermissionLevel = []string{"CAN_CREATE", "CAN_MANAGE", "CAN_USE"} - -var enumValuesWorkspaceScopeBackendType = []string{"AZURE_KEYVAULT", "DATABRICKS"} - -func enumConfigRoot(w *walker, n *node, v *config.Root) { +func enumConfigRoot(w *walker, n *walk.Node, v *config.Root) { if v == nil { - v = &config.Root{} + return } if len(v.Artifacts) > 0 { - c := n.child("artifacts") - for k0, x0 := range v.Artifacts { - if k0 == "" { + c := n.Child("artifacts") + for k, x := range v.Artifacts { + if k == "" { continue } - e := c.child(k0) - enumConfigArtifact(w, &e, x0) + e := c.Child(k) + enumConfigArtifact(w, &e, x) } } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("resources"); c.exists() { - enumConfigResources(w, &c, &v.Resources) + if x := &v.Resources; !walk.IsZeroStruct(x) || n.HasRefs("resources") { + c := n.Child("resources") + enumConfigResources(w, &c, x) } if len(v.Variables) > 0 { - c := n.child("variables") - for k0, x0 := range v.Variables { - if k0 == "" { + c := n.Child("variables") + for k, x := range v.Variables { + if k == "" { continue } - e := c.child(k0) - enumVariableVariable(w, &e, x0) + e := c.Child(k) + enumVariableVariable(w, &e, x) } } - if c := n.child("workspace"); c.exists() { - enumConfigWorkspace(w, &c, &v.Workspace) + if x := &v.Workspace; !walk.IsZeroStruct(x) || n.HasRefs("workspace") { + c := n.Child("workspace") + enumConfigWorkspace(w, &c, x) } } -func enumConfigArtifact(w *walker, n *node, v *config.Artifact) { +func enumConfigArtifact(w *walker, n *walk.Node, v *config.Artifact) { if v == nil { - v = &config.Artifact{} + return + } + if s := string(v.Executable); s != "" { + c := n.Child("executable") + w.enumValue(&c, s, enumValues0) + } + if s := string(v.Type); s != "" { + c := n.Child("type") + w.enumValue(&c, s, enumValues1) } - w.enumField(n, "executable", string(v.Executable), enumValuesExecExecutableType) - w.enumField(n, "type", string(v.Type), enumValuesConfigArtifactType) } -func enumResourcesPermission(w *walker, n *node, v *resources.Permission) { +func enumResourcesPermission(w *walker, n *walk.Node, v *resources.Permission) { if v == nil { - v = &resources.Permission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues2) } - w.enumField(n, "level", string(v.Level), enumValuesIamPermissionLevel) } -func enumConfigResources(w *walker, n *node, v *config.Resources) { +func enumConfigResources(w *walker, n *walk.Node, v *config.Resources) { if v == nil { - v = &config.Resources{} + return } if len(v.Alerts) > 0 { - c := n.child("alerts") - for k0, x0 := range v.Alerts { - if k0 == "" { + c := n.Child("alerts") + for k, x := range v.Alerts { + if k == "" { continue } - e := c.child(k0) - enumResourcesAlert(w, &e, x0) + e := c.Child(k) + enumResourcesAlert(w, &e, x) } } if len(v.Apps) > 0 { - c := n.child("apps") - for k0, x0 := range v.Apps { - if k0 == "" { + c := n.Child("apps") + for k, x := range v.Apps { + if k == "" { continue } - e := c.child(k0) - enumResourcesApp(w, &e, x0) + e := c.Child(k) + enumResourcesApp(w, &e, x) } } if len(v.Catalogs) > 0 { - c := n.child("catalogs") - for k0, x0 := range v.Catalogs { - if k0 == "" { + c := n.Child("catalogs") + for k, x := range v.Catalogs { + if k == "" { continue } - e := c.child(k0) - enumResourcesCatalog(w, &e, x0) + e := c.Child(k) + enumResourcesCatalog(w, &e, x) } } if len(v.ClusterPolicies) > 0 { - c := n.child("cluster_policies") - for k0, x0 := range v.ClusterPolicies { - if k0 == "" { + c := n.Child("cluster_policies") + for k, x := range v.ClusterPolicies { + if k == "" { continue } - e := c.child(k0) - enumResourcesClusterPolicy(w, &e, x0) + e := c.Child(k) + enumResourcesClusterPolicy(w, &e, x) } } if len(v.Clusters) > 0 { - c := n.child("clusters") - for k0, x0 := range v.Clusters { - if k0 == "" { + c := n.Child("clusters") + for k, x := range v.Clusters { + if k == "" { continue } - e := c.child(k0) - enumResourcesCluster(w, &e, x0) + e := c.Child(k) + enumResourcesCluster(w, &e, x) } } if len(v.Dashboards) > 0 { - c := n.child("dashboards") - for k0, x0 := range v.Dashboards { - if k0 == "" { + c := n.Child("dashboards") + for k, x := range v.Dashboards { + if k == "" { continue } - e := c.child(k0) - enumResourcesDashboard(w, &e, x0) + e := c.Child(k) + enumResourcesDashboard(w, &e, x) } } if len(v.DatabaseInstances) > 0 { - c := n.child("database_instances") - for k0, x0 := range v.DatabaseInstances { - if k0 == "" { + c := n.Child("database_instances") + for k, x := range v.DatabaseInstances { + if k == "" { continue } - e := c.child(k0) - enumResourcesDatabaseInstance(w, &e, x0) + e := c.Child(k) + enumResourcesDatabaseInstance(w, &e, x) } } if len(v.Experiments) > 0 { - c := n.child("experiments") - for k0, x0 := range v.Experiments { - if k0 == "" { + c := n.Child("experiments") + for k, x := range v.Experiments { + if k == "" { continue } - e := c.child(k0) - enumResourcesMlflowExperiment(w, &e, x0) + e := c.Child(k) + enumResourcesMlflowExperiment(w, &e, x) } } if len(v.ExternalLocations) > 0 { - c := n.child("external_locations") - for k0, x0 := range v.ExternalLocations { - if k0 == "" { + c := n.Child("external_locations") + for k, x := range v.ExternalLocations { + if k == "" { continue } - e := c.child(k0) - enumResourcesExternalLocation(w, &e, x0) + e := c.Child(k) + enumResourcesExternalLocation(w, &e, x) } } if len(v.Features) > 0 { - c := n.child("features") - for k0, x0 := range v.Features { - if k0 == "" { + c := n.Child("features") + for k, x := range v.Features { + if k == "" { continue } - e := c.child(k0) - enumResourcesFeature(w, &e, x0) + e := c.Child(k) + enumResourcesFeature(w, &e, x) } } if len(v.GenieSpaces) > 0 { - c := n.child("genie_spaces") - for k0, x0 := range v.GenieSpaces { - if k0 == "" { + c := n.Child("genie_spaces") + for k, x := range v.GenieSpaces { + if k == "" { continue } - e := c.child(k0) - enumResourcesGenieSpace(w, &e, x0) + e := c.Child(k) + enumResourcesGenieSpace(w, &e, x) } } if len(v.InstancePools) > 0 { - c := n.child("instance_pools") - for k0, x0 := range v.InstancePools { - if k0 == "" { + c := n.Child("instance_pools") + for k, x := range v.InstancePools { + if k == "" { continue } - e := c.child(k0) - enumResourcesInstancePool(w, &e, x0) + e := c.Child(k) + enumResourcesInstancePool(w, &e, x) } } if len(v.JobRuns) > 0 { - c := n.child("job_runs") - for k0, x0 := range v.JobRuns { - if k0 == "" { + c := n.Child("job_runs") + for k, x := range v.JobRuns { + if k == "" { continue } - e := c.child(k0) - enumResourcesJobRun(w, &e, x0) + e := c.Child(k) + enumResourcesJobRun(w, &e, x) } } if len(v.Jobs) > 0 { - c := n.child("jobs") - for k0, x0 := range v.Jobs { - if k0 == "" { + c := n.Child("jobs") + for k, x := range v.Jobs { + if k == "" { continue } - e := c.child(k0) - enumResourcesJob(w, &e, x0) + e := c.Child(k) + enumResourcesJob(w, &e, x) } } if len(v.McpServices) > 0 { - c := n.child("mcp_services") - for k0, x0 := range v.McpServices { - if k0 == "" { + c := n.Child("mcp_services") + for k, x := range v.McpServices { + if k == "" { continue } - e := c.child(k0) - enumResourcesMcpService(w, &e, x0) + e := c.Child(k) + enumResourcesMcpService(w, &e, x) } } if len(v.ModelProviderServices) > 0 { - c := n.child("model_provider_services") - for k0, x0 := range v.ModelProviderServices { - if k0 == "" { + c := n.Child("model_provider_services") + for k, x := range v.ModelProviderServices { + if k == "" { continue } - e := c.child(k0) - enumResourcesModelProviderService(w, &e, x0) + e := c.Child(k) + enumResourcesModelProviderService(w, &e, x) } } if len(v.ModelServices) > 0 { - c := n.child("model_services") - for k0, x0 := range v.ModelServices { - if k0 == "" { + c := n.Child("model_services") + for k, x := range v.ModelServices { + if k == "" { continue } - e := c.child(k0) - enumResourcesModelService(w, &e, x0) + e := c.Child(k) + enumResourcesModelService(w, &e, x) } } if len(v.ModelServingEndpoints) > 0 { - c := n.child("model_serving_endpoints") - for k0, x0 := range v.ModelServingEndpoints { - if k0 == "" { + c := n.Child("model_serving_endpoints") + for k, x := range v.ModelServingEndpoints { + if k == "" { continue } - e := c.child(k0) - enumResourcesModelServingEndpoint(w, &e, x0) + e := c.Child(k) + enumResourcesModelServingEndpoint(w, &e, x) } } if len(v.Models) > 0 { - c := n.child("models") - for k0, x0 := range v.Models { - if k0 == "" { + c := n.Child("models") + for k, x := range v.Models { + if k == "" { continue } - e := c.child(k0) - enumResourcesMlflowModel(w, &e, x0) + e := c.Child(k) + enumResourcesMlflowModel(w, &e, x) } } if len(v.Pipelines) > 0 { - c := n.child("pipelines") - for k0, x0 := range v.Pipelines { - if k0 == "" { + c := n.Child("pipelines") + for k, x := range v.Pipelines { + if k == "" { continue } - e := c.child(k0) - enumResourcesPipeline(w, &e, x0) + e := c.Child(k) + enumResourcesPipeline(w, &e, x) } } if len(v.PostgresEndpoints) > 0 { - c := n.child("postgres_endpoints") - for k0, x0 := range v.PostgresEndpoints { - if k0 == "" { + c := n.Child("postgres_endpoints") + for k, x := range v.PostgresEndpoints { + if k == "" { continue } - e := c.child(k0) - enumResourcesPostgresEndpoint(w, &e, x0) + e := c.Child(k) + enumResourcesPostgresEndpoint(w, &e, x) } } if len(v.PostgresProjects) > 0 { - c := n.child("postgres_projects") - for k0, x0 := range v.PostgresProjects { - if k0 == "" { + c := n.Child("postgres_projects") + for k, x := range v.PostgresProjects { + if k == "" { continue } - e := c.child(k0) - enumResourcesPostgresProject(w, &e, x0) + e := c.Child(k) + enumResourcesPostgresProject(w, &e, x) } } if len(v.PostgresRoles) > 0 { - c := n.child("postgres_roles") - for k0, x0 := range v.PostgresRoles { - if k0 == "" { + c := n.Child("postgres_roles") + for k, x := range v.PostgresRoles { + if k == "" { continue } - e := c.child(k0) - enumResourcesPostgresRole(w, &e, x0) + e := c.Child(k) + enumResourcesPostgresRole(w, &e, x) } } if len(v.PostgresSnapshotSchedules) > 0 { - c := n.child("postgres_snapshot_schedules") - for k0, x0 := range v.PostgresSnapshotSchedules { - if k0 == "" { + c := n.Child("postgres_snapshot_schedules") + for k, x := range v.PostgresSnapshotSchedules { + if k == "" { continue } - e := c.child(k0) - enumResourcesPostgresSnapshotSchedule(w, &e, x0) + e := c.Child(k) + enumResourcesPostgresSnapshotSchedule(w, &e, x) } } if len(v.PostgresSyncedTables) > 0 { - c := n.child("postgres_synced_tables") - for k0, x0 := range v.PostgresSyncedTables { - if k0 == "" { + c := n.Child("postgres_synced_tables") + for k, x := range v.PostgresSyncedTables { + if k == "" { continue } - e := c.child(k0) - enumResourcesPostgresSyncedTable(w, &e, x0) + e := c.Child(k) + enumResourcesPostgresSyncedTable(w, &e, x) } } if len(v.QualityMonitors) > 0 { - c := n.child("quality_monitors") - for k0, x0 := range v.QualityMonitors { - if k0 == "" { + c := n.Child("quality_monitors") + for k, x := range v.QualityMonitors { + if k == "" { continue } - e := c.child(k0) - enumResourcesQualityMonitor(w, &e, x0) + e := c.Child(k) + enumResourcesQualityMonitor(w, &e, x) } } if len(v.RegisteredModels) > 0 { - c := n.child("registered_models") - for k0, x0 := range v.RegisteredModels { - if k0 == "" { + c := n.Child("registered_models") + for k, x := range v.RegisteredModels { + if k == "" { continue } - e := c.child(k0) - enumResourcesRegisteredModel(w, &e, x0) + e := c.Child(k) + enumResourcesRegisteredModel(w, &e, x) } } if len(v.Schemas) > 0 { - c := n.child("schemas") - for k0, x0 := range v.Schemas { - if k0 == "" { + c := n.Child("schemas") + for k, x := range v.Schemas { + if k == "" { continue } - e := c.child(k0) - enumResourcesSchema(w, &e, x0) + e := c.Child(k) + enumResourcesSchema(w, &e, x) } } if len(v.SecretScopes) > 0 { - c := n.child("secret_scopes") - for k0, x0 := range v.SecretScopes { - if k0 == "" { + c := n.Child("secret_scopes") + for k, x := range v.SecretScopes { + if k == "" { continue } - e := c.child(k0) - enumResourcesSecretScope(w, &e, x0) + e := c.Child(k) + enumResourcesSecretScope(w, &e, x) } } if len(v.Secrets) > 0 { - c := n.child("secrets") - for k0, x0 := range v.Secrets { - if k0 == "" { + c := n.Child("secrets") + for k, x := range v.Secrets { + if k == "" { continue } - e := c.child(k0) - enumResourcesSecret(w, &e, x0) + e := c.Child(k) + enumResourcesSecret(w, &e, x) } } if len(v.SqlWarehouses) > 0 { - c := n.child("sql_warehouses") - for k0, x0 := range v.SqlWarehouses { - if k0 == "" { + c := n.Child("sql_warehouses") + for k, x := range v.SqlWarehouses { + if k == "" { continue } - e := c.child(k0) - enumResourcesSqlWarehouse(w, &e, x0) + e := c.Child(k) + enumResourcesSqlWarehouse(w, &e, x) } } if len(v.SyncedDatabaseTables) > 0 { - c := n.child("synced_database_tables") - for k0, x0 := range v.SyncedDatabaseTables { - if k0 == "" { + c := n.Child("synced_database_tables") + for k, x := range v.SyncedDatabaseTables { + if k == "" { continue } - e := c.child(k0) - enumResourcesSyncedDatabaseTable(w, &e, x0) + e := c.Child(k) + enumResourcesSyncedDatabaseTable(w, &e, x) } } if len(v.VectorSearchEndpoints) > 0 { - c := n.child("vector_search_endpoints") - for k0, x0 := range v.VectorSearchEndpoints { - if k0 == "" { + c := n.Child("vector_search_endpoints") + for k, x := range v.VectorSearchEndpoints { + if k == "" { continue } - e := c.child(k0) - enumResourcesVectorSearchEndpoint(w, &e, x0) + e := c.Child(k) + enumResourcesVectorSearchEndpoint(w, &e, x) } } if len(v.VectorSearchIndexes) > 0 { - c := n.child("vector_search_indexes") - for k0, x0 := range v.VectorSearchIndexes { - if k0 == "" { + c := n.Child("vector_search_indexes") + for k, x := range v.VectorSearchIndexes { + if k == "" { continue } - e := c.child(k0) - enumResourcesVectorSearchIndex(w, &e, x0) + e := c.Child(k) + enumResourcesVectorSearchIndex(w, &e, x) } } if len(v.Volumes) > 0 { - c := n.child("volumes") - for k0, x0 := range v.Volumes { - if k0 == "" { + c := n.Child("volumes") + for k, x := range v.Volumes { + if k == "" { continue } - e := c.child(k0) - enumResourcesVolume(w, &e, x0) + e := c.Child(k) + enumResourcesVolume(w, &e, x) } } } -func enumVariableVariable(w *walker, n *node, v *variable.Variable) { +func enumVariableVariable(w *walker, n *walk.Node, v *variable.Variable) { if v == nil { - v = &variable.Variable{} + return + } + if s := string(v.Type); s != "" { + c := n.Child("type") + w.enumValue(&c, s, enumValues3) } - w.enumField(n, "type", string(v.Type), enumValuesVariableVariableType) } -func enumConfigWorkspace(w *walker, n *node, v *config.Workspace) { +func enumConfigWorkspace(w *walker, n *walk.Node, v *config.Workspace) { if v == nil { - v = &config.Workspace{} + return + } + if s := string(v.SyncMode); s != "" { + c := n.Child("sync_mode") + w.enumValue(&c, s, enumValues4) } - w.enumField(n, "sync_mode", string(v.SyncMode), enumValuesConfigSyncMode) } -func enumResourcesAlert(w *walker, n *node, v *resources.Alert) { +func enumResourcesAlert(w *walker, n *walk.Node, v *resources.Alert) { if v == nil { - v = &resources.Alert{} + return + } + if s := string(v.AlertV2.CustomTemplateFormat); s != "" || walk.Forced(v.AlertV2.ForceSendFields, "CustomTemplateFormat") { + c := n.Child("custom_template_format") + w.enumValue(&c, s, enumValues5) } - w.enumField(n, "custom_template_format", string(v.AlertV2.CustomTemplateFormat), enumValuesSqlCustomTemplateFormat) - if c := n.child("evaluation"); c.exists() { - enumSqlAlertV2Evaluation(w, &c, &v.AlertV2.Evaluation) + if x := &v.AlertV2.Evaluation; !walk.IsZeroStruct(x) || walk.Forced(v.AlertV2.ForceSendFields, "Evaluation") || n.HasRefs("evaluation") { + c := n.Child("evaluation") + enumSqlAlertV2Evaluation(w, &c, x) + } + if s := string(v.AlertV2.LifecycleState); s != "" || walk.Forced(v.AlertV2.ForceSendFields, "LifecycleState") { + c := n.Child("lifecycle_state") + w.enumValue(&c, s, enumValues6) } - w.enumField(n, "lifecycle_state", string(v.AlertV2.LifecycleState), enumValuesSqlAlertLifecycleState) if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("schedule"); c.exists() { - enumSqlCronSchedule(w, &c, &v.AlertV2.Schedule) + if x := &v.AlertV2.Schedule; !walk.IsZeroStruct(x) || walk.Forced(v.AlertV2.ForceSendFields, "Schedule") || n.HasRefs("schedule") { + c := n.Child("schedule") + enumSqlCronSchedule(w, &c, x) } } -func enumResourcesApp(w *walker, n *node, v *resources.App) { +func enumResourcesApp(w *walker, n *walk.Node, v *resources.App) { if v == nil { - v = &resources.App{} + return + } + if x := v.App.ActiveDeployment; x != nil { + c := n.Child("active_deployment") + enumAppsAppDeployment(w, &c, x) } - if c := n.child("active_deployment"); c.exists() { - enumAppsAppDeployment(w, &c, v.App.ActiveDeployment) + if x := v.App.AppStatus; x != nil { + c := n.Child("app_status") + enumAppsApplicationStatus(w, &c, x) } - if c := n.child("app_status"); c.exists() { - enumAppsApplicationStatus(w, &c, v.App.AppStatus) + if s := string(v.App.ComputeSize); s != "" || walk.Forced(v.App.ForceSendFields, "ComputeSize") { + c := n.Child("compute_size") + w.enumValue(&c, s, enumValues7) } - w.enumField(n, "compute_size", string(v.App.ComputeSize), enumValuesAppsComputeSize) - if c := n.child("compute_status"); c.exists() { - enumAppsComputeStatus(w, &c, v.App.ComputeStatus) + if x := v.App.ComputeStatus; x != nil { + c := n.Child("compute_status") + enumAppsComputeStatus(w, &c, x) } - if c := n.child("pending_deployment"); c.exists() { - enumAppsAppDeployment(w, &c, v.App.PendingDeployment) + if x := v.App.PendingDeployment; x != nil { + c := n.Child("pending_deployment") + enumAppsAppDeployment(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesAppPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesAppPermission(w, &e, &v.Permissions[i]) } } if len(v.App.Resources) > 0 { - c := n.child("resources") - for i0 := range v.App.Resources { - e := c.elem(i0) - enumAppsAppResource(w, &e, &v.App.Resources[i0]) + c := n.Child("resources") + for i := range v.App.Resources { + e := c.Elem(i) + enumAppsAppResource(w, &e, &v.App.Resources[i]) } } } -func enumResourcesCatalog(w *walker, n *node, v *resources.Catalog) { +func enumResourcesCatalog(w *walker, n *walk.Node, v *resources.Catalog) { if v == nil { - v = &resources.Catalog{} + return } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesClusterPolicy(w *walker, n *node, v *resources.ClusterPolicy) { +func enumResourcesClusterPolicy(w *walker, n *walk.Node, v *resources.ClusterPolicy) { if v == nil { - v = &resources.ClusterPolicy{} + return } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesClusterPolicyPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesClusterPolicyPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesCluster(w *walker, n *node, v *resources.Cluster) { +func enumResourcesCluster(w *walker, n *walk.Node, v *resources.Cluster) { if v == nil { - v = &resources.Cluster{} + return } - if c := n.child("aws_attributes"); c.exists() { - enumComputeAwsAttributes(w, &c, v.ClusterSpec.AwsAttributes) + if x := v.ClusterSpec.AwsAttributes; x != nil { + c := n.Child("aws_attributes") + enumComputeAwsAttributes(w, &c, x) } - if c := n.child("azure_attributes"); c.exists() { - enumComputeAzureAttributes(w, &c, v.ClusterSpec.AzureAttributes) + if x := v.ClusterSpec.AzureAttributes; x != nil { + c := n.Child("azure_attributes") + enumComputeAzureAttributes(w, &c, x) } - w.enumField(n, "data_security_mode", string(v.ClusterSpec.DataSecurityMode), enumValuesComputeDataSecurityMode) - w.enumField(n, "dependency_mode", string(v.ClusterSpec.DependencyMode), enumValuesComputeDependencyMode) - if c := n.child("gcp_attributes"); c.exists() { - enumComputeGcpAttributes(w, &c, v.ClusterSpec.GcpAttributes) + if s := string(v.ClusterSpec.DataSecurityMode); s != "" || walk.Forced(v.ClusterSpec.ForceSendFields, "DataSecurityMode") { + c := n.Child("data_security_mode") + w.enumValue(&c, s, enumValues8) + } + if s := string(v.ClusterSpec.DependencyMode); s != "" || walk.Forced(v.ClusterSpec.ForceSendFields, "DependencyMode") { + c := n.Child("dependency_mode") + w.enumValue(&c, s, enumValues9) + } + if x := v.ClusterSpec.GcpAttributes; x != nil { + c := n.Child("gcp_attributes") + enumComputeGcpAttributes(w, &c, x) + } + if s := string(v.ClusterSpec.Kind); s != "" || walk.Forced(v.ClusterSpec.ForceSendFields, "Kind") { + c := n.Child("kind") + w.enumValue(&c, s, enumValues10) } - w.enumField(n, "kind", string(v.ClusterSpec.Kind), enumValuesComputeKind) if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesClusterPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesClusterPermission(w, &e, &v.Permissions[i]) } } - w.enumField(n, "runtime_engine", string(v.ClusterSpec.RuntimeEngine), enumValuesComputeRuntimeEngine) + if s := string(v.ClusterSpec.RuntimeEngine); s != "" || walk.Forced(v.ClusterSpec.ForceSendFields, "RuntimeEngine") { + c := n.Child("runtime_engine") + w.enumValue(&c, s, enumValues11) + } } -func enumResourcesDashboard(w *walker, n *node, v *resources.Dashboard) { +func enumResourcesDashboard(w *walker, n *walk.Node, v *resources.Dashboard) { if v == nil { - v = &resources.Dashboard{} + return + } + if s := string(v.DashboardConfig.LifecycleState); s != "" || walk.Forced(v.DashboardConfig.ForceSendFields, "LifecycleState") { + c := n.Child("lifecycle_state") + w.enumValue(&c, s, enumValues12) } - w.enumField(n, "lifecycle_state", string(v.DashboardConfig.LifecycleState), enumValuesDashboardsLifecycleState) if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesDatabaseInstance(w *walker, n *node, v *resources.DatabaseInstance) { +func enumResourcesDatabaseInstance(w *walker, n *walk.Node, v *resources.DatabaseInstance) { if v == nil { - v = &resources.DatabaseInstance{} + return } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPermission(w, &e, &v.Permissions[i]) } } - w.enumField(n, "state", string(v.DatabaseInstance.State), enumValuesDatabaseDatabaseInstanceState) + if s := string(v.DatabaseInstance.State); s != "" || walk.Forced(v.DatabaseInstance.ForceSendFields, "State") { + c := n.Child("state") + w.enumValue(&c, s, enumValues13) + } } -func enumResourcesMlflowExperiment(w *walker, n *node, v *resources.MlflowExperiment) { +func enumResourcesMlflowExperiment(w *walker, n *walk.Node, v *resources.MlflowExperiment) { if v == nil { - v = &resources.MlflowExperiment{} + return } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesMlflowExperimentPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesMlflowExperimentPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesExternalLocation(w *walker, n *node, v *resources.ExternalLocation) { +func enumResourcesExternalLocation(w *walker, n *walk.Node, v *resources.ExternalLocation) { if v == nil { - v = &resources.ExternalLocation{} + return } - if c := n.child("encryption_details"); c.exists() { - enumCatalogEncryptionDetails(w, &c, v.CreateExternalLocation.EncryptionDetails) + if x := v.CreateExternalLocation.EncryptionDetails; x != nil { + c := n.Child("encryption_details") + enumCatalogEncryptionDetails(w, &c, x) } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesFeature(w *walker, n *node, v *resources.Feature) { +func enumResourcesFeature(w *walker, n *walk.Node, v *resources.Feature) { if v == nil { - v = &resources.Feature{} + return } - if c := n.child("function"); c.exists() { - enumMlFunction(w, &c, &v.Feature.Function) + if x := &v.Feature.Function; !walk.IsZeroStruct(x) || walk.Forced(v.Feature.ForceSendFields, "Function") || n.HasRefs("function") { + c := n.Child("function") + enumMlFunction(w, &c, x) } - if c := n.child("source"); c.exists() { - enumMlDataSource(w, &c, &v.Feature.Source) + if x := &v.Feature.Source; !walk.IsZeroStruct(x) || walk.Forced(v.Feature.ForceSendFields, "Source") || n.HasRefs("source") { + c := n.Child("source") + enumMlDataSource(w, &c, x) } } -func enumResourcesGenieSpace(w *walker, n *node, v *resources.GenieSpace) { +func enumResourcesGenieSpace(w *walker, n *walk.Node, v *resources.GenieSpace) { if v == nil { - v = &resources.GenieSpace{} + return } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesInstancePool(w *walker, n *node, v *resources.InstancePool) { +func enumResourcesInstancePool(w *walker, n *walk.Node, v *resources.InstancePool) { if v == nil { - v = &resources.InstancePool{} + return } - if c := n.child("aws_attributes"); c.exists() { - enumComputeInstancePoolAwsAttributes(w, &c, v.CreateInstancePool.AwsAttributes) + if x := v.CreateInstancePool.AwsAttributes; x != nil { + c := n.Child("aws_attributes") + enumComputeInstancePoolAwsAttributes(w, &c, x) } - if c := n.child("azure_attributes"); c.exists() { - enumComputeInstancePoolAzureAttributes(w, &c, v.CreateInstancePool.AzureAttributes) + if x := v.CreateInstancePool.AzureAttributes; x != nil { + c := n.Child("azure_attributes") + enumComputeInstancePoolAzureAttributes(w, &c, x) } - if c := n.child("disk_spec"); c.exists() { - enumComputeDiskSpec(w, &c, v.CreateInstancePool.DiskSpec) + if x := v.CreateInstancePool.DiskSpec; x != nil { + c := n.Child("disk_spec") + enumComputeDiskSpec(w, &c, x) } - if c := n.child("gcp_attributes"); c.exists() { - enumComputeInstancePoolGcpAttributes(w, &c, v.CreateInstancePool.GcpAttributes) + if x := v.CreateInstancePool.GcpAttributes; x != nil { + c := n.Child("gcp_attributes") + enumComputeInstancePoolGcpAttributes(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesInstancePoolPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesInstancePoolPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesJobRun(w *walker, n *node, v *resources.JobRun) { +func enumResourcesJobRun(w *walker, n *walk.Node, v *resources.JobRun) { if v == nil { - v = &resources.JobRun{} + return + } + if s := string(v.RunNow.PerformanceTarget); s != "" || walk.Forced(v.RunNow.ForceSendFields, "PerformanceTarget") { + c := n.Child("performance_target") + w.enumValue(&c, s, enumValues14) } - w.enumField(n, "performance_target", string(v.RunNow.PerformanceTarget), enumValuesJobsPerformanceTarget) } -func enumResourcesJob(w *walker, n *node, v *resources.Job) { +func enumResourcesJob(w *walker, n *walk.Node, v *resources.Job) { if v == nil { - v = &resources.Job{} + return + } + if x := v.JobSettings.Continuous; x != nil { + c := n.Child("continuous") + enumJobsContinuous(w, &c, x) + } + if x := v.JobSettings.Deployment; x != nil { + c := n.Child("deployment") + enumJobsJobDeployment(w, &c, x) } - if c := n.child("continuous"); c.exists() { - enumJobsContinuous(w, &c, v.JobSettings.Continuous) + if s := string(v.JobSettings.EditMode); s != "" || walk.Forced(v.JobSettings.ForceSendFields, "EditMode") { + c := n.Child("edit_mode") + w.enumValue(&c, s, enumValues15) } - if c := n.child("deployment"); c.exists() { - enumJobsJobDeployment(w, &c, v.JobSettings.Deployment) + if s := string(v.JobSettings.Format); s != "" || walk.Forced(v.JobSettings.ForceSendFields, "Format") { + c := n.Child("format") + w.enumValue(&c, s, enumValues16) } - w.enumField(n, "edit_mode", string(v.JobSettings.EditMode), enumValuesJobsJobEditMode) - w.enumField(n, "format", string(v.JobSettings.Format), enumValuesJobsFormat) - if c := n.child("git_source"); c.exists() { - enumJobsGitSource(w, &c, v.JobSettings.GitSource) + if x := v.JobSettings.GitSource; x != nil { + c := n.Child("git_source") + enumJobsGitSource(w, &c, x) } - if c := n.child("health"); c.exists() { - enumJobsJobsHealthRules(w, &c, v.JobSettings.Health) + if x := v.JobSettings.Health; x != nil { + c := n.Child("health") + enumJobsJobsHealthRules(w, &c, x) } if len(v.JobSettings.JobClusters) > 0 { - c := n.child("job_clusters") - for i0 := range v.JobSettings.JobClusters { - e := c.elem(i0) - enumJobsJobCluster(w, &e, &v.JobSettings.JobClusters[i0]) + c := n.Child("job_clusters") + for i := range v.JobSettings.JobClusters { + e := c.Elem(i) + enumJobsJobCluster(w, &e, &v.JobSettings.JobClusters[i]) } } - w.enumField(n, "performance_target", string(v.JobSettings.PerformanceTarget), enumValuesJobsPerformanceTarget) + if s := string(v.JobSettings.PerformanceTarget); s != "" || walk.Forced(v.JobSettings.ForceSendFields, "PerformanceTarget") { + c := n.Child("performance_target") + w.enumValue(&c, s, enumValues14) + } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesJobPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesJobPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("schedule"); c.exists() { - enumJobsCronSchedule(w, &c, v.JobSettings.Schedule) + if x := v.JobSettings.Schedule; x != nil { + c := n.Child("schedule") + enumJobsCronSchedule(w, &c, x) } if len(v.JobSettings.Tasks) > 0 { - c := n.child("tasks") - for i0 := range v.JobSettings.Tasks { - e := c.elem(i0) - enumJobsTask(w, &e, &v.JobSettings.Tasks[i0]) + c := n.Child("tasks") + for i := range v.JobSettings.Tasks { + e := c.Elem(i) + enumJobsTask(w, &e, &v.JobSettings.Tasks[i]) } } - if c := n.child("trigger"); c.exists() { - enumJobsTriggerSettings(w, &c, v.JobSettings.Trigger) + if x := v.JobSettings.Trigger; x != nil { + c := n.Child("trigger") + enumJobsTriggerSettings(w, &c, x) } if len(v.JobSettings.Triggers) > 0 { - c := n.child("triggers") - for i0 := range v.JobSettings.Triggers { - e := c.elem(i0) - enumJobsTriggerConfiguration(w, &e, &v.JobSettings.Triggers[i0]) + c := n.Child("triggers") + for i := range v.JobSettings.Triggers { + e := c.Elem(i) + enumJobsTriggerConfiguration(w, &e, &v.JobSettings.Triggers[i]) } } } -func enumResourcesMcpService(w *walker, n *node, v *resources.McpService) { +func enumResourcesMcpService(w *walker, n *walk.Node, v *resources.McpService) { if v == nil { - v = &resources.McpService{} + return } - if c := n.child("config"); c.exists() { - enumCatalogMcpServiceConfig(w, &c, v.McpServiceConfig.Config) + if x := v.McpServiceConfig.Config; x != nil { + c := n.Child("config") + enumCatalogMcpServiceConfig(w, &c, x) } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesModelProviderService(w *walker, n *node, v *resources.ModelProviderService) { +func enumResourcesModelProviderService(w *walker, n *walk.Node, v *resources.ModelProviderService) { if v == nil { - v = &resources.ModelProviderService{} + return } - if c := n.child("config"); c.exists() { - enumCatalogModelProviderServiceConfig(w, &c, v.ModelProviderServiceConfig.Config) + if x := v.ModelProviderServiceConfig.Config; x != nil { + c := n.Child("config") + enumCatalogModelProviderServiceConfig(w, &c, x) } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesModelService(w *walker, n *node, v *resources.ModelService) { +func enumResourcesModelService(w *walker, n *walk.Node, v *resources.ModelService) { if v == nil { - v = &resources.ModelService{} + return } - if c := n.child("config"); c.exists() { - enumCatalogModelServiceConfig(w, &c, v.ModelServiceConfig.Config) + if x := v.ModelServiceConfig.Config; x != nil { + c := n.Child("config") + enumCatalogModelServiceConfig(w, &c, x) } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesModelServingEndpoint(w *walker, n *node, v *resources.ModelServingEndpoint) { +func enumResourcesModelServingEndpoint(w *walker, n *walk.Node, v *resources.ModelServingEndpoint) { if v == nil { - v = &resources.ModelServingEndpoint{} + return } - if c := n.child("ai_gateway"); c.exists() { - enumServingAiGatewayConfig(w, &c, v.CreateServingEndpoint.AiGateway) + if x := v.CreateServingEndpoint.AiGateway; x != nil { + c := n.Child("ai_gateway") + enumServingAiGatewayConfig(w, &c, x) } - if c := n.child("config"); c.exists() { - enumServingEndpointCoreConfigInput(w, &c, v.CreateServingEndpoint.Config) + if x := v.CreateServingEndpoint.Config; x != nil { + c := n.Child("config") + enumServingEndpointCoreConfigInput(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesModelServingEndpointPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesModelServingEndpointPermission(w, &e, &v.Permissions[i]) } } if len(v.CreateServingEndpoint.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.CreateServingEndpoint.RateLimits { - e := c.elem(i0) - enumServingRateLimit(w, &e, &v.CreateServingEndpoint.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.CreateServingEndpoint.RateLimits { + e := c.Elem(i) + enumServingRateLimit(w, &e, &v.CreateServingEndpoint.RateLimits[i]) } } - if c := n.child("telemetry_config"); c.exists() { - enumServingTelemetryConfig(w, &c, v.CreateServingEndpoint.TelemetryConfig) + if x := v.CreateServingEndpoint.TelemetryConfig; x != nil { + c := n.Child("telemetry_config") + enumServingTelemetryConfig(w, &c, x) } } -func enumResourcesMlflowModel(w *walker, n *node, v *resources.MlflowModel) { +func enumResourcesMlflowModel(w *walker, n *walk.Node, v *resources.MlflowModel) { if v == nil { - v = &resources.MlflowModel{} + return } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesMlflowModelPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesMlflowModelPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesPipeline(w *walker, n *node, v *resources.Pipeline) { +func enumResourcesPipeline(w *walker, n *walk.Node, v *resources.Pipeline) { if v == nil { - v = &resources.Pipeline{} + return } if len(v.CreatePipeline.Clusters) > 0 { - c := n.child("clusters") - for i0 := range v.CreatePipeline.Clusters { - e := c.elem(i0) - enumPipelinesPipelineCluster(w, &e, &v.CreatePipeline.Clusters[i0]) + c := n.Child("clusters") + for i := range v.CreatePipeline.Clusters { + e := c.Elem(i) + enumPipelinesPipelineCluster(w, &e, &v.CreatePipeline.Clusters[i]) } } - if c := n.child("deployment"); c.exists() { - enumPipelinesPipelineDeployment(w, &c, v.CreatePipeline.Deployment) + if x := v.CreatePipeline.Deployment; x != nil { + c := n.Child("deployment") + enumPipelinesPipelineDeployment(w, &c, x) } - if c := n.child("ingestion_definition"); c.exists() { - enumPipelinesIngestionPipelineDefinition(w, &c, v.CreatePipeline.IngestionDefinition) + if x := v.CreatePipeline.IngestionDefinition; x != nil { + c := n.Child("ingestion_definition") + enumPipelinesIngestionPipelineDefinition(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPipelinePermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPipelinePermission(w, &e, &v.Permissions[i]) } } - if c := n.child("restart_window"); c.exists() { - enumPipelinesRestartWindow(w, &c, v.CreatePipeline.RestartWindow) + if x := v.CreatePipeline.RestartWindow; x != nil { + c := n.Child("restart_window") + enumPipelinesRestartWindow(w, &c, x) } } -func enumResourcesPostgresEndpoint(w *walker, n *node, v *resources.PostgresEndpoint) { +func enumResourcesPostgresEndpoint(w *walker, n *walk.Node, v *resources.PostgresEndpoint) { if v == nil { - v = &resources.PostgresEndpoint{} + return + } + if s := string(v.PostgresEndpointConfig.EndpointSpec.EndpointType); s != "" || walk.Forced(v.PostgresEndpointConfig.EndpointSpec.ForceSendFields, "EndpointType") { + c := n.Child("endpoint_type") + w.enumValue(&c, s, enumValues17) } - w.enumField(n, "endpoint_type", string(v.PostgresEndpointConfig.EndpointSpec.EndpointType), enumValuesPostgresEndpointType) } -func enumResourcesPostgresProject(w *walker, n *node, v *resources.PostgresProject) { +func enumResourcesPostgresProject(w *walker, n *walk.Node, v *resources.PostgresProject) { if v == nil { - v = &resources.PostgresProject{} + return } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesPostgresRole(w *walker, n *node, v *resources.PostgresRole) { +func enumResourcesPostgresRole(w *walker, n *walk.Node, v *resources.PostgresRole) { if v == nil { - v = &resources.PostgresRole{} + return + } + if s := string(v.PostgresRoleConfig.RoleRoleSpec.AuthMethod); s != "" || walk.Forced(v.PostgresRoleConfig.RoleRoleSpec.ForceSendFields, "AuthMethod") { + c := n.Child("auth_method") + w.enumValue(&c, s, enumValues18) + } + if s := string(v.PostgresRoleConfig.RoleRoleSpec.IdentityType); s != "" || walk.Forced(v.PostgresRoleConfig.RoleRoleSpec.ForceSendFields, "IdentityType") { + c := n.Child("identity_type") + w.enumValue(&c, s, enumValues19) } - w.enumField(n, "auth_method", string(v.PostgresRoleConfig.RoleRoleSpec.AuthMethod), enumValuesPostgresRoleAuthMethod) - w.enumField(n, "identity_type", string(v.PostgresRoleConfig.RoleRoleSpec.IdentityType), enumValuesPostgresRoleIdentityType) if len(v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles) > 0 { - c := n.child("membership_roles") - for i0 := range v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles { - e := c.elem(i0) - w.enumElem(&e, string(v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles[i0]), enumValuesPostgresRoleMembershipRole) + c := n.Child("membership_roles") + for i := range v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles { + e := c.Elem(i) + w.enumValue(&e, string(v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles[i]), enumValues20) } } } -func enumResourcesPostgresSnapshotSchedule(w *walker, n *node, v *resources.PostgresSnapshotSchedule) { +func enumResourcesPostgresSnapshotSchedule(w *walker, n *walk.Node, v *resources.PostgresSnapshotSchedule) { if v == nil { - v = &resources.PostgresSnapshotSchedule{} + return } if len(v.PostgresSnapshotScheduleConfig.Schedule) > 0 { - c := n.child("schedule") - for i0 := range v.PostgresSnapshotScheduleConfig.Schedule { - e := c.elem(i0) - enumPostgresScheduleCadence(w, &e, &v.PostgresSnapshotScheduleConfig.Schedule[i0]) + c := n.Child("schedule") + for i := range v.PostgresSnapshotScheduleConfig.Schedule { + e := c.Elem(i) + enumPostgresScheduleCadence(w, &e, &v.PostgresSnapshotScheduleConfig.Schedule[i]) } } } -func enumResourcesPostgresSyncedTable(w *walker, n *node, v *resources.PostgresSyncedTable) { +func enumResourcesPostgresSyncedTable(w *walker, n *walk.Node, v *resources.PostgresSyncedTable) { if v == nil { - v = &resources.PostgresSyncedTable{} + return } if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns) > 0 { - c := n.child("extra_columns") - for i0 := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns { - e := c.elem(i0) - enumPostgresSyncedTableSyncedTableSpecExtraColumn(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns[i0]) + c := n.Child("extra_columns") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns { + e := c.Elem(i) + enumPostgresSyncedTableSyncedTableSpecExtraColumn(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns[i]) } } - if c := n.child("new_pipeline_spec"); c.exists() { - enumPostgresNewPipelineSpec(w, &c, v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.NewPipelineSpec) + if x := v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.NewPipelineSpec; x != nil { + c := n.Child("new_pipeline_spec") + enumPostgresNewPipelineSpec(w, &c, x) + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SchedulingPolicy); s != "" || walk.Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "SchedulingPolicy") { + c := n.Child("scheduling_policy") + w.enumValue(&c, s, enumValues21) } - w.enumField(n, "scheduling_policy", string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SchedulingPolicy), enumValuesPostgresSyncedTableSyncedTableSpecSyncedTableSchedulingPolicy) if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides) > 0 { - c := n.child("type_overrides") - for i0 := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides { - e := c.elem(i0) - enumPostgresSyncedTableSyncedTableSpecTypeOverride(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides[i0]) + c := n.Child("type_overrides") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides { + e := c.Elem(i) + enumPostgresSyncedTableSyncedTableSpecTypeOverride(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides[i]) } } } -func enumResourcesQualityMonitor(w *walker, n *node, v *resources.QualityMonitor) { +func enumResourcesQualityMonitor(w *walker, n *walk.Node, v *resources.QualityMonitor) { if v == nil { - v = &resources.QualityMonitor{} + return } if len(v.CreateMonitor.CustomMetrics) > 0 { - c := n.child("custom_metrics") - for i0 := range v.CreateMonitor.CustomMetrics { - e := c.elem(i0) - enumCatalogMonitorMetric(w, &e, &v.CreateMonitor.CustomMetrics[i0]) + c := n.Child("custom_metrics") + for i := range v.CreateMonitor.CustomMetrics { + e := c.Elem(i) + enumCatalogMonitorMetric(w, &e, &v.CreateMonitor.CustomMetrics[i]) } } - if c := n.child("inference_log"); c.exists() { - enumCatalogMonitorInferenceLog(w, &c, v.CreateMonitor.InferenceLog) + if x := v.CreateMonitor.InferenceLog; x != nil { + c := n.Child("inference_log") + enumCatalogMonitorInferenceLog(w, &c, x) } - if c := n.child("schedule"); c.exists() { - enumCatalogMonitorCronSchedule(w, &c, v.CreateMonitor.Schedule) + if x := v.CreateMonitor.Schedule; x != nil { + c := n.Child("schedule") + enumCatalogMonitorCronSchedule(w, &c, x) } } -func enumResourcesRegisteredModel(w *walker, n *node, v *resources.RegisteredModel) { +func enumResourcesRegisteredModel(w *walker, n *walk.Node, v *resources.RegisteredModel) { if v == nil { - v = &resources.RegisteredModel{} + return } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesSchema(w *walker, n *node, v *resources.Schema) { +func enumResourcesSchema(w *walker, n *walk.Node, v *resources.Schema) { if v == nil { - v = &resources.Schema{} + return } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesSecretScope(w *walker, n *node, v *resources.SecretScope) { +func enumResourcesSecretScope(w *walker, n *walk.Node, v *resources.SecretScope) { if v == nil { - v = &resources.SecretScope{} + return + } + if s := string(v.BackendType); s != "" { + c := n.Child("backend_type") + w.enumValue(&c, s, enumValues22) } - w.enumField(n, "backend_type", string(v.BackendType), enumValuesWorkspaceScopeBackendType) if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesSecretScopePermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesSecretScopePermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesSecret(w *walker, n *node, v *resources.Secret) { +func enumResourcesSecret(w *walker, n *walk.Node, v *resources.Secret) { if v == nil { - v = &resources.Secret{} + return } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } } -func enumResourcesSqlWarehouse(w *walker, n *node, v *resources.SqlWarehouse) { +func enumResourcesSqlWarehouse(w *walker, n *walk.Node, v *resources.SqlWarehouse) { if v == nil { - v = &resources.SqlWarehouse{} + return } - if c := n.child("channel"); c.exists() { - enumSqlChannel(w, &c, v.CreateWarehouseRequest.Channel) + if x := v.CreateWarehouseRequest.Channel; x != nil { + c := n.Child("channel") + enumSqlChannel(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesSqlWarehousePermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesSqlWarehousePermission(w, &e, &v.Permissions[i]) } } - w.enumField(n, "spot_instance_policy", string(v.CreateWarehouseRequest.SpotInstancePolicy), enumValuesSqlSpotInstancePolicy) - w.enumField(n, "warehouse_type", string(v.CreateWarehouseRequest.WarehouseType), enumValuesSqlCreateWarehouseRequestWarehouseType) + if s := string(v.CreateWarehouseRequest.SpotInstancePolicy); s != "" || walk.Forced(v.CreateWarehouseRequest.ForceSendFields, "SpotInstancePolicy") { + c := n.Child("spot_instance_policy") + w.enumValue(&c, s, enumValues23) + } + if s := string(v.CreateWarehouseRequest.WarehouseType); s != "" || walk.Forced(v.CreateWarehouseRequest.ForceSendFields, "WarehouseType") { + c := n.Child("warehouse_type") + w.enumValue(&c, s, enumValues24) + } } -func enumResourcesSyncedDatabaseTable(w *walker, n *node, v *resources.SyncedDatabaseTable) { +func enumResourcesSyncedDatabaseTable(w *walker, n *walk.Node, v *resources.SyncedDatabaseTable) { if v == nil { - v = &resources.SyncedDatabaseTable{} + return } - if c := n.child("data_synchronization_status"); c.exists() { - enumDatabaseSyncedTableStatus(w, &c, v.SyncedDatabaseTable.DataSynchronizationStatus) + if x := v.SyncedDatabaseTable.DataSynchronizationStatus; x != nil { + c := n.Child("data_synchronization_status") + enumDatabaseSyncedTableStatus(w, &c, x) } - if c := n.child("spec"); c.exists() { - enumDatabaseSyncedTableSpec(w, &c, v.SyncedDatabaseTable.Spec) + if x := v.SyncedDatabaseTable.Spec; x != nil { + c := n.Child("spec") + enumDatabaseSyncedTableSpec(w, &c, x) + } + if s := string(v.SyncedDatabaseTable.UnityCatalogProvisioningState); s != "" || walk.Forced(v.SyncedDatabaseTable.ForceSendFields, "UnityCatalogProvisioningState") { + c := n.Child("unity_catalog_provisioning_state") + w.enumValue(&c, s, enumValues25) } - w.enumField(n, "unity_catalog_provisioning_state", string(v.SyncedDatabaseTable.UnityCatalogProvisioningState), enumValuesDatabaseProvisioningInfoState) } -func enumResourcesVectorSearchEndpoint(w *walker, n *node, v *resources.VectorSearchEndpoint) { +func enumResourcesVectorSearchEndpoint(w *walker, n *walk.Node, v *resources.VectorSearchEndpoint) { if v == nil { - v = &resources.VectorSearchEndpoint{} + return + } + if s := string(v.CreateEndpoint.EndpointType); s != "" || walk.Forced(v.CreateEndpoint.ForceSendFields, "EndpointType") { + c := n.Child("endpoint_type") + w.enumValue(&c, s, enumValues26) } - w.enumField(n, "endpoint_type", string(v.CreateEndpoint.EndpointType), enumValuesVectorsearchEndpointType) if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - enumResourcesVectorSearchEndpointPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + enumResourcesVectorSearchEndpointPermission(w, &e, &v.Permissions[i]) } } } -func enumResourcesVectorSearchIndex(w *walker, n *node, v *resources.VectorSearchIndex) { +func enumResourcesVectorSearchIndex(w *walker, n *walk.Node, v *resources.VectorSearchIndex) { if v == nil { - v = &resources.VectorSearchIndex{} + return } - if c := n.child("delta_sync_index_spec"); c.exists() { - enumVectorsearchDeltaSyncVectorIndexSpecRequest(w, &c, v.CreateVectorIndexRequest.DeltaSyncIndexSpec) + if x := v.CreateVectorIndexRequest.DeltaSyncIndexSpec; x != nil { + c := n.Child("delta_sync_index_spec") + enumVectorsearchDeltaSyncVectorIndexSpecRequest(w, &c, x) } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } - w.enumField(n, "index_subtype", string(v.CreateVectorIndexRequest.IndexSubtype), enumValuesVectorsearchIndexSubtype) - w.enumField(n, "index_type", string(v.CreateVectorIndexRequest.IndexType), enumValuesVectorsearchVectorIndexType) + if s := string(v.CreateVectorIndexRequest.IndexSubtype); s != "" { + c := n.Child("index_subtype") + w.enumValue(&c, s, enumValues27) + } + if s := string(v.CreateVectorIndexRequest.IndexType); s != "" { + c := n.Child("index_type") + w.enumValue(&c, s, enumValues28) + } } -func enumResourcesVolume(w *walker, n *node, v *resources.Volume) { +func enumResourcesVolume(w *walker, n *walk.Node, v *resources.Volume) { if v == nil { - v = &resources.Volume{} + return } if len(v.Grants) > 0 { - c := n.child("grants") - for i0 := range v.Grants { - e := c.elem(i0) - enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i0]) + c := n.Child("grants") + for i := range v.Grants { + e := c.Elem(i) + enumCatalogPrivilegeAssignment(w, &e, &v.Grants[i]) } } - w.enumField(n, "volume_type", string(v.CreateVolumeRequestContent.VolumeType), enumValuesCatalogVolumeType) + if s := string(v.CreateVolumeRequestContent.VolumeType); s != "" || walk.Forced(v.CreateVolumeRequestContent.ForceSendFields, "VolumeType") { + c := n.Child("volume_type") + w.enumValue(&c, s, enumValues29) + } } -func enumSqlAlertV2Evaluation(w *walker, n *node, v *sql.AlertV2Evaluation) { +func enumSqlAlertV2Evaluation(w *walker, n *walk.Node, v *sql.AlertV2Evaluation) { if v == nil { - v = &sql.AlertV2Evaluation{} + return } - w.enumField(n, "comparison_operator", string(v.ComparisonOperator), enumValuesSqlComparisonOperator) - w.enumField(n, "empty_result_state", string(v.EmptyResultState), enumValuesSqlAlertEvaluationState) - if c := n.child("source"); c.exists() { - enumSqlAlertV2OperandColumn(w, &c, &v.Source) + if s := string(v.ComparisonOperator); s != "" || walk.Forced(v.ForceSendFields, "ComparisonOperator") { + c := n.Child("comparison_operator") + w.enumValue(&c, s, enumValues30) } - w.enumField(n, "state", string(v.State), enumValuesSqlAlertEvaluationState) - if c := n.child("threshold"); c.exists() { - enumSqlAlertV2Operand(w, &c, v.Threshold) + if s := string(v.EmptyResultState); s != "" || walk.Forced(v.ForceSendFields, "EmptyResultState") { + c := n.Child("empty_result_state") + w.enumValue(&c, s, enumValues31) + } + if x := &v.Source; !walk.IsZeroStruct(x) || walk.Forced(v.ForceSendFields, "Source") || n.HasRefs("source") { + c := n.Child("source") + enumSqlAlertV2OperandColumn(w, &c, x) + } + if s := string(v.State); s != "" || walk.Forced(v.ForceSendFields, "State") { + c := n.Child("state") + w.enumValue(&c, s, enumValues31) + } + if x := v.Threshold; x != nil { + c := n.Child("threshold") + enumSqlAlertV2Operand(w, &c, x) } } -func enumSqlCronSchedule(w *walker, n *node, v *sql.CronSchedule) { +func enumSqlCronSchedule(w *walker, n *walk.Node, v *sql.CronSchedule) { if v == nil { - v = &sql.CronSchedule{} + return + } + if s := string(v.PauseStatus); s != "" { + c := n.Child("pause_status") + w.enumValue(&c, s, enumValues32) } - w.enumField(n, "pause_status", string(v.PauseStatus), enumValuesSqlSchedulePauseStatus) } -func enumAppsAppDeployment(w *walker, n *node, v *apps.AppDeployment) { +func enumAppsAppDeployment(w *walker, n *walk.Node, v *apps.AppDeployment) { if v == nil { - v = &apps.AppDeployment{} + return + } + if s := string(v.Mode); s != "" || walk.Forced(v.ForceSendFields, "Mode") { + c := n.Child("mode") + w.enumValue(&c, s, enumValues33) } - w.enumField(n, "mode", string(v.Mode), enumValuesAppsAppDeploymentMode) - if c := n.child("status"); c.exists() { - enumAppsAppDeploymentStatus(w, &c, v.Status) + if x := v.Status; x != nil { + c := n.Child("status") + enumAppsAppDeploymentStatus(w, &c, x) } } -func enumAppsApplicationStatus(w *walker, n *node, v *apps.ApplicationStatus) { +func enumAppsApplicationStatus(w *walker, n *walk.Node, v *apps.ApplicationStatus) { if v == nil { - v = &apps.ApplicationStatus{} + return + } + if s := string(v.State); s != "" || walk.Forced(v.ForceSendFields, "State") { + c := n.Child("state") + w.enumValue(&c, s, enumValues34) } - w.enumField(n, "state", string(v.State), enumValuesAppsApplicationState) } -func enumAppsComputeStatus(w *walker, n *node, v *apps.ComputeStatus) { +func enumAppsComputeStatus(w *walker, n *walk.Node, v *apps.ComputeStatus) { if v == nil { - v = &apps.ComputeStatus{} + return + } + if s := string(v.State); s != "" || walk.Forced(v.ForceSendFields, "State") { + c := n.Child("state") + w.enumValue(&c, s, enumValues35) } - w.enumField(n, "state", string(v.State), enumValuesAppsComputeState) } -func enumResourcesAppPermission(w *walker, n *node, v *resources.AppPermission) { +func enumResourcesAppPermission(w *walker, n *walk.Node, v *resources.AppPermission) { if v == nil { - v = &resources.AppPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues36) } - w.enumField(n, "level", string(v.Level), enumValuesAppsAppPermissionLevel) } -func enumAppsAppResource(w *walker, n *node, v *apps.AppResource) { +func enumAppsAppResource(w *walker, n *walk.Node, v *apps.AppResource) { if v == nil { - v = &apps.AppResource{} + return } - if c := n.child("app"); c.exists() { - enumAppsAppResourceApp(w, &c, v.App) + if x := v.App; x != nil { + c := n.Child("app") + enumAppsAppResourceApp(w, &c, x) } - if c := n.child("database"); c.exists() { - enumAppsAppResourceDatabase(w, &c, v.Database) + if x := v.Database; x != nil { + c := n.Child("database") + enumAppsAppResourceDatabase(w, &c, x) } - if c := n.child("experiment"); c.exists() { - enumAppsAppResourceExperiment(w, &c, v.Experiment) + if x := v.Experiment; x != nil { + c := n.Child("experiment") + enumAppsAppResourceExperiment(w, &c, x) } - if c := n.child("genie_space"); c.exists() { - enumAppsAppResourceGenieSpace(w, &c, v.GenieSpace) + if x := v.GenieSpace; x != nil { + c := n.Child("genie_space") + enumAppsAppResourceGenieSpace(w, &c, x) } - if c := n.child("job"); c.exists() { - enumAppsAppResourceJob(w, &c, v.Job) + if x := v.Job; x != nil { + c := n.Child("job") + enumAppsAppResourceJob(w, &c, x) } - if c := n.child("postgres"); c.exists() { - enumAppsAppResourcePostgres(w, &c, v.Postgres) + if x := v.Postgres; x != nil { + c := n.Child("postgres") + enumAppsAppResourcePostgres(w, &c, x) } - if c := n.child("secret"); c.exists() { - enumAppsAppResourceSecret(w, &c, v.Secret) + if x := v.Secret; x != nil { + c := n.Child("secret") + enumAppsAppResourceSecret(w, &c, x) } - if c := n.child("serving_endpoint"); c.exists() { - enumAppsAppResourceServingEndpoint(w, &c, v.ServingEndpoint) + if x := v.ServingEndpoint; x != nil { + c := n.Child("serving_endpoint") + enumAppsAppResourceServingEndpoint(w, &c, x) } - if c := n.child("sql_warehouse"); c.exists() { - enumAppsAppResourceSqlWarehouse(w, &c, v.SqlWarehouse) + if x := v.SqlWarehouse; x != nil { + c := n.Child("sql_warehouse") + enumAppsAppResourceSqlWarehouse(w, &c, x) } - if c := n.child("uc_securable"); c.exists() { - enumAppsAppResourceUcSecurable(w, &c, v.UcSecurable) + if x := v.UcSecurable; x != nil { + c := n.Child("uc_securable") + enumAppsAppResourceUcSecurable(w, &c, x) } } -func enumCatalogPrivilegeAssignment(w *walker, n *node, v *catalog.PrivilegeAssignment) { +func enumCatalogPrivilegeAssignment(w *walker, n *walk.Node, v *catalog.PrivilegeAssignment) { if v == nil { - v = &catalog.PrivilegeAssignment{} + return } if len(v.Privileges) > 0 { - c := n.child("privileges") - for i0 := range v.Privileges { - e := c.elem(i0) - w.enumElem(&e, string(v.Privileges[i0]), enumValuesCatalogPrivilege) + c := n.Child("privileges") + for i := range v.Privileges { + e := c.Elem(i) + w.enumValue(&e, string(v.Privileges[i]), enumValues37) } } } -func enumResourcesClusterPolicyPermission(w *walker, n *node, v *resources.ClusterPolicyPermission) { +func enumResourcesClusterPolicyPermission(w *walker, n *walk.Node, v *resources.ClusterPolicyPermission) { if v == nil { - v = &resources.ClusterPolicyPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues38) } - w.enumField(n, "level", string(v.Level), enumValuesComputeClusterPolicyPermissionLevel) } -func enumComputeAwsAttributes(w *walker, n *node, v *compute.AwsAttributes) { +func enumComputeAwsAttributes(w *walker, n *walk.Node, v *compute.AwsAttributes) { if v == nil { - v = &compute.AwsAttributes{} + return + } + if s := string(v.Availability); s != "" || walk.Forced(v.ForceSendFields, "Availability") { + c := n.Child("availability") + w.enumValue(&c, s, enumValues39) + } + if s := string(v.EbsVolumeType); s != "" || walk.Forced(v.ForceSendFields, "EbsVolumeType") { + c := n.Child("ebs_volume_type") + w.enumValue(&c, s, enumValues40) } - w.enumField(n, "availability", string(v.Availability), enumValuesComputeAwsAvailability) - w.enumField(n, "ebs_volume_type", string(v.EbsVolumeType), enumValuesComputeEbsVolumeType) } -func enumComputeAzureAttributes(w *walker, n *node, v *compute.AzureAttributes) { +func enumComputeAzureAttributes(w *walker, n *walk.Node, v *compute.AzureAttributes) { if v == nil { - v = &compute.AzureAttributes{} + return + } + if s := string(v.Availability); s != "" || walk.Forced(v.ForceSendFields, "Availability") { + c := n.Child("availability") + w.enumValue(&c, s, enumValues41) } - w.enumField(n, "availability", string(v.Availability), enumValuesComputeAzureAvailability) } -func enumComputeGcpAttributes(w *walker, n *node, v *compute.GcpAttributes) { +func enumComputeGcpAttributes(w *walker, n *walk.Node, v *compute.GcpAttributes) { if v == nil { - v = &compute.GcpAttributes{} + return + } + if s := string(v.Availability); s != "" || walk.Forced(v.ForceSendFields, "Availability") { + c := n.Child("availability") + w.enumValue(&c, s, enumValues42) + } + if s := string(v.ConfidentialComputeType); s != "" || walk.Forced(v.ForceSendFields, "ConfidentialComputeType") { + c := n.Child("confidential_compute_type") + w.enumValue(&c, s, enumValues43) } - w.enumField(n, "availability", string(v.Availability), enumValuesComputeGcpAvailability) - w.enumField(n, "confidential_compute_type", string(v.ConfidentialComputeType), enumValuesComputeConfidentialComputeType) } -func enumResourcesClusterPermission(w *walker, n *node, v *resources.ClusterPermission) { +func enumResourcesClusterPermission(w *walker, n *walk.Node, v *resources.ClusterPermission) { if v == nil { - v = &resources.ClusterPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues44) } - w.enumField(n, "level", string(v.Level), enumValuesComputeClusterPermissionLevel) } -func enumResourcesMlflowExperimentPermission(w *walker, n *node, v *resources.MlflowExperimentPermission) { +func enumResourcesMlflowExperimentPermission(w *walker, n *walk.Node, v *resources.MlflowExperimentPermission) { if v == nil { - v = &resources.MlflowExperimentPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues45) } - w.enumField(n, "level", string(v.Level), enumValuesMlExperimentPermissionLevel) } -func enumCatalogEncryptionDetails(w *walker, n *node, v *catalog.EncryptionDetails) { +func enumCatalogEncryptionDetails(w *walker, n *walk.Node, v *catalog.EncryptionDetails) { if v == nil { - v = &catalog.EncryptionDetails{} + return } - if c := n.child("sse_encryption_details"); c.exists() { - enumCatalogSseEncryptionDetails(w, &c, v.SseEncryptionDetails) + if x := v.SseEncryptionDetails; x != nil { + c := n.Child("sse_encryption_details") + enumCatalogSseEncryptionDetails(w, &c, x) } } -func enumMlFunction(w *walker, n *node, v *ml.Function) { +func enumMlFunction(w *walker, n *walk.Node, v *ml.Function) { if v == nil { - v = &ml.Function{} + return + } + if s := string(v.FunctionType); s != "" { + c := n.Child("function_type") + w.enumValue(&c, s, enumValues46) } - w.enumField(n, "function_type", string(v.FunctionType), enumValuesMlFunctionFunctionType) } -func enumMlDataSource(w *walker, n *node, v *ml.DataSource) { +func enumMlDataSource(w *walker, n *walk.Node, v *ml.DataSource) { if v == nil { - v = &ml.DataSource{} + return } - if c := n.child("request_source"); c.exists() { - enumMlRequestSource(w, &c, v.RequestSource) + if x := v.RequestSource; x != nil { + c := n.Child("request_source") + enumMlRequestSource(w, &c, x) } - if c := n.child("sdp_source"); c.exists() { - enumMlSdpSource(w, &c, v.SdpSource) + if x := v.SdpSource; x != nil { + c := n.Child("sdp_source") + enumMlSdpSource(w, &c, x) } } -func enumComputeInstancePoolAwsAttributes(w *walker, n *node, v *compute.InstancePoolAwsAttributes) { +func enumComputeInstancePoolAwsAttributes(w *walker, n *walk.Node, v *compute.InstancePoolAwsAttributes) { if v == nil { - v = &compute.InstancePoolAwsAttributes{} + return + } + if s := string(v.Availability); s != "" || walk.Forced(v.ForceSendFields, "Availability") { + c := n.Child("availability") + w.enumValue(&c, s, enumValues47) } - w.enumField(n, "availability", string(v.Availability), enumValuesComputeInstancePoolAwsAttributesAvailability) } -func enumComputeInstancePoolAzureAttributes(w *walker, n *node, v *compute.InstancePoolAzureAttributes) { +func enumComputeInstancePoolAzureAttributes(w *walker, n *walk.Node, v *compute.InstancePoolAzureAttributes) { if v == nil { - v = &compute.InstancePoolAzureAttributes{} + return + } + if s := string(v.Availability); s != "" || walk.Forced(v.ForceSendFields, "Availability") { + c := n.Child("availability") + w.enumValue(&c, s, enumValues48) } - w.enumField(n, "availability", string(v.Availability), enumValuesComputeInstancePoolAzureAttributesAvailability) } -func enumComputeDiskSpec(w *walker, n *node, v *compute.DiskSpec) { +func enumComputeDiskSpec(w *walker, n *walk.Node, v *compute.DiskSpec) { if v == nil { - v = &compute.DiskSpec{} + return } - if c := n.child("disk_type"); c.exists() { - enumComputeDiskType(w, &c, v.DiskType) + if x := v.DiskType; x != nil { + c := n.Child("disk_type") + enumComputeDiskType(w, &c, x) } } -func enumComputeInstancePoolGcpAttributes(w *walker, n *node, v *compute.InstancePoolGcpAttributes) { +func enumComputeInstancePoolGcpAttributes(w *walker, n *walk.Node, v *compute.InstancePoolGcpAttributes) { if v == nil { - v = &compute.InstancePoolGcpAttributes{} + return + } + if s := string(v.GcpAvailability); s != "" || walk.Forced(v.ForceSendFields, "GcpAvailability") { + c := n.Child("gcp_availability") + w.enumValue(&c, s, enumValues42) } - w.enumField(n, "gcp_availability", string(v.GcpAvailability), enumValuesComputeGcpAvailability) } -func enumResourcesInstancePoolPermission(w *walker, n *node, v *resources.InstancePoolPermission) { +func enumResourcesInstancePoolPermission(w *walker, n *walk.Node, v *resources.InstancePoolPermission) { if v == nil { - v = &resources.InstancePoolPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues49) } - w.enumField(n, "level", string(v.Level), enumValuesComputeInstancePoolPermissionLevel) } -func enumJobsContinuous(w *walker, n *node, v *jobs.Continuous) { +func enumJobsContinuous(w *walker, n *walk.Node, v *jobs.Continuous) { if v == nil { - v = &jobs.Continuous{} + return + } + if x := v.MaintenanceWindow; x != nil { + c := n.Child("maintenance_window") + enumJobsMaintenanceWindow(w, &c, x) } - if c := n.child("maintenance_window"); c.exists() { - enumJobsMaintenanceWindow(w, &c, v.MaintenanceWindow) + if s := string(v.PauseStatus); s != "" { + c := n.Child("pause_status") + w.enumValue(&c, s, enumValues32) + } + if s := string(v.TaskRetryMode); s != "" { + c := n.Child("task_retry_mode") + w.enumValue(&c, s, enumValues50) } - w.enumField(n, "pause_status", string(v.PauseStatus), enumValuesJobsPauseStatus) - w.enumField(n, "task_retry_mode", string(v.TaskRetryMode), enumValuesJobsTaskRetryMode) } -func enumJobsJobDeployment(w *walker, n *node, v *jobs.JobDeployment) { +func enumJobsJobDeployment(w *walker, n *walk.Node, v *jobs.JobDeployment) { if v == nil { - v = &jobs.JobDeployment{} + return + } + if s := string(v.Kind); s != "" || walk.Forced(v.ForceSendFields, "Kind") { + c := n.Child("kind") + w.enumValue(&c, s, enumValues51) } - w.enumField(n, "kind", string(v.Kind), enumValuesJobsJobDeploymentKind) } -func enumJobsGitSource(w *walker, n *node, v *jobs.GitSource) { +func enumJobsGitSource(w *walker, n *walk.Node, v *jobs.GitSource) { if v == nil { - v = &jobs.GitSource{} + return + } + if s := string(v.GitProvider); s != "" || walk.Forced(v.ForceSendFields, "GitProvider") { + c := n.Child("git_provider") + w.enumValue(&c, s, enumValues52) } - w.enumField(n, "git_provider", string(v.GitProvider), enumValuesJobsGitProvider) - if c := n.child("job_source"); c.exists() { - enumJobsJobSource(w, &c, v.JobSource) + if x := v.JobSource; x != nil { + c := n.Child("job_source") + enumJobsJobSource(w, &c, x) } } -func enumJobsJobsHealthRules(w *walker, n *node, v *jobs.JobsHealthRules) { +func enumJobsJobsHealthRules(w *walker, n *walk.Node, v *jobs.JobsHealthRules) { if v == nil { - v = &jobs.JobsHealthRules{} + return } if len(v.Rules) > 0 { - c := n.child("rules") - for i0 := range v.Rules { - e := c.elem(i0) - enumJobsJobsHealthRule(w, &e, &v.Rules[i0]) + c := n.Child("rules") + for i := range v.Rules { + e := c.Elem(i) + enumJobsJobsHealthRule(w, &e, &v.Rules[i]) } } } -func enumJobsJobCluster(w *walker, n *node, v *jobs.JobCluster) { +func enumJobsJobCluster(w *walker, n *walk.Node, v *jobs.JobCluster) { if v == nil { - v = &jobs.JobCluster{} + return } - if c := n.child("new_cluster"); c.exists() { - enumComputeClusterSpec(w, &c, v.NewCluster) + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + enumComputeClusterSpec(w, &c, x) } } -func enumResourcesJobPermission(w *walker, n *node, v *resources.JobPermission) { +func enumResourcesJobPermission(w *walker, n *walk.Node, v *resources.JobPermission) { if v == nil { - v = &resources.JobPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues53) } - w.enumField(n, "level", string(v.Level), enumValuesJobsJobPermissionLevel) } -func enumJobsCronSchedule(w *walker, n *node, v *jobs.CronSchedule) { +func enumJobsCronSchedule(w *walker, n *walk.Node, v *jobs.CronSchedule) { if v == nil { - v = &jobs.CronSchedule{} + return + } + if s := string(v.PauseStatus); s != "" { + c := n.Child("pause_status") + w.enumValue(&c, s, enumValues32) } - w.enumField(n, "pause_status", string(v.PauseStatus), enumValuesJobsPauseStatus) - if c := n.child("sql_condition"); c.exists() { - enumJobsSqlConditionConfiguration(w, &c, v.SqlCondition) + if x := v.SqlCondition; x != nil { + c := n.Child("sql_condition") + enumJobsSqlConditionConfiguration(w, &c, x) } } -func enumJobsTask(w *walker, n *node, v *jobs.Task) { +func enumJobsTask(w *walker, n *walk.Node, v *jobs.Task) { if v == nil { - v = &jobs.Task{} + return + } + if x := v.AiRuntimeTask; x != nil { + c := n.Child("ai_runtime_task") + enumJobsAiRuntimeTask(w, &c, x) } - if c := n.child("ai_runtime_task"); c.exists() { - enumJobsAiRuntimeTask(w, &c, v.AiRuntimeTask) + if x := v.Compute; x != nil { + c := n.Child("compute") + enumJobsCompute(w, &c, x) } - if c := n.child("compute"); c.exists() { - enumJobsCompute(w, &c, v.Compute) + if x := v.ConditionTask; x != nil { + c := n.Child("condition_task") + enumJobsConditionTask(w, &c, x) } - if c := n.child("condition_task"); c.exists() { - enumJobsConditionTask(w, &c, v.ConditionTask) + if x := v.DashboardTask; x != nil { + c := n.Child("dashboard_task") + enumJobsDashboardTask(w, &c, x) } - if c := n.child("dashboard_task"); c.exists() { - enumJobsDashboardTask(w, &c, v.DashboardTask) + if x := v.DbtTask; x != nil { + c := n.Child("dbt_task") + enumJobsDbtTask(w, &c, x) } - if c := n.child("dbt_task"); c.exists() { - enumJobsDbtTask(w, &c, v.DbtTask) + if x := v.ForEachTask; x != nil { + c := n.Child("for_each_task") + enumJobsForEachTask(w, &c, x) } - if c := n.child("for_each_task"); c.exists() { - enumJobsForEachTask(w, &c, v.ForEachTask) + if x := v.GenAiComputeTask; x != nil { + c := n.Child("gen_ai_compute_task") + enumJobsGenAiComputeTask(w, &c, x) } - if c := n.child("gen_ai_compute_task"); c.exists() { - enumJobsGenAiComputeTask(w, &c, v.GenAiComputeTask) + if x := v.Health; x != nil { + c := n.Child("health") + enumJobsJobsHealthRules(w, &c, x) } - if c := n.child("health"); c.exists() { - enumJobsJobsHealthRules(w, &c, v.Health) + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + enumComputeClusterSpec(w, &c, x) } - if c := n.child("new_cluster"); c.exists() { - enumComputeClusterSpec(w, &c, v.NewCluster) + if x := v.NotebookTask; x != nil { + c := n.Child("notebook_task") + enumJobsNotebookTask(w, &c, x) } - if c := n.child("notebook_task"); c.exists() { - enumJobsNotebookTask(w, &c, v.NotebookTask) + if x := v.PowerBiTask; x != nil { + c := n.Child("power_bi_task") + enumJobsPowerBiTask(w, &c, x) } - if c := n.child("power_bi_task"); c.exists() { - enumJobsPowerBiTask(w, &c, v.PowerBiTask) + if s := string(v.RunIf); s != "" || walk.Forced(v.ForceSendFields, "RunIf") { + c := n.Child("run_if") + w.enumValue(&c, s, enumValues54) } - w.enumField(n, "run_if", string(v.RunIf), enumValuesJobsRunIf) - if c := n.child("spark_python_task"); c.exists() { - enumJobsSparkPythonTask(w, &c, v.SparkPythonTask) + if x := v.SparkPythonTask; x != nil { + c := n.Child("spark_python_task") + enumJobsSparkPythonTask(w, &c, x) } - if c := n.child("sql_task"); c.exists() { - enumJobsSqlTask(w, &c, v.SqlTask) + if x := v.SqlTask; x != nil { + c := n.Child("sql_task") + enumJobsSqlTask(w, &c, x) } } -func enumJobsTriggerSettings(w *walker, n *node, v *jobs.TriggerSettings) { +func enumJobsTriggerSettings(w *walker, n *walk.Node, v *jobs.TriggerSettings) { if v == nil { - v = &jobs.TriggerSettings{} + return } - if c := n.child("model"); c.exists() { - enumJobsModelTriggerConfiguration(w, &c, v.Model) + if x := v.Model; x != nil { + c := n.Child("model") + enumJobsModelTriggerConfiguration(w, &c, x) } - w.enumField(n, "pause_status", string(v.PauseStatus), enumValuesJobsPauseStatus) - if c := n.child("periodic"); c.exists() { - enumJobsPeriodicTriggerConfiguration(w, &c, v.Periodic) + if s := string(v.PauseStatus); s != "" { + c := n.Child("pause_status") + w.enumValue(&c, s, enumValues32) } - if c := n.child("sql_condition"); c.exists() { - enumJobsSqlConditionConfiguration(w, &c, v.SqlCondition) + if x := v.Periodic; x != nil { + c := n.Child("periodic") + enumJobsPeriodicTriggerConfiguration(w, &c, x) } - if c := n.child("table_update"); c.exists() { - enumJobsTableUpdateTriggerConfiguration(w, &c, v.TableUpdate) + if x := v.SqlCondition; x != nil { + c := n.Child("sql_condition") + enumJobsSqlConditionConfiguration(w, &c, x) + } + if x := v.TableUpdate; x != nil { + c := n.Child("table_update") + enumJobsTableUpdateTriggerConfiguration(w, &c, x) } } -func enumJobsTriggerConfiguration(w *walker, n *node, v *jobs.TriggerConfiguration) { +func enumJobsTriggerConfiguration(w *walker, n *walk.Node, v *jobs.TriggerConfiguration) { if v == nil { - v = &jobs.TriggerConfiguration{} + return + } + if x := v.Continuous; x != nil { + c := n.Child("continuous") + enumJobsContinuousTriggerConfiguration(w, &c, x) } - if c := n.child("continuous"); c.exists() { - enumJobsContinuousTriggerConfiguration(w, &c, v.Continuous) + if x := v.Model; x != nil { + c := n.Child("model") + enumJobsModelTriggerConfiguration(w, &c, x) } - if c := n.child("model"); c.exists() { - enumJobsModelTriggerConfiguration(w, &c, v.Model) + if s := string(v.PauseStatus); s != "" { + c := n.Child("pause_status") + w.enumValue(&c, s, enumValues32) } - w.enumField(n, "pause_status", string(v.PauseStatus), enumValuesJobsPauseStatus) - if c := n.child("periodic"); c.exists() { - enumJobsPeriodicTriggerConfiguration(w, &c, v.Periodic) + if x := v.Periodic; x != nil { + c := n.Child("periodic") + enumJobsPeriodicTriggerConfiguration(w, &c, x) } - if c := n.child("sql_condition"); c.exists() { - enumJobsSqlConditionConfiguration(w, &c, v.SqlCondition) + if x := v.SqlCondition; x != nil { + c := n.Child("sql_condition") + enumJobsSqlConditionConfiguration(w, &c, x) } - if c := n.child("table_update"); c.exists() { - enumJobsTableUpdateTriggerConfiguration(w, &c, v.TableUpdate) + if x := v.TableUpdate; x != nil { + c := n.Child("table_update") + enumJobsTableUpdateTriggerConfiguration(w, &c, x) } } -func enumCatalogMcpServiceConfig(w *walker, n *node, v *catalog.McpServiceConfig) { +func enumCatalogMcpServiceConfig(w *walker, n *walk.Node, v *catalog.McpServiceConfig) { if v == nil { - v = &catalog.McpServiceConfig{} + return } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - enumCatalogRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + enumCatalogRateLimit(w, &e, &v.RateLimits[i]) } } } -func enumCatalogModelProviderServiceConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfig) { +func enumCatalogModelProviderServiceConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfig{} + return + } + if s := string(v.ProviderType); s != "" || walk.Forced(v.ForceSendFields, "ProviderType") { + c := n.Child("provider_type") + w.enumValue(&c, s, enumValues55) } - w.enumField(n, "provider_type", string(v.ProviderType), enumValuesCatalogModelProviderServiceConfigExternalModelProviderType) if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - enumCatalogRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + enumCatalogRateLimit(w, &e, &v.RateLimits[i]) } } } -func enumCatalogModelServiceConfig(w *walker, n *node, v *catalog.ModelServiceConfig) { +func enumCatalogModelServiceConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfig) { if v == nil { - v = &catalog.ModelServiceConfig{} + return } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - enumCatalogRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + enumCatalogRateLimit(w, &e, &v.RateLimits[i]) } } - if c := n.child("routing"); c.exists() { - enumCatalogModelServiceConfigRoutingConfig(w, &c, v.Routing) + if x := v.Routing; x != nil { + c := n.Child("routing") + enumCatalogModelServiceConfigRoutingConfig(w, &c, x) } } -func enumServingAiGatewayConfig(w *walker, n *node, v *serving.AiGatewayConfig) { +func enumServingAiGatewayConfig(w *walker, n *walk.Node, v *serving.AiGatewayConfig) { if v == nil { - v = &serving.AiGatewayConfig{} + return } - if c := n.child("guardrails"); c.exists() { - enumServingAiGatewayGuardrails(w, &c, v.Guardrails) + if x := v.Guardrails; x != nil { + c := n.Child("guardrails") + enumServingAiGatewayGuardrails(w, &c, x) } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - enumServingAiGatewayRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + enumServingAiGatewayRateLimit(w, &e, &v.RateLimits[i]) } } } -func enumServingEndpointCoreConfigInput(w *walker, n *node, v *serving.EndpointCoreConfigInput) { +func enumServingEndpointCoreConfigInput(w *walker, n *walk.Node, v *serving.EndpointCoreConfigInput) { if v == nil { - v = &serving.EndpointCoreConfigInput{} + return } if len(v.ServedEntities) > 0 { - c := n.child("served_entities") - for i0 := range v.ServedEntities { - e := c.elem(i0) - enumServingServedEntityInput(w, &e, &v.ServedEntities[i0]) + c := n.Child("served_entities") + for i := range v.ServedEntities { + e := c.Elem(i) + enumServingServedEntityInput(w, &e, &v.ServedEntities[i]) } } if len(v.ServedModels) > 0 { - c := n.child("served_models") - for i0 := range v.ServedModels { - e := c.elem(i0) - enumServingServedModelInput(w, &e, &v.ServedModels[i0]) + c := n.Child("served_models") + for i := range v.ServedModels { + e := c.Elem(i) + enumServingServedModelInput(w, &e, &v.ServedModels[i]) } } } -func enumResourcesModelServingEndpointPermission(w *walker, n *node, v *resources.ModelServingEndpointPermission) { +func enumResourcesModelServingEndpointPermission(w *walker, n *walk.Node, v *resources.ModelServingEndpointPermission) { if v == nil { - v = &resources.ModelServingEndpointPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues56) } - w.enumField(n, "level", string(v.Level), enumValuesServingServingEndpointPermissionLevel) } -func enumServingRateLimit(w *walker, n *node, v *serving.RateLimit) { +func enumServingRateLimit(w *walker, n *walk.Node, v *serving.RateLimit) { if v == nil { - v = &serving.RateLimit{} + return + } + if s := string(v.Key); s != "" { + c := n.Child("key") + w.enumValue(&c, s, enumValues57) + } + if s := string(v.RenewalPeriod); s != "" { + c := n.Child("renewal_period") + w.enumValue(&c, s, enumValues58) } - w.enumField(n, "key", string(v.Key), enumValuesServingRateLimitKey) - w.enumField(n, "renewal_period", string(v.RenewalPeriod), enumValuesServingRateLimitRenewalPeriod) } -func enumServingTelemetryConfig(w *walker, n *node, v *serving.TelemetryConfig) { +func enumServingTelemetryConfig(w *walker, n *walk.Node, v *serving.TelemetryConfig) { if v == nil { - v = &serving.TelemetryConfig{} + return } if len(v.EnabledTelemetryFeatures) > 0 { - c := n.child("enabled_telemetry_features") - for i0 := range v.EnabledTelemetryFeatures { - e := c.elem(i0) - w.enumElem(&e, string(v.EnabledTelemetryFeatures[i0]), enumValuesServingTelemetryFeature) + c := n.Child("enabled_telemetry_features") + for i := range v.EnabledTelemetryFeatures { + e := c.Elem(i) + w.enumValue(&e, string(v.EnabledTelemetryFeatures[i]), enumValues59) } } } -func enumResourcesMlflowModelPermission(w *walker, n *node, v *resources.MlflowModelPermission) { +func enumResourcesMlflowModelPermission(w *walker, n *walk.Node, v *resources.MlflowModelPermission) { if v == nil { - v = &resources.MlflowModelPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues60) } - w.enumField(n, "level", string(v.Level), enumValuesMlRegisteredModelPermissionLevel) } -func enumPipelinesPipelineCluster(w *walker, n *node, v *pipelines.PipelineCluster) { +func enumPipelinesPipelineCluster(w *walker, n *walk.Node, v *pipelines.PipelineCluster) { if v == nil { - v = &pipelines.PipelineCluster{} + return } - if c := n.child("autoscale"); c.exists() { - enumPipelinesPipelineClusterAutoscale(w, &c, v.Autoscale) + if x := v.Autoscale; x != nil { + c := n.Child("autoscale") + enumPipelinesPipelineClusterAutoscale(w, &c, x) } - if c := n.child("aws_attributes"); c.exists() { - enumComputeAwsAttributes(w, &c, v.AwsAttributes) + if x := v.AwsAttributes; x != nil { + c := n.Child("aws_attributes") + enumComputeAwsAttributes(w, &c, x) } - if c := n.child("azure_attributes"); c.exists() { - enumComputeAzureAttributes(w, &c, v.AzureAttributes) + if x := v.AzureAttributes; x != nil { + c := n.Child("azure_attributes") + enumComputeAzureAttributes(w, &c, x) } - if c := n.child("gcp_attributes"); c.exists() { - enumComputeGcpAttributes(w, &c, v.GcpAttributes) + if x := v.GcpAttributes; x != nil { + c := n.Child("gcp_attributes") + enumComputeGcpAttributes(w, &c, x) } } -func enumPipelinesPipelineDeployment(w *walker, n *node, v *pipelines.PipelineDeployment) { +func enumPipelinesPipelineDeployment(w *walker, n *walk.Node, v *pipelines.PipelineDeployment) { if v == nil { - v = &pipelines.PipelineDeployment{} + return + } + if s := string(v.Kind); s != "" || walk.Forced(v.ForceSendFields, "Kind") { + c := n.Child("kind") + w.enumValue(&c, s, enumValues61) } - w.enumField(n, "kind", string(v.Kind), enumValuesPipelinesDeploymentKind) } -func enumPipelinesIngestionPipelineDefinition(w *walker, n *node, v *pipelines.IngestionPipelineDefinition) { +func enumPipelinesIngestionPipelineDefinition(w *walker, n *walk.Node, v *pipelines.IngestionPipelineDefinition) { if v == nil { - v = &pipelines.IngestionPipelineDefinition{} + return } - w.enumField(n, "connector_type", string(v.ConnectorType), enumValuesPipelinesConnectorType) - if c := n.child("full_refresh_window"); c.exists() { - enumPipelinesOperationTimeWindow(w, &c, v.FullRefreshWindow) + if s := string(v.ConnectorType); s != "" || walk.Forced(v.ForceSendFields, "ConnectorType") { + c := n.Child("connector_type") + w.enumValue(&c, s, enumValues62) + } + if x := v.FullRefreshWindow; x != nil { + c := n.Child("full_refresh_window") + enumPipelinesOperationTimeWindow(w, &c, x) } if len(v.Objects) > 0 { - c := n.child("objects") - for i0 := range v.Objects { - e := c.elem(i0) - enumPipelinesIngestionConfig(w, &e, &v.Objects[i0]) + c := n.Child("objects") + for i := range v.Objects { + e := c.Elem(i) + enumPipelinesIngestionConfig(w, &e, &v.Objects[i]) } } - w.enumField(n, "source_type", string(v.SourceType), enumValuesPipelinesIngestionSourceType) - if c := n.child("table_configuration"); c.exists() { - enumPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if s := string(v.SourceType); s != "" || walk.Forced(v.ForceSendFields, "SourceType") { + c := n.Child("source_type") + w.enumValue(&c, s, enumValues63) + } + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + enumPipelinesTableSpecificConfig(w, &c, x) } } -func enumResourcesPipelinePermission(w *walker, n *node, v *resources.PipelinePermission) { +func enumResourcesPipelinePermission(w *walker, n *walk.Node, v *resources.PipelinePermission) { if v == nil { - v = &resources.PipelinePermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues64) } - w.enumField(n, "level", string(v.Level), enumValuesPipelinesPipelinePermissionLevel) } -func enumPipelinesRestartWindow(w *walker, n *node, v *pipelines.RestartWindow) { +func enumPipelinesRestartWindow(w *walker, n *walk.Node, v *pipelines.RestartWindow) { if v == nil { - v = &pipelines.RestartWindow{} + return } if len(v.DaysOfWeek) > 0 { - c := n.child("days_of_week") - for i0 := range v.DaysOfWeek { - e := c.elem(i0) - w.enumElem(&e, string(v.DaysOfWeek[i0]), enumValuesPipelinesDayOfWeek) + c := n.Child("days_of_week") + for i := range v.DaysOfWeek { + e := c.Elem(i) + w.enumValue(&e, string(v.DaysOfWeek[i]), enumValues65) } } } -func enumPostgresScheduleCadence(w *walker, n *node, v *postgres.ScheduleCadence) { +func enumPostgresScheduleCadence(w *walker, n *walk.Node, v *postgres.ScheduleCadence) { if v == nil { - v = &postgres.ScheduleCadence{} + return } - if c := n.child("weekly_schedule"); c.exists() { - enumPostgresWeeklySchedule(w, &c, v.WeeklySchedule) + if x := v.WeeklySchedule; x != nil { + c := n.Child("weekly_schedule") + enumPostgresWeeklySchedule(w, &c, x) } } -func enumPostgresSyncedTableSyncedTableSpecExtraColumn(w *walker, n *node, v *postgres.SyncedTableSyncedTableSpecExtraColumn) { +func enumPostgresSyncedTableSyncedTableSpecExtraColumn(w *walker, n *walk.Node, v *postgres.SyncedTableSyncedTableSpecExtraColumn) { if v == nil { - v = &postgres.SyncedTableSyncedTableSpecExtraColumn{} + return + } + if s := string(v.Maintenance); s != "" || walk.Forced(v.ForceSendFields, "Maintenance") { + c := n.Child("maintenance") + w.enumValue(&c, s, enumValues66) } - w.enumField(n, "maintenance", string(v.Maintenance), enumValuesPostgresSyncedTableSyncedTableSpecExtraColumnMaintenance) } -func enumPostgresNewPipelineSpec(w *walker, n *node, v *postgres.NewPipelineSpec) { +func enumPostgresNewPipelineSpec(w *walker, n *walk.Node, v *postgres.NewPipelineSpec) { if v == nil { - v = &postgres.NewPipelineSpec{} + return + } + if s := string(v.PipelineChannel); s != "" || walk.Forced(v.ForceSendFields, "PipelineChannel") { + c := n.Child("pipeline_channel") + w.enumValue(&c, s, enumValues67) } - w.enumField(n, "pipeline_channel", string(v.PipelineChannel), enumValuesPostgresNewPipelineSpecPipelineChannel) } -func enumPostgresSyncedTableSyncedTableSpecTypeOverride(w *walker, n *node, v *postgres.SyncedTableSyncedTableSpecTypeOverride) { +func enumPostgresSyncedTableSyncedTableSpecTypeOverride(w *walker, n *walk.Node, v *postgres.SyncedTableSyncedTableSpecTypeOverride) { if v == nil { - v = &postgres.SyncedTableSyncedTableSpecTypeOverride{} + return + } + if s := string(v.PgType); s != "" || walk.Forced(v.ForceSendFields, "PgType") { + c := n.Child("pg_type") + w.enumValue(&c, s, enumValues68) } - w.enumField(n, "pg_type", string(v.PgType), enumValuesPostgresSyncedTableSyncedTableSpecPgSpecificType) } -func enumCatalogMonitorMetric(w *walker, n *node, v *catalog.MonitorMetric) { +func enumCatalogMonitorMetric(w *walker, n *walk.Node, v *catalog.MonitorMetric) { if v == nil { - v = &catalog.MonitorMetric{} + return + } + if s := string(v.Type); s != "" { + c := n.Child("type") + w.enumValue(&c, s, enumValues69) } - w.enumField(n, "type", string(v.Type), enumValuesCatalogMonitorMetricType) } -func enumCatalogMonitorInferenceLog(w *walker, n *node, v *catalog.MonitorInferenceLog) { +func enumCatalogMonitorInferenceLog(w *walker, n *walk.Node, v *catalog.MonitorInferenceLog) { if v == nil { - v = &catalog.MonitorInferenceLog{} + return + } + if s := string(v.ProblemType); s != "" || walk.Forced(v.ForceSendFields, "ProblemType") { + c := n.Child("problem_type") + w.enumValue(&c, s, enumValues70) } - w.enumField(n, "problem_type", string(v.ProblemType), enumValuesCatalogMonitorInferenceLogProblemType) } -func enumCatalogMonitorCronSchedule(w *walker, n *node, v *catalog.MonitorCronSchedule) { +func enumCatalogMonitorCronSchedule(w *walker, n *walk.Node, v *catalog.MonitorCronSchedule) { if v == nil { - v = &catalog.MonitorCronSchedule{} + return + } + if s := string(v.PauseStatus); s != "" { + c := n.Child("pause_status") + w.enumValue(&c, s, enumValues71) } - w.enumField(n, "pause_status", string(v.PauseStatus), enumValuesCatalogMonitorCronSchedulePauseStatus) } -func enumResourcesSecretScopePermission(w *walker, n *node, v *resources.SecretScopePermission) { +func enumResourcesSecretScopePermission(w *walker, n *walk.Node, v *resources.SecretScopePermission) { if v == nil { - v = &resources.SecretScopePermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues72) } - w.enumField(n, "level", string(v.Level), enumValuesResourcesSecretScopePermissionLevel) } -func enumSqlChannel(w *walker, n *node, v *sql.Channel) { +func enumSqlChannel(w *walker, n *walk.Node, v *sql.Channel) { if v == nil { - v = &sql.Channel{} + return + } + if s := string(v.Name); s != "" || walk.Forced(v.ForceSendFields, "Name") { + c := n.Child("name") + w.enumValue(&c, s, enumValues73) } - w.enumField(n, "name", string(v.Name), enumValuesSqlChannelName) } -func enumResourcesSqlWarehousePermission(w *walker, n *node, v *resources.SqlWarehousePermission) { +func enumResourcesSqlWarehousePermission(w *walker, n *walk.Node, v *resources.SqlWarehousePermission) { if v == nil { - v = &resources.SqlWarehousePermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues74) } - w.enumField(n, "level", string(v.Level), enumValuesSqlWarehousePermissionLevel) } -func enumDatabaseSyncedTableStatus(w *walker, n *node, v *database.SyncedTableStatus) { +func enumDatabaseSyncedTableStatus(w *walker, n *walk.Node, v *database.SyncedTableStatus) { if v == nil { - v = &database.SyncedTableStatus{} + return + } + if x := v.ContinuousUpdateStatus; x != nil { + c := n.Child("continuous_update_status") + enumDatabaseSyncedTableContinuousUpdateStatus(w, &c, x) } - if c := n.child("continuous_update_status"); c.exists() { - enumDatabaseSyncedTableContinuousUpdateStatus(w, &c, v.ContinuousUpdateStatus) + if s := string(v.DetailedState); s != "" || walk.Forced(v.ForceSendFields, "DetailedState") { + c := n.Child("detailed_state") + w.enumValue(&c, s, enumValues75) } - w.enumField(n, "detailed_state", string(v.DetailedState), enumValuesDatabaseSyncedTableState) - if c := n.child("provisioning_status"); c.exists() { - enumDatabaseSyncedTableProvisioningStatus(w, &c, v.ProvisioningStatus) + if x := v.ProvisioningStatus; x != nil { + c := n.Child("provisioning_status") + enumDatabaseSyncedTableProvisioningStatus(w, &c, x) } - if c := n.child("triggered_update_status"); c.exists() { - enumDatabaseSyncedTableTriggeredUpdateStatus(w, &c, v.TriggeredUpdateStatus) + if x := v.TriggeredUpdateStatus; x != nil { + c := n.Child("triggered_update_status") + enumDatabaseSyncedTableTriggeredUpdateStatus(w, &c, x) } } -func enumDatabaseSyncedTableSpec(w *walker, n *node, v *database.SyncedTableSpec) { +func enumDatabaseSyncedTableSpec(w *walker, n *walk.Node, v *database.SyncedTableSpec) { if v == nil { - v = &database.SyncedTableSpec{} + return + } + if s := string(v.SchedulingPolicy); s != "" || walk.Forced(v.ForceSendFields, "SchedulingPolicy") { + c := n.Child("scheduling_policy") + w.enumValue(&c, s, enumValues21) } - w.enumField(n, "scheduling_policy", string(v.SchedulingPolicy), enumValuesDatabaseSyncedTableSchedulingPolicy) if len(v.TypeOverrides) > 0 { - c := n.child("type_overrides") - for i0 := range v.TypeOverrides { - e := c.elem(i0) - enumDatabaseSyncedTableSpecTypeOverride(w, &e, &v.TypeOverrides[i0]) + c := n.Child("type_overrides") + for i := range v.TypeOverrides { + e := c.Elem(i) + enumDatabaseSyncedTableSpecTypeOverride(w, &e, &v.TypeOverrides[i]) } } } -func enumResourcesVectorSearchEndpointPermission(w *walker, n *node, v *resources.VectorSearchEndpointPermission) { +func enumResourcesVectorSearchEndpointPermission(w *walker, n *walk.Node, v *resources.VectorSearchEndpointPermission) { if v == nil { - v = &resources.VectorSearchEndpointPermission{} + return + } + if s := string(v.Level); s != "" { + c := n.Child("level") + w.enumValue(&c, s, enumValues76) } - w.enumField(n, "level", string(v.Level), enumValuesVectorsearchVectorSearchEndpointPermissionLevel) } -func enumVectorsearchDeltaSyncVectorIndexSpecRequest(w *walker, n *node, v *vectorsearch.DeltaSyncVectorIndexSpecRequest) { +func enumVectorsearchDeltaSyncVectorIndexSpecRequest(w *walker, n *walk.Node, v *vectorsearch.DeltaSyncVectorIndexSpecRequest) { if v == nil { - v = &vectorsearch.DeltaSyncVectorIndexSpecRequest{} + return + } + if s := string(v.PipelineType); s != "" || walk.Forced(v.ForceSendFields, "PipelineType") { + c := n.Child("pipeline_type") + w.enumValue(&c, s, enumValues77) } - w.enumField(n, "pipeline_type", string(v.PipelineType), enumValuesVectorsearchPipelineType) } -func enumSqlAlertV2OperandColumn(w *walker, n *node, v *sql.AlertV2OperandColumn) { +func enumSqlAlertV2OperandColumn(w *walker, n *walk.Node, v *sql.AlertV2OperandColumn) { if v == nil { - v = &sql.AlertV2OperandColumn{} + return + } + if s := string(v.Aggregation); s != "" || walk.Forced(v.ForceSendFields, "Aggregation") { + c := n.Child("aggregation") + w.enumValue(&c, s, enumValues78) } - w.enumField(n, "aggregation", string(v.Aggregation), enumValuesSqlAggregation) } -func enumSqlAlertV2Operand(w *walker, n *node, v *sql.AlertV2Operand) { +func enumSqlAlertV2Operand(w *walker, n *walk.Node, v *sql.AlertV2Operand) { if v == nil { - v = &sql.AlertV2Operand{} + return } - if c := n.child("column"); c.exists() { - enumSqlAlertV2OperandColumn2(w, &c, v.Column) + if x := v.Column; x != nil { + c := n.Child("column") + enumSqlAlertV2OperandColumn2(w, &c, x) } } -func enumAppsAppDeploymentStatus(w *walker, n *node, v *apps.AppDeploymentStatus) { +func enumAppsAppDeploymentStatus(w *walker, n *walk.Node, v *apps.AppDeploymentStatus) { if v == nil { - v = &apps.AppDeploymentStatus{} + return + } + if s := string(v.State); s != "" || walk.Forced(v.ForceSendFields, "State") { + c := n.Child("state") + w.enumValue(&c, s, enumValues79) } - w.enumField(n, "state", string(v.State), enumValuesAppsAppDeploymentState) } -func enumAppsAppResourceApp(w *walker, n *node, v *apps.AppResourceApp) { +func enumAppsAppResourceApp(w *walker, n *walk.Node, v *apps.AppResourceApp) { if v == nil { - v = &apps.AppResourceApp{} + return + } + if s := string(v.Permission); s != "" || walk.Forced(v.ForceSendFields, "Permission") { + c := n.Child("permission") + w.enumValue(&c, s, enumValues38) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceAppAppPermission) } -func enumAppsAppResourceDatabase(w *walker, n *node, v *apps.AppResourceDatabase) { +func enumAppsAppResourceDatabase(w *walker, n *walk.Node, v *apps.AppResourceDatabase) { if v == nil { - v = &apps.AppResourceDatabase{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues80) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceDatabaseDatabasePermission) } -func enumAppsAppResourceExperiment(w *walker, n *node, v *apps.AppResourceExperiment) { +func enumAppsAppResourceExperiment(w *walker, n *walk.Node, v *apps.AppResourceExperiment) { if v == nil { - v = &apps.AppResourceExperiment{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues45) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceExperimentExperimentPermission) } -func enumAppsAppResourceGenieSpace(w *walker, n *node, v *apps.AppResourceGenieSpace) { +func enumAppsAppResourceGenieSpace(w *walker, n *walk.Node, v *apps.AppResourceGenieSpace) { if v == nil { - v = &apps.AppResourceGenieSpace{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues81) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceGenieSpaceGenieSpacePermission) } -func enumAppsAppResourceJob(w *walker, n *node, v *apps.AppResourceJob) { +func enumAppsAppResourceJob(w *walker, n *walk.Node, v *apps.AppResourceJob) { if v == nil { - v = &apps.AppResourceJob{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues53) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceJobJobPermission) } -func enumAppsAppResourcePostgres(w *walker, n *node, v *apps.AppResourcePostgres) { +func enumAppsAppResourcePostgres(w *walker, n *walk.Node, v *apps.AppResourcePostgres) { if v == nil { - v = &apps.AppResourcePostgres{} + return + } + if s := string(v.Permission); s != "" || walk.Forced(v.ForceSendFields, "Permission") { + c := n.Child("permission") + w.enumValue(&c, s, enumValues80) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourcePostgresPostgresPermission) } -func enumAppsAppResourceSecret(w *walker, n *node, v *apps.AppResourceSecret) { +func enumAppsAppResourceSecret(w *walker, n *walk.Node, v *apps.AppResourceSecret) { if v == nil { - v = &apps.AppResourceSecret{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues82) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceSecretSecretPermission) } -func enumAppsAppResourceServingEndpoint(w *walker, n *node, v *apps.AppResourceServingEndpoint) { +func enumAppsAppResourceServingEndpoint(w *walker, n *walk.Node, v *apps.AppResourceServingEndpoint) { if v == nil { - v = &apps.AppResourceServingEndpoint{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues56) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceServingEndpointServingEndpointPermission) } -func enumAppsAppResourceSqlWarehouse(w *walker, n *node, v *apps.AppResourceSqlWarehouse) { +func enumAppsAppResourceSqlWarehouse(w *walker, n *walk.Node, v *apps.AppResourceSqlWarehouse) { if v == nil { - v = &apps.AppResourceSqlWarehouse{} + return + } + if s := string(v.Permission); s != "" { + c := n.Child("permission") + w.enumValue(&c, s, enumValues83) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceSqlWarehouseSqlWarehousePermission) } -func enumAppsAppResourceUcSecurable(w *walker, n *node, v *apps.AppResourceUcSecurable) { +func enumAppsAppResourceUcSecurable(w *walker, n *walk.Node, v *apps.AppResourceUcSecurable) { if v == nil { - v = &apps.AppResourceUcSecurable{} + return + } + if s := string(v.Permission); s != "" || walk.Forced(v.ForceSendFields, "Permission") { + c := n.Child("permission") + w.enumValue(&c, s, enumValues84) + } + if s := string(v.SecurableType); s != "" || walk.Forced(v.ForceSendFields, "SecurableType") { + c := n.Child("securable_type") + w.enumValue(&c, s, enumValues85) } - w.enumField(n, "permission", string(v.Permission), enumValuesAppsAppResourceUcSecurableUcSecurablePermission) - w.enumField(n, "securable_type", string(v.SecurableType), enumValuesAppsAppResourceUcSecurableUcSecurableType) } -func enumCatalogSseEncryptionDetails(w *walker, n *node, v *catalog.SseEncryptionDetails) { +func enumCatalogSseEncryptionDetails(w *walker, n *walk.Node, v *catalog.SseEncryptionDetails) { if v == nil { - v = &catalog.SseEncryptionDetails{} + return + } + if s := string(v.Algorithm); s != "" || walk.Forced(v.ForceSendFields, "Algorithm") { + c := n.Child("algorithm") + w.enumValue(&c, s, enumValues86) } - w.enumField(n, "algorithm", string(v.Algorithm), enumValuesCatalogSseEncryptionDetailsAlgorithm) } -func enumMlRequestSource(w *walker, n *node, v *ml.RequestSource) { +func enumMlRequestSource(w *walker, n *walk.Node, v *ml.RequestSource) { if v == nil { - v = &ml.RequestSource{} + return } - if c := n.child("flat_schema"); c.exists() { - enumMlFlatSchema(w, &c, v.FlatSchema) + if x := v.FlatSchema; x != nil { + c := n.Child("flat_schema") + enumMlFlatSchema(w, &c, x) } } -func enumMlSdpSource(w *walker, n *node, v *ml.SdpSource) { +func enumMlSdpSource(w *walker, n *walk.Node, v *ml.SdpSource) { if v == nil { - v = &ml.SdpSource{} + return + } + if s := string(v.PipelineState); s != "" || walk.Forced(v.ForceSendFields, "PipelineState") { + c := n.Child("pipeline_state") + w.enumValue(&c, s, enumValues87) } - w.enumField(n, "pipeline_state", string(v.PipelineState), enumValuesMlSdpSourceState) } -func enumComputeDiskType(w *walker, n *node, v *compute.DiskType) { +func enumComputeDiskType(w *walker, n *walk.Node, v *compute.DiskType) { if v == nil { - v = &compute.DiskType{} + return + } + if s := string(v.AzureDiskVolumeType); s != "" { + c := n.Child("azure_disk_volume_type") + w.enumValue(&c, s, enumValues88) + } + if s := string(v.EbsVolumeType); s != "" { + c := n.Child("ebs_volume_type") + w.enumValue(&c, s, enumValues40) } - w.enumField(n, "azure_disk_volume_type", string(v.AzureDiskVolumeType), enumValuesComputeDiskTypeAzureDiskVolumeType) - w.enumField(n, "ebs_volume_type", string(v.EbsVolumeType), enumValuesComputeDiskTypeEbsVolumeType) } -func enumJobsMaintenanceWindow(w *walker, n *node, v *jobs.MaintenanceWindow) { +func enumJobsMaintenanceWindow(w *walker, n *walk.Node, v *jobs.MaintenanceWindow) { if v == nil { - v = &jobs.MaintenanceWindow{} + return + } + if s := string(v.DayOfWeek); s != "" { + c := n.Child("day_of_week") + w.enumValue(&c, s, enumValues65) } - w.enumField(n, "day_of_week", string(v.DayOfWeek), enumValuesJobsDayOfWeek) } -func enumJobsJobSource(w *walker, n *node, v *jobs.JobSource) { +func enumJobsJobSource(w *walker, n *walk.Node, v *jobs.JobSource) { if v == nil { - v = &jobs.JobSource{} + return + } + if s := string(v.DirtyState); s != "" { + c := n.Child("dirty_state") + w.enumValue(&c, s, enumValues89) } - w.enumField(n, "dirty_state", string(v.DirtyState), enumValuesJobsJobSourceDirtyState) } -func enumJobsJobsHealthRule(w *walker, n *node, v *jobs.JobsHealthRule) { +func enumJobsJobsHealthRule(w *walker, n *walk.Node, v *jobs.JobsHealthRule) { if v == nil { - v = &jobs.JobsHealthRule{} + return + } + if s := string(v.Metric); s != "" { + c := n.Child("metric") + w.enumValue(&c, s, enumValues90) + } + if s := string(v.Op); s != "" { + c := n.Child("op") + w.enumValue(&c, s, enumValues91) } - w.enumField(n, "metric", string(v.Metric), enumValuesJobsJobsHealthMetric) - w.enumField(n, "op", string(v.Op), enumValuesJobsJobsHealthOperator) } -func enumComputeClusterSpec(w *walker, n *node, v *compute.ClusterSpec) { +func enumComputeClusterSpec(w *walker, n *walk.Node, v *compute.ClusterSpec) { if v == nil { - v = &compute.ClusterSpec{} + return + } + if x := v.AwsAttributes; x != nil { + c := n.Child("aws_attributes") + enumComputeAwsAttributes(w, &c, x) + } + if x := v.AzureAttributes; x != nil { + c := n.Child("azure_attributes") + enumComputeAzureAttributes(w, &c, x) + } + if s := string(v.DataSecurityMode); s != "" || walk.Forced(v.ForceSendFields, "DataSecurityMode") { + c := n.Child("data_security_mode") + w.enumValue(&c, s, enumValues8) + } + if s := string(v.DependencyMode); s != "" || walk.Forced(v.ForceSendFields, "DependencyMode") { + c := n.Child("dependency_mode") + w.enumValue(&c, s, enumValues9) } - if c := n.child("aws_attributes"); c.exists() { - enumComputeAwsAttributes(w, &c, v.AwsAttributes) + if x := v.GcpAttributes; x != nil { + c := n.Child("gcp_attributes") + enumComputeGcpAttributes(w, &c, x) } - if c := n.child("azure_attributes"); c.exists() { - enumComputeAzureAttributes(w, &c, v.AzureAttributes) + if s := string(v.Kind); s != "" || walk.Forced(v.ForceSendFields, "Kind") { + c := n.Child("kind") + w.enumValue(&c, s, enumValues10) } - w.enumField(n, "data_security_mode", string(v.DataSecurityMode), enumValuesComputeDataSecurityMode) - w.enumField(n, "dependency_mode", string(v.DependencyMode), enumValuesComputeDependencyMode) - if c := n.child("gcp_attributes"); c.exists() { - enumComputeGcpAttributes(w, &c, v.GcpAttributes) + if s := string(v.RuntimeEngine); s != "" || walk.Forced(v.ForceSendFields, "RuntimeEngine") { + c := n.Child("runtime_engine") + w.enumValue(&c, s, enumValues11) } - w.enumField(n, "kind", string(v.Kind), enumValuesComputeKind) - w.enumField(n, "runtime_engine", string(v.RuntimeEngine), enumValuesComputeRuntimeEngine) } -func enumJobsSqlConditionConfiguration(w *walker, n *node, v *jobs.SqlConditionConfiguration) { +func enumJobsSqlConditionConfiguration(w *walker, n *walk.Node, v *jobs.SqlConditionConfiguration) { if v == nil { - v = &jobs.SqlConditionConfiguration{} + return + } + if s := string(v.TriggerMode); s != "" { + c := n.Child("trigger_mode") + w.enumValue(&c, s, enumValues92) } - w.enumField(n, "trigger_mode", string(v.TriggerMode), enumValuesJobsSqlConditionTriggerMode) } -func enumJobsAiRuntimeTask(w *walker, n *node, v *jobs.AiRuntimeTask) { +func enumJobsAiRuntimeTask(w *walker, n *walk.Node, v *jobs.AiRuntimeTask) { if v == nil { - v = &jobs.AiRuntimeTask{} + return } if len(v.Deployments) > 0 { - c := n.child("deployments") - for i0 := range v.Deployments { - e := c.elem(i0) - enumJobsDeploymentSpec(w, &e, &v.Deployments[i0]) + c := n.Child("deployments") + for i := range v.Deployments { + e := c.Elem(i) + enumJobsDeploymentSpec(w, &e, &v.Deployments[i]) } } - w.enumField(n, "priority_class", string(v.PriorityClass), enumValuesJobsAiRuntimeTaskPriorityClass) + if s := string(v.PriorityClass); s != "" || walk.Forced(v.ForceSendFields, "PriorityClass") { + c := n.Child("priority_class") + w.enumValue(&c, s, enumValues93) + } } -func enumJobsCompute(w *walker, n *node, v *jobs.Compute) { +func enumJobsCompute(w *walker, n *walk.Node, v *jobs.Compute) { if v == nil { - v = &jobs.Compute{} + return + } + if s := string(v.HardwareAccelerator); s != "" { + c := n.Child("hardware_accelerator") + w.enumValue(&c, s, enumValues94) } - w.enumField(n, "hardware_accelerator", string(v.HardwareAccelerator), enumValuesComputeHardwareAcceleratorType) } -func enumJobsConditionTask(w *walker, n *node, v *jobs.ConditionTask) { +func enumJobsConditionTask(w *walker, n *walk.Node, v *jobs.ConditionTask) { if v == nil { - v = &jobs.ConditionTask{} + return + } + if s := string(v.Op); s != "" { + c := n.Child("op") + w.enumValue(&c, s, enumValues95) } - w.enumField(n, "op", string(v.Op), enumValuesJobsConditionTaskOp) } -func enumJobsDashboardTask(w *walker, n *node, v *jobs.DashboardTask) { +func enumJobsDashboardTask(w *walker, n *walk.Node, v *jobs.DashboardTask) { if v == nil { - v = &jobs.DashboardTask{} + return } - if c := n.child("subscription"); c.exists() { - enumJobsSubscription(w, &c, v.Subscription) + if x := v.Subscription; x != nil { + c := n.Child("subscription") + enumJobsSubscription(w, &c, x) } } -func enumJobsDbtTask(w *walker, n *node, v *jobs.DbtTask) { +func enumJobsDbtTask(w *walker, n *walk.Node, v *jobs.DbtTask) { if v == nil { - v = &jobs.DbtTask{} + return + } + if s := string(v.Source); s != "" || walk.Forced(v.ForceSendFields, "Source") { + c := n.Child("source") + w.enumValue(&c, s, enumValues96) } - w.enumField(n, "source", string(v.Source), enumValuesJobsSource) } -func enumJobsForEachTask(w *walker, n *node, v *jobs.ForEachTask) { +func enumJobsForEachTask(w *walker, n *walk.Node, v *jobs.ForEachTask) { if v == nil { - v = &jobs.ForEachTask{} + return } - if c := n.child("task"); c.exists() { - enumJobsTask2(w, &c, &v.Task) + if x := &v.Task; !walk.IsZeroStruct(x) || walk.Forced(v.ForceSendFields, "Task") || n.HasRefs("task") { + c := n.Child("task") + enumJobsTask2(w, &c, x) } } -func enumJobsGenAiComputeTask(w *walker, n *node, v *jobs.GenAiComputeTask) { +func enumJobsGenAiComputeTask(w *walker, n *walk.Node, v *jobs.GenAiComputeTask) { if v == nil { - v = &jobs.GenAiComputeTask{} + return + } + if s := string(v.Source); s != "" || walk.Forced(v.ForceSendFields, "Source") { + c := n.Child("source") + w.enumValue(&c, s, enumValues96) } - w.enumField(n, "source", string(v.Source), enumValuesJobsSource) } -func enumJobsNotebookTask(w *walker, n *node, v *jobs.NotebookTask) { +func enumJobsNotebookTask(w *walker, n *walk.Node, v *jobs.NotebookTask) { if v == nil { - v = &jobs.NotebookTask{} + return + } + if s := string(v.Source); s != "" || walk.Forced(v.ForceSendFields, "Source") { + c := n.Child("source") + w.enumValue(&c, s, enumValues96) } - w.enumField(n, "source", string(v.Source), enumValuesJobsSource) } -func enumJobsPowerBiTask(w *walker, n *node, v *jobs.PowerBiTask) { +func enumJobsPowerBiTask(w *walker, n *walk.Node, v *jobs.PowerBiTask) { if v == nil { - v = &jobs.PowerBiTask{} + return } - if c := n.child("power_bi_model"); c.exists() { - enumJobsPowerBiModel(w, &c, v.PowerBiModel) + if x := v.PowerBiModel; x != nil { + c := n.Child("power_bi_model") + enumJobsPowerBiModel(w, &c, x) } if len(v.Tables) > 0 { - c := n.child("tables") - for i0 := range v.Tables { - e := c.elem(i0) - enumJobsPowerBiTable(w, &e, &v.Tables[i0]) + c := n.Child("tables") + for i := range v.Tables { + e := c.Elem(i) + enumJobsPowerBiTable(w, &e, &v.Tables[i]) } } } -func enumJobsSparkPythonTask(w *walker, n *node, v *jobs.SparkPythonTask) { +func enumJobsSparkPythonTask(w *walker, n *walk.Node, v *jobs.SparkPythonTask) { if v == nil { - v = &jobs.SparkPythonTask{} + return + } + if s := string(v.Source); s != "" { + c := n.Child("source") + w.enumValue(&c, s, enumValues96) } - w.enumField(n, "source", string(v.Source), enumValuesJobsSource) } -func enumJobsSqlTask(w *walker, n *node, v *jobs.SqlTask) { +func enumJobsSqlTask(w *walker, n *walk.Node, v *jobs.SqlTask) { if v == nil { - v = &jobs.SqlTask{} + return } - if c := n.child("file"); c.exists() { - enumJobsSqlTaskFile(w, &c, v.File) + if x := v.File; x != nil { + c := n.Child("file") + enumJobsSqlTaskFile(w, &c, x) } } -func enumJobsModelTriggerConfiguration(w *walker, n *node, v *jobs.ModelTriggerConfiguration) { +func enumJobsModelTriggerConfiguration(w *walker, n *walk.Node, v *jobs.ModelTriggerConfiguration) { if v == nil { - v = &jobs.ModelTriggerConfiguration{} + return + } + if s := string(v.Condition); s != "" || walk.Forced(v.ForceSendFields, "Condition") { + c := n.Child("condition") + w.enumValue(&c, s, enumValues97) } - w.enumField(n, "condition", string(v.Condition), enumValuesJobsModelTriggerConfigurationCondition) } -func enumJobsPeriodicTriggerConfiguration(w *walker, n *node, v *jobs.PeriodicTriggerConfiguration) { +func enumJobsPeriodicTriggerConfiguration(w *walker, n *walk.Node, v *jobs.PeriodicTriggerConfiguration) { if v == nil { - v = &jobs.PeriodicTriggerConfiguration{} + return + } + if s := string(v.Unit); s != "" { + c := n.Child("unit") + w.enumValue(&c, s, enumValues98) } - w.enumField(n, "unit", string(v.Unit), enumValuesJobsPeriodicTriggerConfigurationTimeUnit) } -func enumJobsTableUpdateTriggerConfiguration(w *walker, n *node, v *jobs.TableUpdateTriggerConfiguration) { +func enumJobsTableUpdateTriggerConfiguration(w *walker, n *walk.Node, v *jobs.TableUpdateTriggerConfiguration) { if v == nil { - v = &jobs.TableUpdateTriggerConfiguration{} + return + } + if s := string(v.Condition); s != "" || walk.Forced(v.ForceSendFields, "Condition") { + c := n.Child("condition") + w.enumValue(&c, s, enumValues99) } - w.enumField(n, "condition", string(v.Condition), enumValuesJobsCondition) } -func enumJobsContinuousTriggerConfiguration(w *walker, n *node, v *jobs.ContinuousTriggerConfiguration) { +func enumJobsContinuousTriggerConfiguration(w *walker, n *walk.Node, v *jobs.ContinuousTriggerConfiguration) { if v == nil { - v = &jobs.ContinuousTriggerConfiguration{} + return } - if c := n.child("maintenance_window"); c.exists() { - enumJobsMaintenanceWindow(w, &c, v.MaintenanceWindow) + if x := v.MaintenanceWindow; x != nil { + c := n.Child("maintenance_window") + enumJobsMaintenanceWindow(w, &c, x) + } + if s := string(v.TaskRetryMode); s != "" { + c := n.Child("task_retry_mode") + w.enumValue(&c, s, enumValues50) } - w.enumField(n, "task_retry_mode", string(v.TaskRetryMode), enumValuesJobsTaskRetryMode) } -func enumCatalogRateLimit(w *walker, n *node, v *catalog.RateLimit) { +func enumCatalogRateLimit(w *walker, n *walk.Node, v *catalog.RateLimit) { if v == nil { - v = &catalog.RateLimit{} + return + } + if s := string(v.Key); s != "" || walk.Forced(v.ForceSendFields, "Key") { + c := n.Child("key") + w.enumValue(&c, s, enumValues100) + } + if s := string(v.RenewalPeriod); s != "" || walk.Forced(v.ForceSendFields, "RenewalPeriod") { + c := n.Child("renewal_period") + w.enumValue(&c, s, enumValues101) } - w.enumField(n, "key", string(v.Key), enumValuesCatalogRateLimitRateLimitKey) - w.enumField(n, "renewal_period", string(v.RenewalPeriod), enumValuesCatalogRateLimitRateLimitRenewalPeriod) } -func enumCatalogModelServiceConfigRoutingConfig(w *walker, n *node, v *catalog.ModelServiceConfigRoutingConfig) { +func enumCatalogModelServiceConfigRoutingConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigRoutingConfig) { if v == nil { - v = &catalog.ModelServiceConfigRoutingConfig{} + return } if len(v.Destinations) > 0 { - c := n.child("destinations") - for i0 := range v.Destinations { - e := c.elem(i0) - enumCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i0]) + c := n.Child("destinations") + for i := range v.Destinations { + e := c.Elem(i) + enumCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i]) } } - if c := n.child("fallback"); c.exists() { - enumCatalogModelServiceConfigFallbackConfig(w, &c, v.Fallback) + if x := v.Fallback; x != nil { + c := n.Child("fallback") + enumCatalogModelServiceConfigFallbackConfig(w, &c, x) } } -func enumServingAiGatewayGuardrails(w *walker, n *node, v *serving.AiGatewayGuardrails) { +func enumServingAiGatewayGuardrails(w *walker, n *walk.Node, v *serving.AiGatewayGuardrails) { if v == nil { - v = &serving.AiGatewayGuardrails{} + return } - if c := n.child("input"); c.exists() { - enumServingAiGatewayGuardrailParameters(w, &c, v.Input) + if x := v.Input; x != nil { + c := n.Child("input") + enumServingAiGatewayGuardrailParameters(w, &c, x) } - if c := n.child("output"); c.exists() { - enumServingAiGatewayGuardrailParameters(w, &c, v.Output) + if x := v.Output; x != nil { + c := n.Child("output") + enumServingAiGatewayGuardrailParameters(w, &c, x) } } -func enumServingAiGatewayRateLimit(w *walker, n *node, v *serving.AiGatewayRateLimit) { +func enumServingAiGatewayRateLimit(w *walker, n *walk.Node, v *serving.AiGatewayRateLimit) { if v == nil { - v = &serving.AiGatewayRateLimit{} + return + } + if s := string(v.Key); s != "" || walk.Forced(v.ForceSendFields, "Key") { + c := n.Child("key") + w.enumValue(&c, s, enumValues102) + } + if s := string(v.RenewalPeriod); s != "" || walk.Forced(v.ForceSendFields, "RenewalPeriod") { + c := n.Child("renewal_period") + w.enumValue(&c, s, enumValues58) } - w.enumField(n, "key", string(v.Key), enumValuesServingAiGatewayRateLimitKey) - w.enumField(n, "renewal_period", string(v.RenewalPeriod), enumValuesServingAiGatewayRateLimitRenewalPeriod) } -func enumServingServedEntityInput(w *walker, n *node, v *serving.ServedEntityInput) { +func enumServingServedEntityInput(w *walker, n *walk.Node, v *serving.ServedEntityInput) { if v == nil { - v = &serving.ServedEntityInput{} + return + } + if x := v.ExternalModel; x != nil { + c := n.Child("external_model") + enumServingExternalModel(w, &c, x) } - if c := n.child("external_model"); c.exists() { - enumServingExternalModel(w, &c, v.ExternalModel) + if s := string(v.WorkloadType); s != "" || walk.Forced(v.ForceSendFields, "WorkloadType") { + c := n.Child("workload_type") + w.enumValue(&c, s, enumValues103) } - w.enumField(n, "workload_type", string(v.WorkloadType), enumValuesServingServingModelWorkloadType) } -func enumServingServedModelInput(w *walker, n *node, v *serving.ServedModelInput) { +func enumServingServedModelInput(w *walker, n *walk.Node, v *serving.ServedModelInput) { if v == nil { - v = &serving.ServedModelInput{} + return + } + if s := string(v.WorkloadType); s != "" || walk.Forced(v.ForceSendFields, "WorkloadType") { + c := n.Child("workload_type") + w.enumValue(&c, s, enumValues103) } - w.enumField(n, "workload_type", string(v.WorkloadType), enumValuesServingServedModelInputWorkloadType) } -func enumPipelinesPipelineClusterAutoscale(w *walker, n *node, v *pipelines.PipelineClusterAutoscale) { +func enumPipelinesPipelineClusterAutoscale(w *walker, n *walk.Node, v *pipelines.PipelineClusterAutoscale) { if v == nil { - v = &pipelines.PipelineClusterAutoscale{} + return + } + if s := string(v.Mode); s != "" { + c := n.Child("mode") + w.enumValue(&c, s, enumValues104) } - w.enumField(n, "mode", string(v.Mode), enumValuesPipelinesPipelineClusterAutoscaleMode) } -func enumPipelinesOperationTimeWindow(w *walker, n *node, v *pipelines.OperationTimeWindow) { +func enumPipelinesOperationTimeWindow(w *walker, n *walk.Node, v *pipelines.OperationTimeWindow) { if v == nil { - v = &pipelines.OperationTimeWindow{} + return } if len(v.DaysOfWeek) > 0 { - c := n.child("days_of_week") - for i0 := range v.DaysOfWeek { - e := c.elem(i0) - w.enumElem(&e, string(v.DaysOfWeek[i0]), enumValuesPipelinesDayOfWeek) + c := n.Child("days_of_week") + for i := range v.DaysOfWeek { + e := c.Elem(i) + w.enumValue(&e, string(v.DaysOfWeek[i]), enumValues65) } } } -func enumPipelinesIngestionConfig(w *walker, n *node, v *pipelines.IngestionConfig) { +func enumPipelinesIngestionConfig(w *walker, n *walk.Node, v *pipelines.IngestionConfig) { if v == nil { - v = &pipelines.IngestionConfig{} + return } - if c := n.child("report"); c.exists() { - enumPipelinesReportSpec(w, &c, v.Report) + if x := v.Report; x != nil { + c := n.Child("report") + enumPipelinesReportSpec(w, &c, x) } - if c := n.child("schema"); c.exists() { - enumPipelinesSchemaSpec(w, &c, v.Schema) + if x := v.Schema; x != nil { + c := n.Child("schema") + enumPipelinesSchemaSpec(w, &c, x) } - if c := n.child("table"); c.exists() { - enumPipelinesTableSpec(w, &c, v.Table) + if x := v.Table; x != nil { + c := n.Child("table") + enumPipelinesTableSpec(w, &c, x) } } -func enumPipelinesTableSpecificConfig(w *walker, n *node, v *pipelines.TableSpecificConfig) { +func enumPipelinesTableSpecificConfig(w *walker, n *walk.Node, v *pipelines.TableSpecificConfig) { if v == nil { - v = &pipelines.TableSpecificConfig{} + return + } + if s := string(v.ScdType); s != "" || walk.Forced(v.ForceSendFields, "ScdType") { + c := n.Child("scd_type") + w.enumValue(&c, s, enumValues105) } - w.enumField(n, "scd_type", string(v.ScdType), enumValuesPipelinesTableSpecificConfigScdType) } -func enumPostgresWeeklySchedule(w *walker, n *node, v *postgres.WeeklySchedule) { +func enumPostgresWeeklySchedule(w *walker, n *walk.Node, v *postgres.WeeklySchedule) { if v == nil { - v = &postgres.WeeklySchedule{} + return + } + if s := string(v.DayOfWeek); s != "" || walk.Forced(v.ForceSendFields, "DayOfWeek") { + c := n.Child("day_of_week") + w.enumValue(&c, s, enumValues65) } - w.enumField(n, "day_of_week", string(v.DayOfWeek), enumValuesPostgresDayOfWeek) } -func enumDatabaseSyncedTableContinuousUpdateStatus(w *walker, n *node, v *database.SyncedTableContinuousUpdateStatus) { +func enumDatabaseSyncedTableContinuousUpdateStatus(w *walker, n *walk.Node, v *database.SyncedTableContinuousUpdateStatus) { if v == nil { - v = &database.SyncedTableContinuousUpdateStatus{} + return } - if c := n.child("initial_pipeline_sync_progress"); c.exists() { - enumDatabaseSyncedTablePipelineProgress(w, &c, v.InitialPipelineSyncProgress) + if x := v.InitialPipelineSyncProgress; x != nil { + c := n.Child("initial_pipeline_sync_progress") + enumDatabaseSyncedTablePipelineProgress(w, &c, x) } } -func enumDatabaseSyncedTableProvisioningStatus(w *walker, n *node, v *database.SyncedTableProvisioningStatus) { +func enumDatabaseSyncedTableProvisioningStatus(w *walker, n *walk.Node, v *database.SyncedTableProvisioningStatus) { if v == nil { - v = &database.SyncedTableProvisioningStatus{} + return } - if c := n.child("initial_pipeline_sync_progress"); c.exists() { - enumDatabaseSyncedTablePipelineProgress(w, &c, v.InitialPipelineSyncProgress) + if x := v.InitialPipelineSyncProgress; x != nil { + c := n.Child("initial_pipeline_sync_progress") + enumDatabaseSyncedTablePipelineProgress(w, &c, x) } } -func enumDatabaseSyncedTableTriggeredUpdateStatus(w *walker, n *node, v *database.SyncedTableTriggeredUpdateStatus) { +func enumDatabaseSyncedTableTriggeredUpdateStatus(w *walker, n *walk.Node, v *database.SyncedTableTriggeredUpdateStatus) { if v == nil { - v = &database.SyncedTableTriggeredUpdateStatus{} + return } - if c := n.child("triggered_update_progress"); c.exists() { - enumDatabaseSyncedTablePipelineProgress(w, &c, v.TriggeredUpdateProgress) + if x := v.TriggeredUpdateProgress; x != nil { + c := n.Child("triggered_update_progress") + enumDatabaseSyncedTablePipelineProgress(w, &c, x) } } -func enumDatabaseSyncedTableSpecTypeOverride(w *walker, n *node, v *database.SyncedTableSpecTypeOverride) { +func enumDatabaseSyncedTableSpecTypeOverride(w *walker, n *walk.Node, v *database.SyncedTableSpecTypeOverride) { if v == nil { - v = &database.SyncedTableSpecTypeOverride{} + return + } + if s := string(v.PgType); s != "" || walk.Forced(v.ForceSendFields, "PgType") { + c := n.Child("pg_type") + w.enumValue(&c, s, enumValues68) } - w.enumField(n, "pg_type", string(v.PgType), enumValuesDatabaseSyncedTableSpecPgSpecificType) } -func enumSqlAlertV2OperandColumn2(w *walker, n *node, v *sql.AlertV2OperandColumn) { +func enumSqlAlertV2OperandColumn2(w *walker, n *walk.Node, v *sql.AlertV2OperandColumn) { if v == nil { - v = &sql.AlertV2OperandColumn{} + return + } + if s := string(v.Aggregation); s != "" || walk.Forced(v.ForceSendFields, "Aggregation") { + c := n.Child("aggregation") + w.enumValue(&c, s, enumValues78) } - w.enumField(n, "aggregation", string(v.Aggregation), enumValuesSqlAggregation) } -func enumMlFlatSchema(w *walker, n *node, v *ml.FlatSchema) { +func enumMlFlatSchema(w *walker, n *walk.Node, v *ml.FlatSchema) { if v == nil { - v = &ml.FlatSchema{} + return } if len(v.Fields) > 0 { - c := n.child("fields") - for i0 := range v.Fields { - e := c.elem(i0) - enumMlFieldDefinition(w, &e, &v.Fields[i0]) + c := n.Child("fields") + for i := range v.Fields { + e := c.Elem(i) + enumMlFieldDefinition(w, &e, &v.Fields[i]) } } } -func enumJobsDeploymentSpec(w *walker, n *node, v *jobs.DeploymentSpec) { +func enumJobsDeploymentSpec(w *walker, n *walk.Node, v *jobs.DeploymentSpec) { if v == nil { - v = &jobs.DeploymentSpec{} + return } - if c := n.child("compute"); c.exists() { - enumJobsComputeSpec(w, &c, &v.Compute) + if x := &v.Compute; !walk.IsZeroStruct(x) || walk.Forced(v.ForceSendFields, "Compute") || n.HasRefs("compute") { + c := n.Child("compute") + enumJobsComputeSpec(w, &c, x) } } -func enumJobsSubscription(w *walker, n *node, v *jobs.Subscription) { +func enumJobsSubscription(w *walker, n *walk.Node, v *jobs.Subscription) { if v == nil { - v = &jobs.Subscription{} + return } - if c := n.child("attachment"); c.exists() { - enumJobsSubscriptionAttachment(w, &c, v.Attachment) + if x := v.Attachment; x != nil { + c := n.Child("attachment") + enumJobsSubscriptionAttachment(w, &c, x) } } -func enumJobsTask2(w *walker, n *node, v *jobs.Task) { +func enumJobsTask2(w *walker, n *walk.Node, v *jobs.Task) { if v == nil { - v = &jobs.Task{} + return + } + if x := v.AiRuntimeTask; x != nil { + c := n.Child("ai_runtime_task") + enumJobsAiRuntimeTask(w, &c, x) } - if c := n.child("ai_runtime_task"); c.exists() { - enumJobsAiRuntimeTask(w, &c, v.AiRuntimeTask) + if x := v.Compute; x != nil { + c := n.Child("compute") + enumJobsCompute(w, &c, x) } - if c := n.child("compute"); c.exists() { - enumJobsCompute(w, &c, v.Compute) + if x := v.ConditionTask; x != nil { + c := n.Child("condition_task") + enumJobsConditionTask(w, &c, x) } - if c := n.child("condition_task"); c.exists() { - enumJobsConditionTask(w, &c, v.ConditionTask) + if x := v.DashboardTask; x != nil { + c := n.Child("dashboard_task") + enumJobsDashboardTask(w, &c, x) } - if c := n.child("dashboard_task"); c.exists() { - enumJobsDashboardTask(w, &c, v.DashboardTask) + if x := v.DbtTask; x != nil { + c := n.Child("dbt_task") + enumJobsDbtTask(w, &c, x) } - if c := n.child("dbt_task"); c.exists() { - enumJobsDbtTask(w, &c, v.DbtTask) + if x := v.GenAiComputeTask; x != nil { + c := n.Child("gen_ai_compute_task") + enumJobsGenAiComputeTask(w, &c, x) } - if c := n.child("gen_ai_compute_task"); c.exists() { - enumJobsGenAiComputeTask(w, &c, v.GenAiComputeTask) + if x := v.Health; x != nil { + c := n.Child("health") + enumJobsJobsHealthRules(w, &c, x) } - if c := n.child("health"); c.exists() { - enumJobsJobsHealthRules(w, &c, v.Health) + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + enumComputeClusterSpec(w, &c, x) } - if c := n.child("new_cluster"); c.exists() { - enumComputeClusterSpec(w, &c, v.NewCluster) + if x := v.NotebookTask; x != nil { + c := n.Child("notebook_task") + enumJobsNotebookTask(w, &c, x) } - if c := n.child("notebook_task"); c.exists() { - enumJobsNotebookTask(w, &c, v.NotebookTask) + if x := v.PowerBiTask; x != nil { + c := n.Child("power_bi_task") + enumJobsPowerBiTask(w, &c, x) } - if c := n.child("power_bi_task"); c.exists() { - enumJobsPowerBiTask(w, &c, v.PowerBiTask) + if s := string(v.RunIf); s != "" || walk.Forced(v.ForceSendFields, "RunIf") { + c := n.Child("run_if") + w.enumValue(&c, s, enumValues54) } - w.enumField(n, "run_if", string(v.RunIf), enumValuesJobsRunIf) - if c := n.child("spark_python_task"); c.exists() { - enumJobsSparkPythonTask(w, &c, v.SparkPythonTask) + if x := v.SparkPythonTask; x != nil { + c := n.Child("spark_python_task") + enumJobsSparkPythonTask(w, &c, x) } - if c := n.child("sql_task"); c.exists() { - enumJobsSqlTask(w, &c, v.SqlTask) + if x := v.SqlTask; x != nil { + c := n.Child("sql_task") + enumJobsSqlTask(w, &c, x) } } -func enumJobsPowerBiModel(w *walker, n *node, v *jobs.PowerBiModel) { +func enumJobsPowerBiModel(w *walker, n *walk.Node, v *jobs.PowerBiModel) { if v == nil { - v = &jobs.PowerBiModel{} + return + } + if s := string(v.AuthenticationMethod); s != "" || walk.Forced(v.ForceSendFields, "AuthenticationMethod") { + c := n.Child("authentication_method") + w.enumValue(&c, s, enumValues106) + } + if s := string(v.StorageMode); s != "" || walk.Forced(v.ForceSendFields, "StorageMode") { + c := n.Child("storage_mode") + w.enumValue(&c, s, enumValues107) } - w.enumField(n, "authentication_method", string(v.AuthenticationMethod), enumValuesJobsAuthenticationMethod) - w.enumField(n, "storage_mode", string(v.StorageMode), enumValuesJobsStorageMode) } -func enumJobsPowerBiTable(w *walker, n *node, v *jobs.PowerBiTable) { +func enumJobsPowerBiTable(w *walker, n *walk.Node, v *jobs.PowerBiTable) { if v == nil { - v = &jobs.PowerBiTable{} + return + } + if s := string(v.StorageMode); s != "" || walk.Forced(v.ForceSendFields, "StorageMode") { + c := n.Child("storage_mode") + w.enumValue(&c, s, enumValues107) } - w.enumField(n, "storage_mode", string(v.StorageMode), enumValuesJobsStorageMode) } -func enumJobsSqlTaskFile(w *walker, n *node, v *jobs.SqlTaskFile) { +func enumJobsSqlTaskFile(w *walker, n *walk.Node, v *jobs.SqlTaskFile) { if v == nil { - v = &jobs.SqlTaskFile{} + return + } + if s := string(v.Source); s != "" { + c := n.Child("source") + w.enumValue(&c, s, enumValues96) } - w.enumField(n, "source", string(v.Source), enumValuesJobsSource) } -func enumCatalogModelServiceConfigDestinationConfig(w *walker, n *node, v *catalog.ModelServiceConfigDestinationConfig) { +func enumCatalogModelServiceConfigDestinationConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigDestinationConfig) { if v == nil { - v = &catalog.ModelServiceConfigDestinationConfig{} + return + } + if s := string(v.DestinationType); s != "" || walk.Forced(v.ForceSendFields, "DestinationType") { + c := n.Child("destination_type") + w.enumValue(&c, s, enumValues108) } - w.enumField(n, "destination_type", string(v.DestinationType), enumValuesCatalogModelServiceConfigDestinationConfigDestinationType) } -func enumCatalogModelServiceConfigFallbackConfig(w *walker, n *node, v *catalog.ModelServiceConfigFallbackConfig) { +func enumCatalogModelServiceConfigFallbackConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigFallbackConfig) { if v == nil { - v = &catalog.ModelServiceConfigFallbackConfig{} + return } if len(v.Destinations) > 0 { - c := n.child("destinations") - for i0 := range v.Destinations { - e := c.elem(i0) - enumCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i0]) + c := n.Child("destinations") + for i := range v.Destinations { + e := c.Elem(i) + enumCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i]) } } } -func enumServingAiGatewayGuardrailParameters(w *walker, n *node, v *serving.AiGatewayGuardrailParameters) { +func enumServingAiGatewayGuardrailParameters(w *walker, n *walk.Node, v *serving.AiGatewayGuardrailParameters) { if v == nil { - v = &serving.AiGatewayGuardrailParameters{} + return } - if c := n.child("pii"); c.exists() { - enumServingAiGatewayGuardrailPiiBehavior(w, &c, v.Pii) + if x := v.Pii; x != nil { + c := n.Child("pii") + enumServingAiGatewayGuardrailPiiBehavior(w, &c, x) } } -func enumServingExternalModel(w *walker, n *node, v *serving.ExternalModel) { +func enumServingExternalModel(w *walker, n *walk.Node, v *serving.ExternalModel) { if v == nil { - v = &serving.ExternalModel{} + return + } + if x := v.AmazonBedrockConfig; x != nil { + c := n.Child("amazon_bedrock_config") + enumServingAmazonBedrockConfig(w, &c, x) } - if c := n.child("amazon_bedrock_config"); c.exists() { - enumServingAmazonBedrockConfig(w, &c, v.AmazonBedrockConfig) + if s := string(v.Provider); s != "" { + c := n.Child("provider") + w.enumValue(&c, s, enumValues109) } - w.enumField(n, "provider", string(v.Provider), enumValuesServingExternalModelProvider) } -func enumPipelinesReportSpec(w *walker, n *node, v *pipelines.ReportSpec) { +func enumPipelinesReportSpec(w *walker, n *walk.Node, v *pipelines.ReportSpec) { if v == nil { - v = &pipelines.ReportSpec{} + return } - if c := n.child("table_configuration"); c.exists() { - enumPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + enumPipelinesTableSpecificConfig(w, &c, x) } } -func enumPipelinesSchemaSpec(w *walker, n *node, v *pipelines.SchemaSpec) { +func enumPipelinesSchemaSpec(w *walker, n *walk.Node, v *pipelines.SchemaSpec) { if v == nil { - v = &pipelines.SchemaSpec{} + return } - if c := n.child("connector_options"); c.exists() { - enumPipelinesConnectorOptions(w, &c, v.ConnectorOptions) + if x := v.ConnectorOptions; x != nil { + c := n.Child("connector_options") + enumPipelinesConnectorOptions(w, &c, x) } - if c := n.child("fanout_options"); c.exists() { - enumPipelinesIngestionPipelineDefinitionFanoutOptions(w, &c, v.FanoutOptions) + if x := v.FanoutOptions; x != nil { + c := n.Child("fanout_options") + enumPipelinesIngestionPipelineDefinitionFanoutOptions(w, &c, x) } - if c := n.child("table_configuration"); c.exists() { - enumPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + enumPipelinesTableSpecificConfig(w, &c, x) } } -func enumPipelinesTableSpec(w *walker, n *node, v *pipelines.TableSpec) { +func enumPipelinesTableSpec(w *walker, n *walk.Node, v *pipelines.TableSpec) { if v == nil { - v = &pipelines.TableSpec{} + return } - if c := n.child("connector_options"); c.exists() { - enumPipelinesConnectorOptions(w, &c, v.ConnectorOptions) + if x := v.ConnectorOptions; x != nil { + c := n.Child("connector_options") + enumPipelinesConnectorOptions(w, &c, x) } - if c := n.child("table_configuration"); c.exists() { - enumPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + enumPipelinesTableSpecificConfig(w, &c, x) } } -func enumDatabaseSyncedTablePipelineProgress(w *walker, n *node, v *database.SyncedTablePipelineProgress) { +func enumDatabaseSyncedTablePipelineProgress(w *walker, n *walk.Node, v *database.SyncedTablePipelineProgress) { if v == nil { - v = &database.SyncedTablePipelineProgress{} + return + } + if s := string(v.ProvisioningPhase); s != "" || walk.Forced(v.ForceSendFields, "ProvisioningPhase") { + c := n.Child("provisioning_phase") + w.enumValue(&c, s, enumValues110) } - w.enumField(n, "provisioning_phase", string(v.ProvisioningPhase), enumValuesDatabaseProvisioningPhase) } -func enumMlFieldDefinition(w *walker, n *node, v *ml.FieldDefinition) { +func enumMlFieldDefinition(w *walker, n *walk.Node, v *ml.FieldDefinition) { if v == nil { - v = &ml.FieldDefinition{} + return + } + if s := string(v.DataType); s != "" { + c := n.Child("data_type") + w.enumValue(&c, s, enumValues111) } - w.enumField(n, "data_type", string(v.DataType), enumValuesMlScalarDataType) } -func enumJobsComputeSpec(w *walker, n *node, v *jobs.ComputeSpec) { +func enumJobsComputeSpec(w *walker, n *walk.Node, v *jobs.ComputeSpec) { if v == nil { - v = &jobs.ComputeSpec{} + return + } + if s := string(v.AcceleratorType); s != "" || walk.Forced(v.ForceSendFields, "AcceleratorType") { + c := n.Child("accelerator_type") + w.enumValue(&c, s, enumValues94) } - w.enumField(n, "accelerator_type", string(v.AcceleratorType), enumValuesJobsComputeSpecAcceleratorType) } -func enumJobsSubscriptionAttachment(w *walker, n *node, v *jobs.SubscriptionAttachment) { +func enumJobsSubscriptionAttachment(w *walker, n *walk.Node, v *jobs.SubscriptionAttachment) { if v == nil { - v = &jobs.SubscriptionAttachment{} + return } - if c := n.child("data_attachment"); c.exists() { - enumJobsSubscriptionAttachmentDataAttachment(w, &c, v.DataAttachment) + if x := v.DataAttachment; x != nil { + c := n.Child("data_attachment") + enumJobsSubscriptionAttachmentDataAttachment(w, &c, x) } - if c := n.child("page_attachment"); c.exists() { - enumJobsSubscriptionAttachmentPageAttachment(w, &c, v.PageAttachment) + if x := v.PageAttachment; x != nil { + c := n.Child("page_attachment") + enumJobsSubscriptionAttachmentPageAttachment(w, &c, x) } } -func enumServingAiGatewayGuardrailPiiBehavior(w *walker, n *node, v *serving.AiGatewayGuardrailPiiBehavior) { +func enumServingAiGatewayGuardrailPiiBehavior(w *walker, n *walk.Node, v *serving.AiGatewayGuardrailPiiBehavior) { if v == nil { - v = &serving.AiGatewayGuardrailPiiBehavior{} + return + } + if s := string(v.Behavior); s != "" { + c := n.Child("behavior") + w.enumValue(&c, s, enumValues112) } - w.enumField(n, "behavior", string(v.Behavior), enumValuesServingAiGatewayGuardrailPiiBehaviorBehavior) } -func enumServingAmazonBedrockConfig(w *walker, n *node, v *serving.AmazonBedrockConfig) { +func enumServingAmazonBedrockConfig(w *walker, n *walk.Node, v *serving.AmazonBedrockConfig) { if v == nil { - v = &serving.AmazonBedrockConfig{} + return + } + if s := string(v.BedrockProvider); s != "" || walk.Forced(v.ForceSendFields, "BedrockProvider") { + c := n.Child("bedrock_provider") + w.enumValue(&c, s, enumValues113) } - w.enumField(n, "bedrock_provider", string(v.BedrockProvider), enumValuesServingAmazonBedrockConfigBedrockProvider) } -func enumPipelinesConnectorOptions(w *walker, n *node, v *pipelines.ConnectorOptions) { +func enumPipelinesConnectorOptions(w *walker, n *walk.Node, v *pipelines.ConnectorOptions) { if v == nil { - v = &pipelines.ConnectorOptions{} + return } - if c := n.child("gdrive_options"); c.exists() { - enumPipelinesGoogleDriveOptions(w, &c, v.GdriveOptions) + if x := v.GdriveOptions; x != nil { + c := n.Child("gdrive_options") + enumPipelinesGoogleDriveOptions(w, &c, x) } - if c := n.child("kafka_options"); c.exists() { - enumPipelinesKafkaOptions(w, &c, v.KafkaOptions) + if x := v.KafkaOptions; x != nil { + c := n.Child("kafka_options") + enumPipelinesKafkaOptions(w, &c, x) } - if c := n.child("linkedin_ads_options"); c.exists() { - enumPipelinesLinkedInAdsOptions(w, &c, v.LinkedinAdsOptions) + if x := v.LinkedinAdsOptions; x != nil { + c := n.Child("linkedin_ads_options") + enumPipelinesLinkedInAdsOptions(w, &c, x) } - if c := n.child("outlook_options"); c.exists() { - enumPipelinesOutlookOptions(w, &c, v.OutlookOptions) + if x := v.OutlookOptions; x != nil { + c := n.Child("outlook_options") + enumPipelinesOutlookOptions(w, &c, x) } - if c := n.child("sharepoint_options"); c.exists() { - enumPipelinesSharepointOptions(w, &c, v.SharepointOptions) + if x := v.SharepointOptions; x != nil { + c := n.Child("sharepoint_options") + enumPipelinesSharepointOptions(w, &c, x) } - if c := n.child("tiktok_ads_options"); c.exists() { - enumPipelinesTikTokAdsOptions(w, &c, v.TiktokAdsOptions) + if x := v.TiktokAdsOptions; x != nil { + c := n.Child("tiktok_ads_options") + enumPipelinesTikTokAdsOptions(w, &c, x) } } -func enumPipelinesIngestionPipelineDefinitionFanoutOptions(w *walker, n *node, v *pipelines.IngestionPipelineDefinitionFanoutOptions) { +func enumPipelinesIngestionPipelineDefinitionFanoutOptions(w *walker, n *walk.Node, v *pipelines.IngestionPipelineDefinitionFanoutOptions) { if v == nil { - v = &pipelines.IngestionPipelineDefinitionFanoutOptions{} + return } if len(v.Transforms) > 0 { - c := n.child("transforms") - for i0 := range v.Transforms { - e := c.elem(i0) - enumPipelinesTransformer(w, &e, &v.Transforms[i0]) + c := n.Child("transforms") + for i := range v.Transforms { + e := c.Elem(i) + enumPipelinesTransformer(w, &e, &v.Transforms[i]) } } } -func enumJobsSubscriptionAttachmentDataAttachment(w *walker, n *node, v *jobs.SubscriptionAttachmentDataAttachment) { +func enumJobsSubscriptionAttachmentDataAttachment(w *walker, n *walk.Node, v *jobs.SubscriptionAttachmentDataAttachment) { if v == nil { - v = &jobs.SubscriptionAttachmentDataAttachment{} + return + } + if s := string(v.FileFormat); s != "" || walk.Forced(v.ForceSendFields, "FileFormat") { + c := n.Child("file_format") + w.enumValue(&c, s, enumValues114) } - w.enumField(n, "file_format", string(v.FileFormat), enumValuesJobsSubscriptionAttachmentDataAttachmentFileFormat) } -func enumJobsSubscriptionAttachmentPageAttachment(w *walker, n *node, v *jobs.SubscriptionAttachmentPageAttachment) { +func enumJobsSubscriptionAttachmentPageAttachment(w *walker, n *walk.Node, v *jobs.SubscriptionAttachmentPageAttachment) { if v == nil { - v = &jobs.SubscriptionAttachmentPageAttachment{} + return + } + if s := string(v.PageSelection); s != "" { + c := n.Child("page_selection") + w.enumValue(&c, s, enumValues115) } - w.enumField(n, "page_selection", string(v.PageSelection), enumValuesJobsSubscriptionAttachmentPageAttachmentPageSelection) } -func enumPipelinesGoogleDriveOptions(w *walker, n *node, v *pipelines.GoogleDriveOptions) { +func enumPipelinesGoogleDriveOptions(w *walker, n *walk.Node, v *pipelines.GoogleDriveOptions) { if v == nil { - v = &pipelines.GoogleDriveOptions{} + return + } + if s := string(v.EntityType); s != "" || walk.Forced(v.ForceSendFields, "EntityType") { + c := n.Child("entity_type") + w.enumValue(&c, s, enumValues116) } - w.enumField(n, "entity_type", string(v.EntityType), enumValuesPipelinesGoogleDriveOptionsGoogleDriveEntityType) - if c := n.child("file_ingestion_options"); c.exists() { - enumPipelinesFileIngestionOptions(w, &c, v.FileIngestionOptions) + if x := v.FileIngestionOptions; x != nil { + c := n.Child("file_ingestion_options") + enumPipelinesFileIngestionOptions(w, &c, x) } } -func enumPipelinesKafkaOptions(w *walker, n *node, v *pipelines.KafkaOptions) { +func enumPipelinesKafkaOptions(w *walker, n *walk.Node, v *pipelines.KafkaOptions) { if v == nil { - v = &pipelines.KafkaOptions{} + return } - if c := n.child("key_transformer"); c.exists() { - enumPipelinesTransformer2(w, &c, v.KeyTransformer) + if x := v.KeyTransformer; x != nil { + c := n.Child("key_transformer") + enumPipelinesTransformer2(w, &c, x) } - if c := n.child("value_transformer"); c.exists() { - enumPipelinesTransformer2(w, &c, v.ValueTransformer) + if x := v.ValueTransformer; x != nil { + c := n.Child("value_transformer") + enumPipelinesTransformer2(w, &c, x) } } -func enumPipelinesLinkedInAdsOptions(w *walker, n *node, v *pipelines.LinkedInAdsOptions) { +func enumPipelinesLinkedInAdsOptions(w *walker, n *walk.Node, v *pipelines.LinkedInAdsOptions) { if v == nil { - v = &pipelines.LinkedInAdsOptions{} + return } - if c := n.child("custom_report_options"); c.exists() { - enumPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w, &c, v.CustomReportOptions) + if x := v.CustomReportOptions; x != nil { + c := n.Child("custom_report_options") + enumPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w, &c, x) } } -func enumPipelinesOutlookOptions(w *walker, n *node, v *pipelines.OutlookOptions) { +func enumPipelinesOutlookOptions(w *walker, n *walk.Node, v *pipelines.OutlookOptions) { if v == nil { - v = &pipelines.OutlookOptions{} + return + } + if s := string(v.AttachmentMode); s != "" || walk.Forced(v.ForceSendFields, "AttachmentMode") { + c := n.Child("attachment_mode") + w.enumValue(&c, s, enumValues117) + } + if s := string(v.BodyFormat); s != "" || walk.Forced(v.ForceSendFields, "BodyFormat") { + c := n.Child("body_format") + w.enumValue(&c, s, enumValues118) } - w.enumField(n, "attachment_mode", string(v.AttachmentMode), enumValuesPipelinesOutlookAttachmentMode) - w.enumField(n, "body_format", string(v.BodyFormat), enumValuesPipelinesOutlookBodyFormat) } -func enumPipelinesSharepointOptions(w *walker, n *node, v *pipelines.SharepointOptions) { +func enumPipelinesSharepointOptions(w *walker, n *walk.Node, v *pipelines.SharepointOptions) { if v == nil { - v = &pipelines.SharepointOptions{} + return + } + if s := string(v.EntityType); s != "" || walk.Forced(v.ForceSendFields, "EntityType") { + c := n.Child("entity_type") + w.enumValue(&c, s, enumValues119) } - w.enumField(n, "entity_type", string(v.EntityType), enumValuesPipelinesSharepointOptionsSharepointEntityType) - if c := n.child("file_ingestion_options"); c.exists() { - enumPipelinesFileIngestionOptions(w, &c, v.FileIngestionOptions) + if x := v.FileIngestionOptions; x != nil { + c := n.Child("file_ingestion_options") + enumPipelinesFileIngestionOptions(w, &c, x) } } -func enumPipelinesTikTokAdsOptions(w *walker, n *node, v *pipelines.TikTokAdsOptions) { +func enumPipelinesTikTokAdsOptions(w *walker, n *walk.Node, v *pipelines.TikTokAdsOptions) { if v == nil { - v = &pipelines.TikTokAdsOptions{} + return + } + if x := v.CustomReportOptions; x != nil { + c := n.Child("custom_report_options") + enumPipelinesTikTokAdsOptionsTikTokAdsCustomReportOptions(w, &c, x) } - if c := n.child("custom_report_options"); c.exists() { - enumPipelinesTikTokAdsOptionsTikTokAdsCustomReportOptions(w, &c, v.CustomReportOptions) + if s := string(v.DataLevel); s != "" || walk.Forced(v.ForceSendFields, "DataLevel") { + c := n.Child("data_level") + w.enumValue(&c, s, enumValues120) + } + if s := string(v.ReportType); s != "" || walk.Forced(v.ForceSendFields, "ReportType") { + c := n.Child("report_type") + w.enumValue(&c, s, enumValues121) } - w.enumField(n, "data_level", string(v.DataLevel), enumValuesPipelinesTikTokAdsOptionsTikTokDataLevel) - w.enumField(n, "report_type", string(v.ReportType), enumValuesPipelinesTikTokAdsOptionsTikTokReportType) } -func enumPipelinesTransformer(w *walker, n *node, v *pipelines.Transformer) { +func enumPipelinesTransformer(w *walker, n *walk.Node, v *pipelines.Transformer) { if v == nil { - v = &pipelines.Transformer{} + return + } + if x := v.AvroOptions; x != nil { + c := n.Child("avro_options") + enumPipelinesAvroTransformerOptions(w, &c, x) } - if c := n.child("avro_options"); c.exists() { - enumPipelinesAvroTransformerOptions(w, &c, v.AvroOptions) + if s := string(v.Format); s != "" || walk.Forced(v.ForceSendFields, "Format") { + c := n.Child("format") + w.enumValue(&c, s, enumValues122) } - w.enumField(n, "format", string(v.Format), enumValuesPipelinesTransformerFormat) - if c := n.child("json_options"); c.exists() { - enumPipelinesJsonTransformerOptions(w, &c, v.JsonOptions) + if x := v.JsonOptions; x != nil { + c := n.Child("json_options") + enumPipelinesJsonTransformerOptions(w, &c, x) } - if c := n.child("protobuf_options"); c.exists() { - enumPipelinesProtobufTransformerOptions(w, &c, v.ProtobufOptions) + if x := v.ProtobufOptions; x != nil { + c := n.Child("protobuf_options") + enumPipelinesProtobufTransformerOptions(w, &c, x) } } -func enumPipelinesFileIngestionOptions(w *walker, n *node, v *pipelines.FileIngestionOptions) { +func enumPipelinesFileIngestionOptions(w *walker, n *walk.Node, v *pipelines.FileIngestionOptions) { if v == nil { - v = &pipelines.FileIngestionOptions{} + return + } + if s := string(v.Format); s != "" || walk.Forced(v.ForceSendFields, "Format") { + c := n.Child("format") + w.enumValue(&c, s, enumValues123) + } + if s := string(v.SchemaEvolutionMode); s != "" || walk.Forced(v.ForceSendFields, "SchemaEvolutionMode") { + c := n.Child("schema_evolution_mode") + w.enumValue(&c, s, enumValues124) } - w.enumField(n, "format", string(v.Format), enumValuesPipelinesFileIngestionOptionsFileFormat) - w.enumField(n, "schema_evolution_mode", string(v.SchemaEvolutionMode), enumValuesPipelinesFileIngestionOptionsSchemaEvolutionMode) } -func enumPipelinesTransformer2(w *walker, n *node, v *pipelines.Transformer) { +func enumPipelinesTransformer2(w *walker, n *walk.Node, v *pipelines.Transformer) { if v == nil { - v = &pipelines.Transformer{} + return + } + if x := v.AvroOptions; x != nil { + c := n.Child("avro_options") + enumPipelinesAvroTransformerOptions(w, &c, x) } - if c := n.child("avro_options"); c.exists() { - enumPipelinesAvroTransformerOptions(w, &c, v.AvroOptions) + if s := string(v.Format); s != "" || walk.Forced(v.ForceSendFields, "Format") { + c := n.Child("format") + w.enumValue(&c, s, enumValues122) } - w.enumField(n, "format", string(v.Format), enumValuesPipelinesTransformerFormat) - if c := n.child("json_options"); c.exists() { - enumPipelinesJsonTransformerOptions(w, &c, v.JsonOptions) + if x := v.JsonOptions; x != nil { + c := n.Child("json_options") + enumPipelinesJsonTransformerOptions(w, &c, x) } - if c := n.child("protobuf_options"); c.exists() { - enumPipelinesProtobufTransformerOptions(w, &c, v.ProtobufOptions) + if x := v.ProtobufOptions; x != nil { + c := n.Child("protobuf_options") + enumPipelinesProtobufTransformerOptions(w, &c, x) } } -func enumPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w *walker, n *node, v *pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions) { +func enumPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w *walker, n *walk.Node, v *pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions) { if v == nil { - v = &pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions{} + return } if len(v.EntityGranularity) > 0 { - c := n.child("entity_granularity") - for i0 := range v.EntityGranularity { - e := c.elem(i0) - w.enumElem(&e, string(v.EntityGranularity[i0]), enumValuesPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsEntityGranularity) + c := n.Child("entity_granularity") + for i := range v.EntityGranularity { + e := c.Elem(i) + w.enumValue(&e, string(v.EntityGranularity[i]), enumValues125) } } - w.enumField(n, "finder", string(v.Finder), enumValuesPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsFinder) - w.enumField(n, "time_granularity", string(v.TimeGranularity), enumValuesPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsTimeGranularity) + if s := string(v.Finder); s != "" { + c := n.Child("finder") + w.enumValue(&c, s, enumValues126) + } + if s := string(v.TimeGranularity); s != "" { + c := n.Child("time_granularity") + w.enumValue(&c, s, enumValues127) + } } -func enumPipelinesTikTokAdsOptionsTikTokAdsCustomReportOptions(w *walker, n *node, v *pipelines.TikTokAdsOptionsTikTokAdsCustomReportOptions) { +func enumPipelinesTikTokAdsOptionsTikTokAdsCustomReportOptions(w *walker, n *walk.Node, v *pipelines.TikTokAdsOptionsTikTokAdsCustomReportOptions) { if v == nil { - v = &pipelines.TikTokAdsOptionsTikTokAdsCustomReportOptions{} + return + } + if s := string(v.DataLevel); s != "" || walk.Forced(v.ForceSendFields, "DataLevel") { + c := n.Child("data_level") + w.enumValue(&c, s, enumValues120) + } + if s := string(v.ReportType); s != "" || walk.Forced(v.ForceSendFields, "ReportType") { + c := n.Child("report_type") + w.enumValue(&c, s, enumValues121) } - w.enumField(n, "data_level", string(v.DataLevel), enumValuesPipelinesTikTokAdsOptionsTikTokDataLevel) - w.enumField(n, "report_type", string(v.ReportType), enumValuesPipelinesTikTokAdsOptionsTikTokReportType) } -func enumPipelinesAvroTransformerOptions(w *walker, n *node, v *pipelines.AvroTransformerOptions) { +func enumPipelinesAvroTransformerOptions(w *walker, n *walk.Node, v *pipelines.AvroTransformerOptions) { if v == nil { - v = &pipelines.AvroTransformerOptions{} + return + } + if s := string(v.ParseMode); s != "" || walk.Forced(v.ForceSendFields, "ParseMode") { + c := n.Child("parse_mode") + w.enumValue(&c, s, enumValues128) } - w.enumField(n, "parse_mode", string(v.ParseMode), enumValuesPipelinesParseMode) } -func enumPipelinesJsonTransformerOptions(w *walker, n *node, v *pipelines.JsonTransformerOptions) { +func enumPipelinesJsonTransformerOptions(w *walker, n *walk.Node, v *pipelines.JsonTransformerOptions) { if v == nil { - v = &pipelines.JsonTransformerOptions{} + return + } + if s := string(v.SchemaEvolutionMode); s != "" || walk.Forced(v.ForceSendFields, "SchemaEvolutionMode") { + c := n.Child("schema_evolution_mode") + w.enumValue(&c, s, enumValues124) } - w.enumField(n, "schema_evolution_mode", string(v.SchemaEvolutionMode), enumValuesPipelinesFileIngestionOptionsSchemaEvolutionMode) } -func enumPipelinesProtobufTransformerOptions(w *walker, n *node, v *pipelines.ProtobufTransformerOptions) { +func enumPipelinesProtobufTransformerOptions(w *walker, n *walk.Node, v *pipelines.ProtobufTransformerOptions) { if v == nil { - v = &pipelines.ProtobufTransformerOptions{} + return + } + if s := string(v.ParseMode); s != "" || walk.Forced(v.ForceSendFields, "ParseMode") { + c := n.Child("parse_mode") + w.enumValue(&c, s, enumValues128) } - w.enumField(n, "parse_mode", string(v.ParseMode), enumValuesPipelinesParseMode) } diff --git a/bundle/config/validate/interpolation_in_auth_config.go b/bundle/config/validate/interpolation_in_auth_config.go index f749f9e0613..5b5a5ab22fb 100644 --- a/bundle/config/validate/interpolation_in_auth_config.go +++ b/bundle/config/validate/interpolation_in_auth_config.go @@ -7,8 +7,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type noInterpolationInAuthConfig struct{} @@ -22,65 +22,58 @@ func (f *noInterpolationInAuthConfig) Name() string { } func (f *noInterpolationInAuthConfig) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - authFields := []string{ + w := &b.Config.Workspace + authFields := []struct { + name string + value string + }{ // Generic attributes. - "host", - "profile", - "auth_type", - "metadata_service_url", + {"host", w.Host}, + {"profile", w.Profile}, + {"auth_type", w.AuthType}, + {"metadata_service_url", w.MetadataServiceURL}, // OAuth specific attributes. - "client_id", + {"client_id", w.ClientID}, // Google specific attributes. - "google_service_account", + {"google_service_account", w.GoogleServiceAccount}, - // Azure specific attributes. - "azure_resource_id", - "azure_use_msi", - "azure_client_id", - "azure_tenant_id", - "azure_environment", - "azure_login_app_id", + // Azure specific attributes. "azure_resource_id" used to be listed here, but it + // is not a workspace key (the field is azure_workspace_resource_id), so it was + // never checked. + {"azure_use_msi", b.Config.ReferenceAt(structpath.NewPath(nil, "workspace", "azure_use_msi"))}, + {"azure_client_id", w.AzureClientID}, + {"azure_tenant_id", w.AzureTenantID}, + {"azure_environment", w.AzureEnvironment}, + {"azure_login_app_id", w.AzureLoginAppID}, // Unified host specific attributes. - "account_id", - "workspace_id", + {"account_id", w.AccountID}, + {"workspace_id", w.WorkspaceID}, } diags := diag.Diagnostics{} - for _, fieldName := range authFields { - p := dyn.NewPath(dyn.Key("workspace"), dyn.Key(fieldName)) - v, err := dyn.GetByPath(b.Config.Value(), p) - if dyn.IsNoSuchKeyError(err) { + for _, field := range authFields { + // Check if the field contains interpolation. + if !structvar.ContainsVariableReference(field.value) { continue } - if err != nil { - return diag.FromErr(err) - } - - vv, ok := v.AsString() + envVar, ok := auth.GetEnvFor(field.name) if !ok { continue } - // Check if the field contains interpolation. - if dynvar.ContainsVariableReference(vv) { - envVar, ok := auth.GetEnvFor(fieldName) - if !ok { - continue - } - - diags = append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Variable interpolation is not supported for fields that configure authentication", - Detail: fmt.Sprintf(`Interpolation is not supported for the field %s. Please set + p := structpath.NewPath(nil, "workspace", field.name) + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Variable interpolation is not supported for fields that configure authentication", + Detail: fmt.Sprintf(`Interpolation is not supported for the field %s. Please set the %s environment variable if you wish to configure this field at runtime.`, p.String(), envVar), - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), - }) - } + Locations: b.Config.GetLocationsOfExact(p), + Paths: []*structpath.PathNode{p}, + }) } return diags diff --git a/bundle/config/validate/job_task_cluster_spec.go b/bundle/config/validate/job_task_cluster_spec.go index e387d83f6fb..71bce7173d5 100644 --- a/bundle/config/validate/job_task_cluster_spec.go +++ b/bundle/config/validate/job_task_cluster_spec.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/jobs" ) @@ -27,13 +26,13 @@ func (v *jobTaskClusterSpec) Name() string { func (v *jobTaskClusterSpec) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { diags := diag.Diagnostics{} - jobsPath := dyn.NewPath(dyn.Key("resources"), dyn.Key("jobs")) + jobsPath := structpath.NewPath(nil, "resources", "jobs") for resourceName, job := range b.Config.Resources.Jobs { - resourcePath := jobsPath.Append(dyn.Key(resourceName)) + resourcePath := structpath.NewPath(jobsPath, resourceName) for taskIndex, task := range job.Tasks { - taskPath := resourcePath.Append(dyn.Key("tasks"), dyn.Index(taskIndex)) + taskPath := structpath.NewPath(resourcePath, "tasks", taskIndex) diags = diags.Extend(validateJobTask(b, task, taskPath)) } @@ -42,7 +41,7 @@ func (v *jobTaskClusterSpec) Apply(ctx context.Context, b *bundle.Bundle) diag.D return diags } -func validateJobTask(b *bundle.Bundle, task jobs.Task, taskPath dyn.Path) diag.Diagnostics { +func validateJobTask(b *bundle.Bundle, task jobs.Task, taskPath *structpath.PathNode) diag.Diagnostics { diags := diag.Diagnostics{} var specified []string @@ -73,7 +72,7 @@ func validateJobTask(b *bundle.Bundle, task jobs.Task, taskPath dyn.Path) diag.D } if task.ForEachTask != nil { - forEachTaskPath := taskPath.Append(dyn.Key("for_each_task"), dyn.Key("task")) + forEachTaskPath := structpath.NewPath(taskPath, "for_each_task", "task") diags = diags.Extend(validateJobTask(b, task.ForEachTask.Task, forEachTaskPath)) } @@ -89,13 +88,12 @@ func validateJobTask(b *bundle.Bundle, task jobs.Task, taskPath dyn.Path) diag.D strings.Join(unspecified, ", "), ) - taskNode := dyn.ToStructPath(taskPath) diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "Missing required cluster or environment settings", Detail: detail, - Locations: b.Config.GetLocationsOf(taskNode), - Paths: []*structpath.PathNode{taskNode}, + Locations: b.Config.GetLocationsOf(taskPath), + Paths: []*structpath.PathNode{taskPath}, }) } } diff --git a/bundle/config/validate/no_interpolation_in_bundle_name.go b/bundle/config/validate/no_interpolation_in_bundle_name.go index 8fc547b174e..0850f27ac15 100644 --- a/bundle/config/validate/no_interpolation_in_bundle_name.go +++ b/bundle/config/validate/no_interpolation_in_bundle_name.go @@ -5,9 +5,9 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/logdiag" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type noInterpolationInBundleName struct{} @@ -21,7 +21,7 @@ func (m *noInterpolationInBundleName) Name() string { } func (m *noInterpolationInBundleName) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - if dynvar.ContainsVariableReference(b.Config.Bundle.Name) { + if structvar.ContainsVariableReference(b.Config.Bundle.Name) { logdiag.LogDiag(ctx, diag.Diagnostic{ Severity: diag.Warning, Summary: `Please do not use variable interpolation in the name of your bundle. The name of your bundle diff --git a/bundle/config/validate/no_variable_reference_in_resource_key.go b/bundle/config/validate/no_variable_reference_in_resource_key.go index 75b8361ce97..6d7cb655092 100644 --- a/bundle/config/validate/no_variable_reference_in_resource_key.go +++ b/bundle/config/validate/no_variable_reference_in_resource_key.go @@ -7,7 +7,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structvar" ) type noVariableReferenceInResourceKey struct{} @@ -36,7 +36,7 @@ func (m *noVariableReferenceInResourceKey) Apply(_ context.Context, b *bundle.Bu pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { key := p[len(p)-1].Key() - if dynvar.ContainsVariableReference(key) { + if structvar.ContainsVariableReference(key) { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("resource key %q must not contain variable references", key), diff --git a/bundle/config/validate/pipeline_development_deprecated.go b/bundle/config/validate/pipeline_development_deprecated.go index 3eb3c77c5f0..cb7f93a5688 100644 --- a/bundle/config/validate/pipeline_development_deprecated.go +++ b/bundle/config/validate/pipeline_development_deprecated.go @@ -2,11 +2,13 @@ package validate import ( "context" + "maps" + "slices" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) const ( @@ -32,35 +34,45 @@ func (v *pipelineDevelopmentDeprecated) Apply(_ context.Context, b *bundle.Bundl presetEnabled := config.IsExplicitlyEnabled(b.Config.Presets.PipelinesDevelopment) - pattern := dyn.NewPattern(dyn.Key("resources"), dyn.Key("pipelines"), dyn.AnyKey(), dyn.Key("development")) - _, err := dyn.MapByPattern(b.Config.Value(), pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - // Only user-written values have a location; the value set by "mode: development" does not. - if len(v.Locations()) == 0 { - return v, nil + pipelines := b.Config.Resources.Pipelines + for _, key := range slices.Sorted(maps.Keys(pipelines)) { + pipeline := pipelines[key] + if pipeline == nil { + continue } + p := structpath.NewPath(nil, "resources", "pipelines", key, "development") + + // The property is set if it is forced (an explicit value: decoding records a + // written false, and the preset keeps the record when it sets true), a + // reference, or true without the preset. A true from the preset + // ("mode: development") is not reported; a "development: true" written in the + // configuration is then indistinguishable from it. + development := pipeline.Development //nolint:staticcheck // SA1019: see apply_presets.go + forced := slices.Contains(pipeline.ForceSendFields, "Development") + if !forced && !b.Config.IsReferenceAt(p) && (!development || presetEnabled) { + continue + } + locs := b.Config.GetLocationsOfExact(p) + diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: pipelineDevelopmentDeprecatedSummary, - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: locs, + Paths: []*structpath.PathNode{p}, }) // The preset overwrites YAML values with true, so a false here was set by a Python // mutator after the preset ran. "bundle run" still sends development: true. - if development, ok := v.AsBool(); ok && presetEnabled && !development { + if presetEnabled && !pipeline.Development { //nolint:staticcheck // SA1019: see above diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: pipelineDevelopmentIgnoredSummary, Detail: pipelineDevelopmentIgnoredDetail, - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: locs, + Paths: []*structpath.PathNode{p}, }) } - return v, nil - }) - if err != nil { - return diag.FromErr(err) } return diags diff --git a/bundle/config/validate/required.go b/bundle/config/validate/required.go index d8892de4f6b..251293e8697 100644 --- a/bundle/config/validate/required.go +++ b/bundle/config/validate/required.go @@ -9,8 +9,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/catalog" ) type required struct{} @@ -26,13 +26,9 @@ func (f *required) Name() string { // Warn for missing fields, based on annotations in the Go SDK / OpenAPI spec. // The walker is generated (required_walk.go) by bundle/internal/validation. func warnForMissingFields(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - w := &walker{diags: diag.Diagnostics{}} - n := rootNode(b.Config.Value()) - requiredConfigRoot(w, &n, &b.Config) - - sortDiagnostics(w.diags) - - return w.diags + diags := walkConfig(&b.Config, true, requiredConfigRoot) + sortDiagnostics(diags) + return diags } // sortDiagnostics orders diagnostics deterministically, since they are collected @@ -64,19 +60,18 @@ func errorForMissingFields(ctx context.Context, b *bundle.Bundle) diag.Diagnosti diags := diag.Diagnostics{} for key, dashboard := range b.Config.Resources.Dashboards { + path := structpath.NewPath(nil, "resources", "dashboards", key) if dashboard.DisplayName == "" { - path := structpath.NewPath(nil, "resources", "dashboards", key) nameLocations = append(nameLocations, b.Config.GetLocationsOf(path)...) namePaths = append(namePaths, path) } if dashboard.WarehouseId == "" { - path := structpath.NewPath(nil, "resources", "dashboards", key) warehouseIdLocations = append(warehouseIdLocations, b.Config.GetLocationsOf(path)...) warehouseIdPaths = append(warehouseIdPaths, path) } } - if len(nameLocations) > 0 { + if len(namePaths) > 0 { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "dashboard display_name is required", @@ -84,7 +79,7 @@ func errorForMissingFields(ctx context.Context, b *bundle.Bundle) diag.Diagnosti Paths: namePaths, }) } - if len(warehouseIdLocations) > 0 { + if len(warehouseIdPaths) > 0 { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "dashboard warehouse_id is required", @@ -120,32 +115,29 @@ func errorForMissingFields(ctx context.Context, b *bundle.Bundle) diag.Diagnosti func errorForInvalidGrants(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { diags := diag.Diagnostics{} - _, err := dyn.MapByPattern( - b.Config.Value(), - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey(), dyn.Key("grants"), dyn.AnyIndex()), - func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - if isMissingOrEmptyString(v.Get("principal")) { + b.Config.Resources.IterateAllGrants(func(typ, name string, grants *[]catalog.PrivilegeAssignment) bool { + for i, g := range *grants { + np := structpath.NewPath(nil, "resources", typ, name, "grants", i) + if g.Principal == "" { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "grant principal is required", - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: b.Config.GetLocationsOfExact(np), + Paths: []*structpath.PathNode{np}, }) } - if isMissingOrEmptySequence(v.Get("privileges")) { + // A pure reference stands for the privileges until it is resolved. + if len(g.Privileges) == 0 && !b.Config.IsReferenceAt(structpath.NewPath(np, "privileges")) { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "grant privileges is required", - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: b.Config.GetLocationsOfExact(np), + Paths: []*structpath.PathNode{np}, }) } - return v, nil - }, - ) - if err != nil { - return diag.FromErr(err) - } + } + return true + }) sortDiagnostics(diags) @@ -162,14 +154,16 @@ func errorForInvalidSecretScopePermissions(ctx context.Context, b *bundle.Bundle if perm.UserName != "" || perm.GroupName != "" || perm.ServicePrincipalName != "" { continue } + scopePath := structpath.NewPath(nil, "resources", "secret_scopes", key) + path := structpath.NewPath(scopePath, "permissions", i) // ApplyBundlePermissions rebuilds permissions via convert.FromTyped and drops // per-entry locations, so point at the scope. diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "secret scope permission principal is required", Detail: "Set one of user_name, group_name or service_principal_name", - Locations: b.Config.GetLocationsOf(structpath.NewPath(nil, "resources", "secret_scopes", key)), - Paths: structpath.NewPathSlice("resources", "secret_scopes", key, "permissions", i), + Locations: b.Config.GetLocationsOf(scopePath), + Paths: []*structpath.PathNode{path}, }) } } @@ -179,30 +173,6 @@ func errorForInvalidSecretScopePermissions(ctx context.Context, b *bundle.Bundle return diags } -// isMissingOrEmptyString reports whether v is unset, null, or an empty string. -func isMissingOrEmptyString(v dyn.Value) bool { - switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: - return true - case dyn.KindString: - return v.MustString() == "" - default: - return false - } -} - -// isMissingOrEmptySequence reports whether v is unset, null, or an empty sequence. -func isMissingOrEmptySequence(v dyn.Value) bool { - switch v.Kind() { - case dyn.KindInvalid, dyn.KindNil: - return true - case dyn.KindSequence: - return len(v.MustSequence()) == 0 - default: - return false - } -} - func (f *required) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { diags := errorForMissingFields(ctx, b) diags = diags.Extend(errorForInvalidGrants(ctx, b)) diff --git a/bundle/config/validate/required_walk.go b/bundle/config/validate/required_walk.go index fbabdeec6fb..121933e85b3 100644 --- a/bundle/config/validate/required_walk.go +++ b/bundle/config/validate/required_walk.go @@ -1,12 +1,13 @@ // Code generated by bundle/internal/validation. DO NOT EDIT. -// requiredConfigRoot reports the required fields that are not set. +// requiredWalk reports required fields that are not set. package validate import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/databricks-sdk-go/service/apps" "github.com/databricks/databricks-sdk-go/service/catalog" "github.com/databricks/databricks-sdk-go/service/compute" @@ -20,4392 +21,5667 @@ import ( "github.com/databricks/databricks-sdk-go/service/workspace" ) -func requiredConfigRoot(w *walker, n *node, v *config.Root) { +func requiredConfigRoot(w *walker, n *walk.Node, v *config.Root) { if v == nil { - v = &config.Root{} + return } if len(v.Artifacts) > 0 { - c := n.child("artifacts") - for k0, x0 := range v.Artifacts { - if k0 == "" { + c := n.Child("artifacts") + for k, x := range v.Artifacts { + if k == "" { continue } - e := c.child(k0) - requiredConfigArtifact(w, &e, x0) + e := c.Child(k) + requiredConfigArtifact(w, &e, x) } } - if c := n.child("bundle"); c.exists() { - requiredConfigBundle(w, &c, &v.Bundle) + if x := &v.Bundle; !walk.IsZeroStruct(x) || n.HasRefs("bundle") { + c := n.Child("bundle") + requiredConfigBundle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("resources"); c.exists() { - requiredConfigResources(w, &c, &v.Resources) + if x := &v.Resources; !walk.IsZeroStruct(x) || n.HasRefs("resources") { + c := n.Child("resources") + requiredConfigResources(w, &c, x) } if len(v.Scripts) > 0 { - c := n.child("scripts") - for k0, x0 := range v.Scripts { - if k0 == "" { + c := n.Child("scripts") + for k, x := range v.Scripts { + if k == "" { continue } - e := c.child(k0) - requiredConfigScript(w, &e, &x0) + e := c.Child(k) + requiredConfigScript(w, &e, &x) } } } -func requiredConfigArtifact(w *walker, n *node, v *config.Artifact) { +func requiredConfigArtifact(w *walker, n *walk.Node, v *config.Artifact) { if v == nil { - v = &config.Artifact{} + return } if len(v.Files) > 0 { - c := n.child("files") - for i0 := range v.Files { - e := c.elem(i0) - requiredConfigArtifactFile(w, &e, &v.Files[i0]) + c := n.Child("files") + for i := range v.Files { + e := c.Elem(i) + requiredConfigArtifactFile(w, &e, &v.Files[i]) } } } -func requiredConfigBundle(w *walker, n *node, v *config.Bundle) { +func requiredConfigBundle(w *walker, n *walk.Node, v *config.Bundle) { if v == nil { - v = &config.Bundle{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredResourcesPermission(w *walker, n *node, v *resources.Permission) { +func requiredResourcesPermission(w *walker, n *walk.Node, v *resources.Permission) { if v == nil { - v = &resources.Permission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredConfigResources(w *walker, n *node, v *config.Resources) { +func requiredConfigResources(w *walker, n *walk.Node, v *config.Resources) { if v == nil { - v = &config.Resources{} + return } if len(v.Alerts) > 0 { - c := n.child("alerts") - for k0, x0 := range v.Alerts { - if k0 == "" { + c := n.Child("alerts") + for k, x := range v.Alerts { + if k == "" { continue } - e := c.child(k0) - requiredResourcesAlert(w, &e, x0) + e := c.Child(k) + requiredResourcesAlert(w, &e, x) } } if len(v.Apps) > 0 { - c := n.child("apps") - for k0, x0 := range v.Apps { - if k0 == "" { + c := n.Child("apps") + for k, x := range v.Apps { + if k == "" { continue } - e := c.child(k0) - requiredResourcesApp(w, &e, x0) + e := c.Child(k) + requiredResourcesApp(w, &e, x) } } if len(v.Catalogs) > 0 { - c := n.child("catalogs") - for k0, x0 := range v.Catalogs { - if k0 == "" { + c := n.Child("catalogs") + for k, x := range v.Catalogs { + if k == "" { continue } - e := c.child(k0) - requiredResourcesCatalog(w, &e, x0) + e := c.Child(k) + requiredResourcesCatalog(w, &e, x) } } if len(v.ClusterPolicies) > 0 { - c := n.child("cluster_policies") - for k0, x0 := range v.ClusterPolicies { - if k0 == "" { + c := n.Child("cluster_policies") + for k, x := range v.ClusterPolicies { + if k == "" { continue } - e := c.child(k0) - requiredResourcesClusterPolicy(w, &e, x0) + e := c.Child(k) + requiredResourcesClusterPolicy(w, &e, x) } } if len(v.Clusters) > 0 { - c := n.child("clusters") - for k0, x0 := range v.Clusters { - if k0 == "" { + c := n.Child("clusters") + for k, x := range v.Clusters { + if k == "" { continue } - e := c.child(k0) - requiredResourcesCluster(w, &e, x0) + e := c.Child(k) + requiredResourcesCluster(w, &e, x) } } if len(v.Dashboards) > 0 { - c := n.child("dashboards") - for k0, x0 := range v.Dashboards { - if k0 == "" { + c := n.Child("dashboards") + for k, x := range v.Dashboards { + if k == "" { continue } - e := c.child(k0) - requiredResourcesDashboard(w, &e, x0) + e := c.Child(k) + requiredResourcesDashboard(w, &e, x) } } if len(v.DatabaseCatalogs) > 0 { - c := n.child("database_catalogs") - for k0, x0 := range v.DatabaseCatalogs { - if k0 == "" { + c := n.Child("database_catalogs") + for k, x := range v.DatabaseCatalogs { + if k == "" { continue } - e := c.child(k0) - requiredResourcesDatabaseCatalog(w, &e, x0) + e := c.Child(k) + requiredResourcesDatabaseCatalog(w, &e, x) } } if len(v.DatabaseInstances) > 0 { - c := n.child("database_instances") - for k0, x0 := range v.DatabaseInstances { - if k0 == "" { + c := n.Child("database_instances") + for k, x := range v.DatabaseInstances { + if k == "" { continue } - e := c.child(k0) - requiredResourcesDatabaseInstance(w, &e, x0) + e := c.Child(k) + requiredResourcesDatabaseInstance(w, &e, x) } } if len(v.Experiments) > 0 { - c := n.child("experiments") - for k0, x0 := range v.Experiments { - if k0 == "" { + c := n.Child("experiments") + for k, x := range v.Experiments { + if k == "" { continue } - e := c.child(k0) - requiredResourcesMlflowExperiment(w, &e, x0) + e := c.Child(k) + requiredResourcesMlflowExperiment(w, &e, x) } } if len(v.ExternalLocations) > 0 { - c := n.child("external_locations") - for k0, x0 := range v.ExternalLocations { - if k0 == "" { + c := n.Child("external_locations") + for k, x := range v.ExternalLocations { + if k == "" { continue } - e := c.child(k0) - requiredResourcesExternalLocation(w, &e, x0) + e := c.Child(k) + requiredResourcesExternalLocation(w, &e, x) } } if len(v.Features) > 0 { - c := n.child("features") - for k0, x0 := range v.Features { - if k0 == "" { + c := n.Child("features") + for k, x := range v.Features { + if k == "" { continue } - e := c.child(k0) - requiredResourcesFeature(w, &e, x0) + e := c.Child(k) + requiredResourcesFeature(w, &e, x) } } if len(v.GenieSpaces) > 0 { - c := n.child("genie_spaces") - for k0, x0 := range v.GenieSpaces { - if k0 == "" { + c := n.Child("genie_spaces") + for k, x := range v.GenieSpaces { + if k == "" { continue } - e := c.child(k0) - requiredResourcesGenieSpace(w, &e, x0) + e := c.Child(k) + requiredResourcesGenieSpace(w, &e, x) } } if len(v.InstancePools) > 0 { - c := n.child("instance_pools") - for k0, x0 := range v.InstancePools { - if k0 == "" { + c := n.Child("instance_pools") + for k, x := range v.InstancePools { + if k == "" { continue } - e := c.child(k0) - requiredResourcesInstancePool(w, &e, x0) + e := c.Child(k) + requiredResourcesInstancePool(w, &e, x) } } if len(v.JobRuns) > 0 { - c := n.child("job_runs") - for k0, x0 := range v.JobRuns { - if k0 == "" { + c := n.Child("job_runs") + for k, x := range v.JobRuns { + if k == "" { continue } - e := c.child(k0) - requiredResourcesJobRun(w, &e, x0) + e := c.Child(k) + requiredResourcesJobRun(w, &e, x) } } if len(v.Jobs) > 0 { - c := n.child("jobs") - for k0, x0 := range v.Jobs { - if k0 == "" { + c := n.Child("jobs") + for k, x := range v.Jobs { + if k == "" { continue } - e := c.child(k0) - requiredResourcesJob(w, &e, x0) + e := c.Child(k) + requiredResourcesJob(w, &e, x) } } if len(v.McpServices) > 0 { - c := n.child("mcp_services") - for k0, x0 := range v.McpServices { - if k0 == "" { + c := n.Child("mcp_services") + for k, x := range v.McpServices { + if k == "" { continue } - e := c.child(k0) - requiredResourcesMcpService(w, &e, x0) + e := c.Child(k) + requiredResourcesMcpService(w, &e, x) } } if len(v.ModelProviderServices) > 0 { - c := n.child("model_provider_services") - for k0, x0 := range v.ModelProviderServices { - if k0 == "" { + c := n.Child("model_provider_services") + for k, x := range v.ModelProviderServices { + if k == "" { continue } - e := c.child(k0) - requiredResourcesModelProviderService(w, &e, x0) + e := c.Child(k) + requiredResourcesModelProviderService(w, &e, x) } } if len(v.ModelServices) > 0 { - c := n.child("model_services") - for k0, x0 := range v.ModelServices { - if k0 == "" { + c := n.Child("model_services") + for k, x := range v.ModelServices { + if k == "" { continue } - e := c.child(k0) - requiredResourcesModelService(w, &e, x0) + e := c.Child(k) + requiredResourcesModelService(w, &e, x) } } if len(v.ModelServingEndpoints) > 0 { - c := n.child("model_serving_endpoints") - for k0, x0 := range v.ModelServingEndpoints { - if k0 == "" { + c := n.Child("model_serving_endpoints") + for k, x := range v.ModelServingEndpoints { + if k == "" { continue } - e := c.child(k0) - requiredResourcesModelServingEndpoint(w, &e, x0) + e := c.Child(k) + requiredResourcesModelServingEndpoint(w, &e, x) } } if len(v.Models) > 0 { - c := n.child("models") - for k0, x0 := range v.Models { - if k0 == "" { + c := n.Child("models") + for k, x := range v.Models { + if k == "" { continue } - e := c.child(k0) - requiredResourcesMlflowModel(w, &e, x0) + e := c.Child(k) + requiredResourcesMlflowModel(w, &e, x) } } if len(v.Pipelines) > 0 { - c := n.child("pipelines") - for k0, x0 := range v.Pipelines { - if k0 == "" { + c := n.Child("pipelines") + for k, x := range v.Pipelines { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPipeline(w, &e, x0) + e := c.Child(k) + requiredResourcesPipeline(w, &e, x) } } if len(v.PostgresBranches) > 0 { - c := n.child("postgres_branches") - for k0, x0 := range v.PostgresBranches { - if k0 == "" { + c := n.Child("postgres_branches") + for k, x := range v.PostgresBranches { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresBranch(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresBranch(w, &e, x) } } if len(v.PostgresCatalogs) > 0 { - c := n.child("postgres_catalogs") - for k0, x0 := range v.PostgresCatalogs { - if k0 == "" { + c := n.Child("postgres_catalogs") + for k, x := range v.PostgresCatalogs { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresCatalog(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresCatalog(w, &e, x) } } if len(v.PostgresDatabases) > 0 { - c := n.child("postgres_databases") - for k0, x0 := range v.PostgresDatabases { - if k0 == "" { + c := n.Child("postgres_databases") + for k, x := range v.PostgresDatabases { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresDatabase(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresDatabase(w, &e, x) } } if len(v.PostgresEndpoints) > 0 { - c := n.child("postgres_endpoints") - for k0, x0 := range v.PostgresEndpoints { - if k0 == "" { + c := n.Child("postgres_endpoints") + for k, x := range v.PostgresEndpoints { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresEndpoint(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresEndpoint(w, &e, x) } } if len(v.PostgresProjects) > 0 { - c := n.child("postgres_projects") - for k0, x0 := range v.PostgresProjects { - if k0 == "" { + c := n.Child("postgres_projects") + for k, x := range v.PostgresProjects { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresProject(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresProject(w, &e, x) } } if len(v.PostgresRoles) > 0 { - c := n.child("postgres_roles") - for k0, x0 := range v.PostgresRoles { - if k0 == "" { + c := n.Child("postgres_roles") + for k, x := range v.PostgresRoles { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresRole(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresRole(w, &e, x) } } if len(v.PostgresSnapshotSchedules) > 0 { - c := n.child("postgres_snapshot_schedules") - for k0, x0 := range v.PostgresSnapshotSchedules { - if k0 == "" { + c := n.Child("postgres_snapshot_schedules") + for k, x := range v.PostgresSnapshotSchedules { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresSnapshotSchedule(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresSnapshotSchedule(w, &e, x) } } if len(v.PostgresSyncedTables) > 0 { - c := n.child("postgres_synced_tables") - for k0, x0 := range v.PostgresSyncedTables { - if k0 == "" { + c := n.Child("postgres_synced_tables") + for k, x := range v.PostgresSyncedTables { + if k == "" { continue } - e := c.child(k0) - requiredResourcesPostgresSyncedTable(w, &e, x0) + e := c.Child(k) + requiredResourcesPostgresSyncedTable(w, &e, x) } } if len(v.QualityMonitors) > 0 { - c := n.child("quality_monitors") - for k0, x0 := range v.QualityMonitors { - if k0 == "" { + c := n.Child("quality_monitors") + for k, x := range v.QualityMonitors { + if k == "" { continue } - e := c.child(k0) - requiredResourcesQualityMonitor(w, &e, x0) + e := c.Child(k) + requiredResourcesQualityMonitor(w, &e, x) } } if len(v.RegisteredModels) > 0 { - c := n.child("registered_models") - for k0, x0 := range v.RegisteredModels { - if k0 == "" { + c := n.Child("registered_models") + for k, x := range v.RegisteredModels { + if k == "" { continue } - e := c.child(k0) - requiredResourcesRegisteredModel(w, &e, x0) + e := c.Child(k) + requiredResourcesRegisteredModel(w, &e, x) } } if len(v.Schemas) > 0 { - c := n.child("schemas") - for k0, x0 := range v.Schemas { - if k0 == "" { + c := n.Child("schemas") + for k, x := range v.Schemas { + if k == "" { continue } - e := c.child(k0) - requiredResourcesSchema(w, &e, x0) + e := c.Child(k) + requiredResourcesSchema(w, &e, x) } } if len(v.SecretScopes) > 0 { - c := n.child("secret_scopes") - for k0, x0 := range v.SecretScopes { - if k0 == "" { + c := n.Child("secret_scopes") + for k, x := range v.SecretScopes { + if k == "" { continue } - e := c.child(k0) - requiredResourcesSecretScope(w, &e, x0) + e := c.Child(k) + requiredResourcesSecretScope(w, &e, x) } } if len(v.Secrets) > 0 { - c := n.child("secrets") - for k0, x0 := range v.Secrets { - if k0 == "" { + c := n.Child("secrets") + for k, x := range v.Secrets { + if k == "" { continue } - e := c.child(k0) - requiredResourcesSecret(w, &e, x0) + e := c.Child(k) + requiredResourcesSecret(w, &e, x) } } if len(v.SqlWarehouses) > 0 { - c := n.child("sql_warehouses") - for k0, x0 := range v.SqlWarehouses { - if k0 == "" { + c := n.Child("sql_warehouses") + for k, x := range v.SqlWarehouses { + if k == "" { continue } - e := c.child(k0) - requiredResourcesSqlWarehouse(w, &e, x0) + e := c.Child(k) + requiredResourcesSqlWarehouse(w, &e, x) } } if len(v.SyncedDatabaseTables) > 0 { - c := n.child("synced_database_tables") - for k0, x0 := range v.SyncedDatabaseTables { - if k0 == "" { + c := n.Child("synced_database_tables") + for k, x := range v.SyncedDatabaseTables { + if k == "" { continue } - e := c.child(k0) - requiredResourcesSyncedDatabaseTable(w, &e, x0) + e := c.Child(k) + requiredResourcesSyncedDatabaseTable(w, &e, x) } } if len(v.VectorSearchEndpoints) > 0 { - c := n.child("vector_search_endpoints") - for k0, x0 := range v.VectorSearchEndpoints { - if k0 == "" { + c := n.Child("vector_search_endpoints") + for k, x := range v.VectorSearchEndpoints { + if k == "" { continue } - e := c.child(k0) - requiredResourcesVectorSearchEndpoint(w, &e, x0) + e := c.Child(k) + requiredResourcesVectorSearchEndpoint(w, &e, x) } } if len(v.VectorSearchIndexes) > 0 { - c := n.child("vector_search_indexes") - for k0, x0 := range v.VectorSearchIndexes { - if k0 == "" { + c := n.Child("vector_search_indexes") + for k, x := range v.VectorSearchIndexes { + if k == "" { continue } - e := c.child(k0) - requiredResourcesVectorSearchIndex(w, &e, x0) + e := c.Child(k) + requiredResourcesVectorSearchIndex(w, &e, x) } } if len(v.Volumes) > 0 { - c := n.child("volumes") - for k0, x0 := range v.Volumes { - if k0 == "" { + c := n.Child("volumes") + for k, x := range v.Volumes { + if k == "" { continue } - e := c.child(k0) - requiredResourcesVolume(w, &e, x0) + e := c.Child(k) + requiredResourcesVolume(w, &e, x) } } } -func requiredConfigScript(w *walker, n *node, v *config.Script) { +func requiredConfigScript(w *walker, n *walk.Node, v *config.Script) { if v == nil { - v = &config.Script{} + w.missing(n, "content") + return } - if !(v.Content != "") && !n.has("content") { + if !(v.Content != "") { w.missing(n, "content") } } -func requiredConfigArtifactFile(w *walker, n *node, v *config.ArtifactFile) { +func requiredConfigArtifactFile(w *walker, n *walk.Node, v *config.ArtifactFile) { if v == nil { - v = &config.ArtifactFile{} + w.missing(n, "source") + return } - if !(v.Source != "") && !n.has("source") { + if !(v.Source != "") { w.missing(n, "source") } } -func requiredResourcesAlert(w *walker, n *node, v *resources.Alert) { +func requiredResourcesAlert(w *walker, n *walk.Node, v *resources.Alert) { if v == nil { - v = &resources.Alert{} + w.missing(n, "display_name") + w.missing(n, "evaluation") + w.missing(n, "query_text") + w.missing(n, "schedule") + w.missing(n, "warehouse_id") + return } - if !(v.AlertV2.DisplayName != "") && !n.has("display_name") { + if !(v.AlertV2.DisplayName != "" || walk.Forced(v.AlertV2.ForceSendFields, "DisplayName")) { w.missing(n, "display_name") } - if !n.has("evaluation") { + if !(!walk.IsZeroStruct(&v.AlertV2.Evaluation) || walk.Forced(v.AlertV2.ForceSendFields, "Evaluation") || n.HasRefs("evaluation")) { w.missing(n, "evaluation") } - if !(v.AlertV2.QueryText != "") && !n.has("query_text") { + if !(v.AlertV2.QueryText != "" || walk.Forced(v.AlertV2.ForceSendFields, "QueryText")) { w.missing(n, "query_text") } - if !n.has("schedule") { + if !(!walk.IsZeroStruct(&v.AlertV2.Schedule) || walk.Forced(v.AlertV2.ForceSendFields, "Schedule") || n.HasRefs("schedule")) { w.missing(n, "schedule") } - if !(v.AlertV2.WarehouseId != "") && !n.has("warehouse_id") { + if !(v.AlertV2.WarehouseId != "" || walk.Forced(v.AlertV2.ForceSendFields, "WarehouseId")) { w.missing(n, "warehouse_id") } - if c := n.child("evaluation"); c.exists() { - requiredSqlAlertV2Evaluation(w, &c, &v.AlertV2.Evaluation) + if x := &v.AlertV2.Evaluation; !walk.IsZeroStruct(x) || walk.Forced(v.AlertV2.ForceSendFields, "Evaluation") || n.HasRefs("evaluation") { + c := n.Child("evaluation") + requiredSqlAlertV2Evaluation(w, &c, x) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.AlertV2.Parameters) > 0 { - c := n.child("parameters") - for i0 := range v.AlertV2.Parameters { - e := c.elem(i0) - requiredSqlAlertStatementParameter(w, &e, &v.AlertV2.Parameters[i0]) + c := n.Child("parameters") + for i := range v.AlertV2.Parameters { + e := c.Elem(i) + requiredSqlAlertStatementParameter(w, &e, &v.AlertV2.Parameters[i]) } } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("schedule"); c.exists() { - requiredSqlCronSchedule(w, &c, &v.AlertV2.Schedule) + if x := &v.AlertV2.Schedule; !walk.IsZeroStruct(x) || walk.Forced(v.AlertV2.ForceSendFields, "Schedule") || n.HasRefs("schedule") { + c := n.Child("schedule") + requiredSqlCronSchedule(w, &c, x) } } -func requiredResourcesApp(w *walker, n *node, v *resources.App) { +func requiredResourcesApp(w *walker, n *walk.Node, v *resources.App) { if v == nil { - v = &resources.App{} - } - if !(v.App.Name != "") && !n.has("name") { w.missing(n, "name") + return } - if c := n.child("active_deployment"); c.exists() { - requiredAppsAppDeployment(w, &c, v.App.ActiveDeployment) - } - if c := n.child("config"); c.exists() { - requiredResourcesAppConfig(w, &c, v.Config) - } - if c := n.child("default_git_source"); c.exists() { - requiredAppsGitSource(w, &c, v.App.DefaultGitSource) - } - if c := n.child("git_repository"); c.exists() { - requiredAppsGitRepository(w, &c, v.App.GitRepository) - } - if c := n.child("git_source"); c.exists() { - requiredAppsGitSource(w, &c, v.App.GitSource) - } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycleWithStarted(w, &c, v.Lifecycle) + if !(v.App.Name != "" || walk.Forced(v.App.ForceSendFields, "Name")) { + w.missing(n, "name") } - if c := n.child("pending_deployment"); c.exists() { - requiredAppsAppDeployment(w, &c, v.App.PendingDeployment) + if x := v.App.ActiveDeployment; x != nil { + c := n.Child("active_deployment") + requiredAppsAppDeployment(w, &c, x) + } else if walk.Forced(v.App.ForceSendFields, "ActiveDeployment") || n.IsRef("active_deployment") { + c := n.Child("active_deployment") + requiredAppsAppDeployment(w, &c, nil) + } + if x := v.Config; x != nil { + c := n.Child("config") + requiredResourcesAppConfig(w, &c, x) + } else if n.IsRef("config") { + c := n.Child("config") + requiredResourcesAppConfig(w, &c, nil) + } + if x := v.App.DefaultGitSource; x != nil { + c := n.Child("default_git_source") + requiredAppsGitSource(w, &c, x) + } else if walk.Forced(v.App.ForceSendFields, "DefaultGitSource") || n.IsRef("default_git_source") { + c := n.Child("default_git_source") + requiredAppsGitSource(w, &c, nil) + } + if x := v.App.GitRepository; x != nil { + c := n.Child("git_repository") + requiredAppsGitRepository(w, &c, x) + } else if walk.Forced(v.App.ForceSendFields, "GitRepository") || n.IsRef("git_repository") { + c := n.Child("git_repository") + requiredAppsGitRepository(w, &c, nil) + } + if x := v.App.GitSource; x != nil { + c := n.Child("git_source") + requiredAppsGitSource(w, &c, x) + } else if walk.Forced(v.App.ForceSendFields, "GitSource") || n.IsRef("git_source") { + c := n.Child("git_source") + requiredAppsGitSource(w, &c, nil) + } + if x := v.Lifecycle; x != nil { + c := n.Child("lifecycle") + requiredResourcesLifecycleWithStarted(w, &c, x) + } else if n.IsRef("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycleWithStarted(w, &c, nil) + } + if x := v.App.PendingDeployment; x != nil { + c := n.Child("pending_deployment") + requiredAppsAppDeployment(w, &c, x) + } else if walk.Forced(v.App.ForceSendFields, "PendingDeployment") || n.IsRef("pending_deployment") { + c := n.Child("pending_deployment") + requiredAppsAppDeployment(w, &c, nil) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesAppPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesAppPermission(w, &e, &v.Permissions[i]) } } if len(v.App.Resources) > 0 { - c := n.child("resources") - for i0 := range v.App.Resources { - e := c.elem(i0) - requiredAppsAppResource(w, &e, &v.App.Resources[i0]) + c := n.Child("resources") + for i := range v.App.Resources { + e := c.Elem(i) + requiredAppsAppResource(w, &e, &v.App.Resources[i]) } } if len(v.App.TelemetryExportDestinations) > 0 { - c := n.child("telemetry_export_destinations") - for i0 := range v.App.TelemetryExportDestinations { - e := c.elem(i0) - requiredAppsTelemetryExportDestination(w, &e, &v.App.TelemetryExportDestinations[i0]) + c := n.Child("telemetry_export_destinations") + for i := range v.App.TelemetryExportDestinations { + e := c.Elem(i) + requiredAppsTelemetryExportDestination(w, &e, &v.App.TelemetryExportDestinations[i]) } } } -func requiredResourcesCatalog(w *walker, n *node, v *resources.Catalog) { +func requiredResourcesCatalog(w *walker, n *walk.Node, v *resources.Catalog) { if v == nil { - v = &resources.Catalog{} + w.missing(n, "name") + return } - if !(v.CreateCatalog.Name != "") && !n.has("name") { + if !(v.CreateCatalog.Name != "" || walk.Forced(v.CreateCatalog.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } - if c := n.child("managed_encryption_settings"); c.exists() { - requiredCatalogEncryptionSettings(w, &c, v.CreateCatalog.ManagedEncryptionSettings) + if x := v.CreateCatalog.ManagedEncryptionSettings; x != nil { + c := n.Child("managed_encryption_settings") + requiredCatalogEncryptionSettings(w, &c, x) + } else if walk.Forced(v.CreateCatalog.ForceSendFields, "ManagedEncryptionSettings") || n.IsRef("managed_encryption_settings") { + c := n.Child("managed_encryption_settings") + requiredCatalogEncryptionSettings(w, &c, nil) } } -func requiredResourcesClusterPolicy(w *walker, n *node, v *resources.ClusterPolicy) { +func requiredResourcesClusterPolicy(w *walker, n *walk.Node, v *resources.ClusterPolicy) { if v == nil { - v = &resources.ClusterPolicy{} + return } if len(v.CreatePolicy.Libraries) > 0 { - c := n.child("libraries") - for i0 := range v.CreatePolicy.Libraries { - e := c.elem(i0) - requiredComputeLibrary(w, &e, &v.CreatePolicy.Libraries[i0]) + c := n.Child("libraries") + for i := range v.CreatePolicy.Libraries { + e := c.Elem(i) + requiredComputeLibrary(w, &e, &v.CreatePolicy.Libraries[i]) } } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesClusterPolicyPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesClusterPolicyPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesCluster(w *walker, n *node, v *resources.Cluster) { +func requiredResourcesCluster(w *walker, n *walk.Node, v *resources.Cluster) { if v == nil { - v = &resources.Cluster{} + return } - if c := n.child("cluster_log_conf"); c.exists() { - requiredComputeClusterLogConf(w, &c, v.ClusterSpec.ClusterLogConf) + if x := v.ClusterSpec.ClusterLogConf; x != nil { + c := n.Child("cluster_log_conf") + requiredComputeClusterLogConf(w, &c, x) + } else if walk.Forced(v.ClusterSpec.ForceSendFields, "ClusterLogConf") || n.IsRef("cluster_log_conf") { + c := n.Child("cluster_log_conf") + requiredComputeClusterLogConf(w, &c, nil) } if len(v.ClusterSpec.InitScripts) > 0 { - c := n.child("init_scripts") - for i0 := range v.ClusterSpec.InitScripts { - e := c.elem(i0) - requiredComputeInitScriptInfo(w, &e, &v.ClusterSpec.InitScripts[i0]) + c := n.Child("init_scripts") + for i := range v.ClusterSpec.InitScripts { + e := c.Elem(i) + requiredComputeInitScriptInfo(w, &e, &v.ClusterSpec.InitScripts[i]) } } if len(v.Libraries) > 0 { - c := n.child("libraries") - for i0 := range v.Libraries { - e := c.elem(i0) - requiredComputeLibrary(w, &e, &v.Libraries[i0]) + c := n.Child("libraries") + for i := range v.Libraries { + e := c.Elem(i) + requiredComputeLibrary(w, &e, &v.Libraries[i]) } } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycleWithStarted(w, &c, v.Lifecycle) + if x := v.Lifecycle; x != nil { + c := n.Child("lifecycle") + requiredResourcesLifecycleWithStarted(w, &c, x) + } else if n.IsRef("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycleWithStarted(w, &c, nil) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesClusterPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesClusterPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("workload_type"); c.exists() { - requiredComputeWorkloadType(w, &c, v.ClusterSpec.WorkloadType) + if x := v.ClusterSpec.WorkloadType; x != nil { + c := n.Child("workload_type") + requiredComputeWorkloadType(w, &c, x) + } else if walk.Forced(v.ClusterSpec.ForceSendFields, "WorkloadType") || n.IsRef("workload_type") { + c := n.Child("workload_type") + requiredComputeWorkloadType(w, &c, nil) } } -func requiredResourcesDashboard(w *walker, n *node, v *resources.Dashboard) { +func requiredResourcesDashboard(w *walker, n *walk.Node, v *resources.Dashboard) { if v == nil { - v = &resources.Dashboard{} + return } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesDatabaseCatalog(w *walker, n *node, v *resources.DatabaseCatalog) { +func requiredResourcesDatabaseCatalog(w *walker, n *walk.Node, v *resources.DatabaseCatalog) { if v == nil { - v = &resources.DatabaseCatalog{} + w.missing(n, "database_instance_name") + w.missing(n, "database_name") + w.missing(n, "name") + return } - if !(v.DatabaseCatalog.DatabaseInstanceName != "") && !n.has("database_instance_name") { + if !(v.DatabaseCatalog.DatabaseInstanceName != "" || walk.Forced(v.DatabaseCatalog.ForceSendFields, "DatabaseInstanceName")) { w.missing(n, "database_instance_name") } - if !(v.DatabaseCatalog.DatabaseName != "") && !n.has("database_name") { + if !(v.DatabaseCatalog.DatabaseName != "" || walk.Forced(v.DatabaseCatalog.ForceSendFields, "DatabaseName")) { w.missing(n, "database_name") } - if !(v.DatabaseCatalog.Name != "") && !n.has("name") { + if !(v.DatabaseCatalog.Name != "" || walk.Forced(v.DatabaseCatalog.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesDatabaseInstance(w *walker, n *node, v *resources.DatabaseInstance) { +func requiredResourcesDatabaseInstance(w *walker, n *walk.Node, v *resources.DatabaseInstance) { if v == nil { - v = &resources.DatabaseInstance{} + w.missing(n, "name") + return } - if !(v.DatabaseInstance.Name != "") && !n.has("name") { + if !(v.DatabaseInstance.Name != "" || walk.Forced(v.DatabaseInstance.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesMlflowExperiment(w *walker, n *node, v *resources.MlflowExperiment) { +func requiredResourcesMlflowExperiment(w *walker, n *walk.Node, v *resources.MlflowExperiment) { if v == nil { - v = &resources.MlflowExperiment{} + w.missing(n, "name") + return } - if !(v.CreateExperiment.Name != "") && !n.has("name") { + if !(v.CreateExperiment.Name != "" || walk.Forced(v.CreateExperiment.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesMlflowExperimentPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesMlflowExperimentPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("trace_location"); c.exists() { - requiredMlExperimentTraceLocation(w, &c, v.CreateExperiment.TraceLocation) + if x := v.CreateExperiment.TraceLocation; x != nil { + c := n.Child("trace_location") + requiredMlExperimentTraceLocation(w, &c, x) + } else if walk.Forced(v.CreateExperiment.ForceSendFields, "TraceLocation") || n.IsRef("trace_location") { + c := n.Child("trace_location") + requiredMlExperimentTraceLocation(w, &c, nil) } } -func requiredResourcesExternalLocation(w *walker, n *node, v *resources.ExternalLocation) { +func requiredResourcesExternalLocation(w *walker, n *walk.Node, v *resources.ExternalLocation) { if v == nil { - v = &resources.ExternalLocation{} + w.missing(n, "credential_name") + w.missing(n, "name") + w.missing(n, "url") + return } - if !(v.CreateExternalLocation.CredentialName != "") && !n.has("credential_name") { + if !(v.CreateExternalLocation.CredentialName != "" || walk.Forced(v.CreateExternalLocation.ForceSendFields, "CredentialName")) { w.missing(n, "credential_name") } - if !(v.CreateExternalLocation.Name != "") && !n.has("name") { + if !(v.CreateExternalLocation.Name != "" || walk.Forced(v.CreateExternalLocation.ForceSendFields, "Name")) { w.missing(n, "name") } - if !(v.CreateExternalLocation.Url != "") && !n.has("url") { + if !(v.CreateExternalLocation.Url != "" || walk.Forced(v.CreateExternalLocation.ForceSendFields, "Url")) { w.missing(n, "url") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.Lifecycle) + if x := &v.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesFeature(w *walker, n *node, v *resources.Feature) { +func requiredResourcesFeature(w *walker, n *walk.Node, v *resources.Feature) { if v == nil { - v = &resources.Feature{} + w.missing(n, "full_name") + w.missing(n, "function") + w.missing(n, "source") + return } - if !(v.Feature.FullName != "") && !n.has("full_name") { + if !(v.Feature.FullName != "" || walk.Forced(v.Feature.ForceSendFields, "FullName")) { w.missing(n, "full_name") } - if !n.has("function") { + if !(!walk.IsZeroStruct(&v.Feature.Function) || walk.Forced(v.Feature.ForceSendFields, "Function") || n.HasRefs("function")) { w.missing(n, "function") } - if !n.has("source") { + if !(!walk.IsZeroStruct(&v.Feature.Source) || walk.Forced(v.Feature.ForceSendFields, "Source") || n.HasRefs("source")) { w.missing(n, "source") } if len(v.Feature.Entities) > 0 { - c := n.child("entities") - for i0 := range v.Feature.Entities { - e := c.elem(i0) - requiredMlEntityColumn(w, &e, &v.Feature.Entities[i0]) + c := n.Child("entities") + for i := range v.Feature.Entities { + e := c.Elem(i) + requiredMlEntityColumn(w, &e, &v.Feature.Entities[i]) } } - if c := n.child("function"); c.exists() { - requiredMlFunction(w, &c, &v.Feature.Function) + if x := &v.Feature.Function; !walk.IsZeroStruct(x) || walk.Forced(v.Feature.ForceSendFields, "Function") || n.HasRefs("function") { + c := n.Child("function") + requiredMlFunction(w, &c, x) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } - if c := n.child("source"); c.exists() { - requiredMlDataSource(w, &c, &v.Feature.Source) + if x := &v.Feature.Source; !walk.IsZeroStruct(x) || walk.Forced(v.Feature.ForceSendFields, "Source") || n.HasRefs("source") { + c := n.Child("source") + requiredMlDataSource(w, &c, x) } - if c := n.child("time_window"); c.exists() { - requiredMlTimeWindow(w, &c, v.Feature.TimeWindow) + if x := v.Feature.TimeWindow; x != nil { + c := n.Child("time_window") + requiredMlTimeWindow(w, &c, x) + } else if walk.Forced(v.Feature.ForceSendFields, "TimeWindow") || n.IsRef("time_window") { + c := n.Child("time_window") + requiredMlTimeWindow(w, &c, nil) } - if c := n.child("timeseries_column"); c.exists() { - requiredMlTimeseriesColumn(w, &c, v.Feature.TimeseriesColumn) + if x := v.Feature.TimeseriesColumn; x != nil { + c := n.Child("timeseries_column") + requiredMlTimeseriesColumn(w, &c, x) + } else if walk.Forced(v.Feature.ForceSendFields, "TimeseriesColumn") || n.IsRef("timeseries_column") { + c := n.Child("timeseries_column") + requiredMlTimeseriesColumn(w, &c, nil) } } -func requiredResourcesGenieSpace(w *walker, n *node, v *resources.GenieSpace) { +func requiredResourcesGenieSpace(w *walker, n *walk.Node, v *resources.GenieSpace) { if v == nil { - v = &resources.GenieSpace{} + return } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesInstancePool(w *walker, n *node, v *resources.InstancePool) { +func requiredResourcesInstancePool(w *walker, n *walk.Node, v *resources.InstancePool) { if v == nil { - v = &resources.InstancePool{} + w.missing(n, "instance_pool_name") + w.missing(n, "node_type_id") + return } - if !(v.CreateInstancePool.InstancePoolName != "") && !n.has("instance_pool_name") { + if !(v.CreateInstancePool.InstancePoolName != "" || walk.Forced(v.CreateInstancePool.ForceSendFields, "InstancePoolName")) { w.missing(n, "instance_pool_name") } - if !(v.CreateInstancePool.NodeTypeId != "") && !n.has("node_type_id") { + if !(v.CreateInstancePool.NodeTypeId != "" || walk.Forced(v.CreateInstancePool.ForceSendFields, "NodeTypeId")) { w.missing(n, "node_type_id") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesInstancePoolPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesInstancePoolPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesJobRun(w *walker, n *node, v *resources.JobRun) { +func requiredResourcesJobRun(w *walker, n *walk.Node, v *resources.JobRun) { if v == nil { - v = &resources.JobRun{} + w.missing(n, "job_id") + return } - if !(v.RunNow.JobId != 0) && !n.has("job_id") { + if !(v.RunNow.JobId != 0 || walk.Forced(v.RunNow.ForceSendFields, "JobId") || n.IsRef("job_id")) { w.missing(n, "job_id") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesJobRunLifecycle(w, &c, v.Lifecycle) + if x := v.Lifecycle; x != nil { + c := n.Child("lifecycle") + requiredResourcesJobRunLifecycle(w, &c, x) + } else if n.IsRef("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesJobRunLifecycle(w, &c, nil) } - if c := n.child("queue"); c.exists() { - requiredJobsQueueSettings(w, &c, v.RunNow.Queue) + if x := v.RunNow.Queue; x != nil { + c := n.Child("queue") + requiredJobsQueueSettings(w, &c, x) + } else if walk.Forced(v.RunNow.ForceSendFields, "Queue") || n.IsRef("queue") { + c := n.Child("queue") + requiredJobsQueueSettings(w, &c, nil) } } -func requiredResourcesJob(w *walker, n *node, v *resources.Job) { +func requiredResourcesJob(w *walker, n *walk.Node, v *resources.Job) { if v == nil { - v = &resources.Job{} + return } - if c := n.child("continuous"); c.exists() { - requiredJobsContinuous(w, &c, v.JobSettings.Continuous) + if x := v.JobSettings.Continuous; x != nil { + c := n.Child("continuous") + requiredJobsContinuous(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "Continuous") || n.IsRef("continuous") { + c := n.Child("continuous") + requiredJobsContinuous(w, &c, nil) } - if c := n.child("deployment"); c.exists() { - requiredJobsJobDeployment(w, &c, v.JobSettings.Deployment) + if x := v.JobSettings.Deployment; x != nil { + c := n.Child("deployment") + requiredJobsJobDeployment(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "Deployment") || n.IsRef("deployment") { + c := n.Child("deployment") + requiredJobsJobDeployment(w, &c, nil) } if len(v.JobSettings.EnvironmentVariables) > 0 { - c := n.child("environment_variables") - for i0 := range v.JobSettings.EnvironmentVariables { - e := c.elem(i0) - requiredJobsJobEnvironmentVariables(w, &e, &v.JobSettings.EnvironmentVariables[i0]) + c := n.Child("environment_variables") + for i := range v.JobSettings.EnvironmentVariables { + e := c.Elem(i) + requiredJobsJobEnvironmentVariables(w, &e, &v.JobSettings.EnvironmentVariables[i]) } } if len(v.JobSettings.Environments) > 0 { - c := n.child("environments") - for i0 := range v.JobSettings.Environments { - e := c.elem(i0) - requiredJobsJobEnvironment(w, &e, &v.JobSettings.Environments[i0]) + c := n.Child("environments") + for i := range v.JobSettings.Environments { + e := c.Elem(i) + requiredJobsJobEnvironment(w, &e, &v.JobSettings.Environments[i]) } } - if c := n.child("git_source"); c.exists() { - requiredJobsGitSource(w, &c, v.JobSettings.GitSource) + if x := v.JobSettings.GitSource; x != nil { + c := n.Child("git_source") + requiredJobsGitSource(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "GitSource") || n.IsRef("git_source") { + c := n.Child("git_source") + requiredJobsGitSource(w, &c, nil) } - if c := n.child("health"); c.exists() { - requiredJobsJobsHealthRules(w, &c, v.JobSettings.Health) + if x := v.JobSettings.Health; x != nil { + c := n.Child("health") + requiredJobsJobsHealthRules(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "Health") || n.IsRef("health") { + c := n.Child("health") + requiredJobsJobsHealthRules(w, &c, nil) } if len(v.JobSettings.JobClusters) > 0 { - c := n.child("job_clusters") - for i0 := range v.JobSettings.JobClusters { - e := c.elem(i0) - requiredJobsJobCluster(w, &e, &v.JobSettings.JobClusters[i0]) + c := n.Child("job_clusters") + for i := range v.JobSettings.JobClusters { + e := c.Elem(i) + requiredJobsJobCluster(w, &e, &v.JobSettings.JobClusters[i]) } } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.JobSettings.Parameters) > 0 { - c := n.child("parameters") - for i0 := range v.JobSettings.Parameters { - e := c.elem(i0) - requiredJobsJobParameterDefinition(w, &e, &v.JobSettings.Parameters[i0]) + c := n.Child("parameters") + for i := range v.JobSettings.Parameters { + e := c.Elem(i) + requiredJobsJobParameterDefinition(w, &e, &v.JobSettings.Parameters[i]) } } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesJobPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesJobPermission(w, &e, &v.Permissions[i]) } } - if c := n.child("queue"); c.exists() { - requiredJobsQueueSettings(w, &c, v.JobSettings.Queue) + if x := v.JobSettings.Queue; x != nil { + c := n.Child("queue") + requiredJobsQueueSettings(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "Queue") || n.IsRef("queue") { + c := n.Child("queue") + requiredJobsQueueSettings(w, &c, nil) } - if c := n.child("schedule"); c.exists() { - requiredJobsCronSchedule(w, &c, v.JobSettings.Schedule) + if x := v.JobSettings.Schedule; x != nil { + c := n.Child("schedule") + requiredJobsCronSchedule(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "Schedule") || n.IsRef("schedule") { + c := n.Child("schedule") + requiredJobsCronSchedule(w, &c, nil) } if len(v.JobSettings.Tasks) > 0 { - c := n.child("tasks") - for i0 := range v.JobSettings.Tasks { - e := c.elem(i0) - requiredJobsTask(w, &e, &v.JobSettings.Tasks[i0]) + c := n.Child("tasks") + for i := range v.JobSettings.Tasks { + e := c.Elem(i) + requiredJobsTask(w, &e, &v.JobSettings.Tasks[i]) } } - if c := n.child("trigger"); c.exists() { - requiredJobsTriggerSettings(w, &c, v.JobSettings.Trigger) + if x := v.JobSettings.Trigger; x != nil { + c := n.Child("trigger") + requiredJobsTriggerSettings(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "Trigger") || n.IsRef("trigger") { + c := n.Child("trigger") + requiredJobsTriggerSettings(w, &c, nil) } if len(v.JobSettings.Triggers) > 0 { - c := n.child("triggers") - for i0 := range v.JobSettings.Triggers { - e := c.elem(i0) - requiredJobsTriggerConfiguration(w, &e, &v.JobSettings.Triggers[i0]) + c := n.Child("triggers") + for i := range v.JobSettings.Triggers { + e := c.Elem(i) + requiredJobsTriggerConfiguration(w, &e, &v.JobSettings.Triggers[i]) } } - if c := n.child("webhook_notifications"); c.exists() { - requiredJobsWebhookNotifications(w, &c, v.JobSettings.WebhookNotifications) + if x := v.JobSettings.WebhookNotifications; x != nil { + c := n.Child("webhook_notifications") + requiredJobsWebhookNotifications(w, &c, x) + } else if walk.Forced(v.JobSettings.ForceSendFields, "WebhookNotifications") || n.IsRef("webhook_notifications") { + c := n.Child("webhook_notifications") + requiredJobsWebhookNotifications(w, &c, nil) } } -func requiredResourcesMcpService(w *walker, n *node, v *resources.McpService) { +func requiredResourcesMcpService(w *walker, n *walk.Node, v *resources.McpService) { if v == nil { - v = &resources.McpService{} + w.missing(n, "parent") + w.missing(n, "mcp_service_id") + return } - if !(v.McpServiceConfig.Parent != "") && !n.has("parent") { + if !(v.McpServiceConfig.Parent != "" || walk.Forced(v.McpServiceConfig.ForceSendFields, "Parent")) { w.missing(n, "parent") } - if !(v.McpServiceConfig.McpServiceId != "") && !n.has("mcp_service_id") { + if !(v.McpServiceConfig.McpServiceId != "" || walk.Forced(v.McpServiceConfig.ForceSendFields, "McpServiceId")) { w.missing(n, "mcp_service_id") } - if c := n.child("config"); c.exists() { - requiredCatalogMcpServiceConfig(w, &c, v.McpServiceConfig.Config) + if x := v.McpServiceConfig.Config; x != nil { + c := n.Child("config") + requiredCatalogMcpServiceConfig(w, &c, x) + } else if walk.Forced(v.McpServiceConfig.ForceSendFields, "Config") || n.IsRef("config") { + c := n.Child("config") + requiredCatalogMcpServiceConfig(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesModelProviderService(w *walker, n *node, v *resources.ModelProviderService) { +func requiredResourcesModelProviderService(w *walker, n *walk.Node, v *resources.ModelProviderService) { if v == nil { - v = &resources.ModelProviderService{} + w.missing(n, "parent") + w.missing(n, "model_provider_service_id") + return } - if !(v.ModelProviderServiceConfig.Parent != "") && !n.has("parent") { + if !(v.ModelProviderServiceConfig.Parent != "" || walk.Forced(v.ModelProviderServiceConfig.ForceSendFields, "Parent")) { w.missing(n, "parent") } - if !(v.ModelProviderServiceConfig.ModelProviderServiceId != "") && !n.has("model_provider_service_id") { + if !(v.ModelProviderServiceConfig.ModelProviderServiceId != "" || walk.Forced(v.ModelProviderServiceConfig.ForceSendFields, "ModelProviderServiceId")) { w.missing(n, "model_provider_service_id") } - if c := n.child("config"); c.exists() { - requiredCatalogModelProviderServiceConfig(w, &c, v.ModelProviderServiceConfig.Config) + if x := v.ModelProviderServiceConfig.Config; x != nil { + c := n.Child("config") + requiredCatalogModelProviderServiceConfig(w, &c, x) + } else if walk.Forced(v.ModelProviderServiceConfig.ForceSendFields, "Config") || n.IsRef("config") { + c := n.Child("config") + requiredCatalogModelProviderServiceConfig(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesModelService(w *walker, n *node, v *resources.ModelService) { +func requiredResourcesModelService(w *walker, n *walk.Node, v *resources.ModelService) { if v == nil { - v = &resources.ModelService{} + w.missing(n, "parent") + w.missing(n, "model_service_id") + return } - if !(v.ModelServiceConfig.Parent != "") && !n.has("parent") { + if !(v.ModelServiceConfig.Parent != "" || walk.Forced(v.ModelServiceConfig.ForceSendFields, "Parent")) { w.missing(n, "parent") } - if !(v.ModelServiceConfig.ModelServiceId != "") && !n.has("model_service_id") { + if !(v.ModelServiceConfig.ModelServiceId != "" || walk.Forced(v.ModelServiceConfig.ForceSendFields, "ModelServiceId")) { w.missing(n, "model_service_id") } - if c := n.child("config"); c.exists() { - requiredCatalogModelServiceConfig(w, &c, v.ModelServiceConfig.Config) + if x := v.ModelServiceConfig.Config; x != nil { + c := n.Child("config") + requiredCatalogModelServiceConfig(w, &c, x) + } else if walk.Forced(v.ModelServiceConfig.ForceSendFields, "Config") || n.IsRef("config") { + c := n.Child("config") + requiredCatalogModelServiceConfig(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesModelServingEndpoint(w *walker, n *node, v *resources.ModelServingEndpoint) { +func requiredResourcesModelServingEndpoint(w *walker, n *walk.Node, v *resources.ModelServingEndpoint) { if v == nil { - v = &resources.ModelServingEndpoint{} + w.missing(n, "name") + return } - if !(v.CreateServingEndpoint.Name != "") && !n.has("name") { + if !(v.CreateServingEndpoint.Name != "" || walk.Forced(v.CreateServingEndpoint.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("ai_gateway"); c.exists() { - requiredServingAiGatewayConfig(w, &c, v.CreateServingEndpoint.AiGateway) + if x := v.CreateServingEndpoint.AiGateway; x != nil { + c := n.Child("ai_gateway") + requiredServingAiGatewayConfig(w, &c, x) + } else if walk.Forced(v.CreateServingEndpoint.ForceSendFields, "AiGateway") || n.IsRef("ai_gateway") { + c := n.Child("ai_gateway") + requiredServingAiGatewayConfig(w, &c, nil) } - if c := n.child("config"); c.exists() { - requiredServingEndpointCoreConfigInput(w, &c, v.CreateServingEndpoint.Config) + if x := v.CreateServingEndpoint.Config; x != nil { + c := n.Child("config") + requiredServingEndpointCoreConfigInput(w, &c, x) + } else if walk.Forced(v.CreateServingEndpoint.ForceSendFields, "Config") || n.IsRef("config") { + c := n.Child("config") + requiredServingEndpointCoreConfigInput(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesModelServingEndpointPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesModelServingEndpointPermission(w, &e, &v.Permissions[i]) } } if len(v.CreateServingEndpoint.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.CreateServingEndpoint.RateLimits { - e := c.elem(i0) - requiredServingRateLimit(w, &e, &v.CreateServingEndpoint.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.CreateServingEndpoint.RateLimits { + e := c.Elem(i) + requiredServingRateLimit(w, &e, &v.CreateServingEndpoint.RateLimits[i]) } } if len(v.CreateServingEndpoint.Tags) > 0 { - c := n.child("tags") - for i0 := range v.CreateServingEndpoint.Tags { - e := c.elem(i0) - requiredServingEndpointTag(w, &e, &v.CreateServingEndpoint.Tags[i0]) + c := n.Child("tags") + for i := range v.CreateServingEndpoint.Tags { + e := c.Elem(i) + requiredServingEndpointTag(w, &e, &v.CreateServingEndpoint.Tags[i]) } } } -func requiredResourcesMlflowModel(w *walker, n *node, v *resources.MlflowModel) { +func requiredResourcesMlflowModel(w *walker, n *walk.Node, v *resources.MlflowModel) { if v == nil { - v = &resources.MlflowModel{} + w.missing(n, "name") + return } - if !(v.CreateModelRequest.Name != "") && !n.has("name") { + if !(v.CreateModelRequest.Name != "" || walk.Forced(v.CreateModelRequest.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesMlflowModelPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesMlflowModelPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesPipeline(w *walker, n *node, v *resources.Pipeline) { +func requiredResourcesPipeline(w *walker, n *walk.Node, v *resources.Pipeline) { if v == nil { - v = &resources.Pipeline{} + return } if len(v.CreatePipeline.Clusters) > 0 { - c := n.child("clusters") - for i0 := range v.CreatePipeline.Clusters { - e := c.elem(i0) - requiredPipelinesPipelineCluster(w, &e, &v.CreatePipeline.Clusters[i0]) + c := n.Child("clusters") + for i := range v.CreatePipeline.Clusters { + e := c.Elem(i) + requiredPipelinesPipelineCluster(w, &e, &v.CreatePipeline.Clusters[i]) } } - if c := n.child("deployment"); c.exists() { - requiredPipelinesPipelineDeployment(w, &c, v.CreatePipeline.Deployment) - } - if c := n.child("gateway_definition"); c.exists() { - requiredPipelinesIngestionGatewayPipelineDefinition(w, &c, v.CreatePipeline.GatewayDefinition) - } - if c := n.child("ingestion_definition"); c.exists() { - requiredPipelinesIngestionPipelineDefinition(w, &c, v.CreatePipeline.IngestionDefinition) + if x := v.CreatePipeline.Deployment; x != nil { + c := n.Child("deployment") + requiredPipelinesPipelineDeployment(w, &c, x) + } else if walk.Forced(v.CreatePipeline.ForceSendFields, "Deployment") || n.IsRef("deployment") { + c := n.Child("deployment") + requiredPipelinesPipelineDeployment(w, &c, nil) + } + if x := v.CreatePipeline.GatewayDefinition; x != nil { + c := n.Child("gateway_definition") + requiredPipelinesIngestionGatewayPipelineDefinition(w, &c, x) + } else if walk.Forced(v.CreatePipeline.ForceSendFields, "GatewayDefinition") || n.IsRef("gateway_definition") { + c := n.Child("gateway_definition") + requiredPipelinesIngestionGatewayPipelineDefinition(w, &c, nil) + } + if x := v.CreatePipeline.IngestionDefinition; x != nil { + c := n.Child("ingestion_definition") + requiredPipelinesIngestionPipelineDefinition(w, &c, x) + } else if walk.Forced(v.CreatePipeline.ForceSendFields, "IngestionDefinition") || n.IsRef("ingestion_definition") { + c := n.Child("ingestion_definition") + requiredPipelinesIngestionPipelineDefinition(w, &c, nil) } if len(v.CreatePipeline.Libraries) > 0 { - c := n.child("libraries") - for i0 := range v.CreatePipeline.Libraries { - e := c.elem(i0) - requiredPipelinesPipelineLibrary(w, &e, &v.CreatePipeline.Libraries[i0]) + c := n.Child("libraries") + for i := range v.CreatePipeline.Libraries { + e := c.Elem(i) + requiredPipelinesPipelineLibrary(w, &e, &v.CreatePipeline.Libraries[i]) } } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPipelinePermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPipelinePermission(w, &e, &v.Permissions[i]) } } - if c := n.child("restart_window"); c.exists() { - requiredPipelinesRestartWindow(w, &c, v.CreatePipeline.RestartWindow) + if x := v.CreatePipeline.RestartWindow; x != nil { + c := n.Child("restart_window") + requiredPipelinesRestartWindow(w, &c, x) + } else if walk.Forced(v.CreatePipeline.ForceSendFields, "RestartWindow") || n.IsRef("restart_window") { + c := n.Child("restart_window") + requiredPipelinesRestartWindow(w, &c, nil) } } -func requiredResourcesPostgresBranch(w *walker, n *node, v *resources.PostgresBranch) { +func requiredResourcesPostgresBranch(w *walker, n *walk.Node, v *resources.PostgresBranch) { if v == nil { - v = &resources.PostgresBranch{} + w.missing(n, "branch_id") + w.missing(n, "parent") + return } - if !(v.PostgresBranchConfig.BranchId != "") && !n.has("branch_id") { + if !(v.PostgresBranchConfig.BranchId != "" || walk.Forced(v.PostgresBranchConfig.ForceSendFields, "BranchId")) { w.missing(n, "branch_id") } - if !(v.PostgresBranchConfig.Parent != "") && !n.has("parent") { + if !(v.PostgresBranchConfig.Parent != "" || walk.Forced(v.PostgresBranchConfig.ForceSendFields, "Parent")) { w.missing(n, "parent") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesPostgresCatalog(w *walker, n *node, v *resources.PostgresCatalog) { +func requiredResourcesPostgresCatalog(w *walker, n *walk.Node, v *resources.PostgresCatalog) { if v == nil { - v = &resources.PostgresCatalog{} + w.missing(n, "postgres_database") + w.missing(n, "catalog_id") + return } - if !(v.PostgresCatalogConfig.CatalogCatalogSpec.PostgresDatabase != "") && !n.has("postgres_database") { + if !(v.PostgresCatalogConfig.CatalogCatalogSpec.PostgresDatabase != "" || walk.Forced(v.PostgresCatalogConfig.CatalogCatalogSpec.ForceSendFields, "PostgresDatabase")) { w.missing(n, "postgres_database") } - if !(v.PostgresCatalogConfig.CatalogId != "") && !n.has("catalog_id") { + if !(v.PostgresCatalogConfig.CatalogId != "") { w.missing(n, "catalog_id") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesPostgresDatabase(w *walker, n *node, v *resources.PostgresDatabase) { +func requiredResourcesPostgresDatabase(w *walker, n *walk.Node, v *resources.PostgresDatabase) { if v == nil { - v = &resources.PostgresDatabase{} + w.missing(n, "role") + w.missing(n, "database_id") + w.missing(n, "parent") + return } - if !(v.PostgresDatabaseConfig.DatabaseDatabaseSpec.Role != "") && !n.has("role") { + if !(v.PostgresDatabaseConfig.DatabaseDatabaseSpec.Role != "" || walk.Forced(v.PostgresDatabaseConfig.DatabaseDatabaseSpec.ForceSendFields, "Role")) { w.missing(n, "role") } - if !(v.PostgresDatabaseConfig.DatabaseId != "") && !n.has("database_id") { + if !(v.PostgresDatabaseConfig.DatabaseId != "") { w.missing(n, "database_id") } - if !(v.PostgresDatabaseConfig.Parent != "") && !n.has("parent") { + if !(v.PostgresDatabaseConfig.Parent != "") { w.missing(n, "parent") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesPostgresEndpoint(w *walker, n *node, v *resources.PostgresEndpoint) { +func requiredResourcesPostgresEndpoint(w *walker, n *walk.Node, v *resources.PostgresEndpoint) { if v == nil { - v = &resources.PostgresEndpoint{} + w.missing(n, "endpoint_type") + w.missing(n, "endpoint_id") + w.missing(n, "parent") + return } - if !(v.PostgresEndpointConfig.EndpointSpec.EndpointType != "") && !n.has("endpoint_type") { + if !(v.PostgresEndpointConfig.EndpointSpec.EndpointType != "" || walk.Forced(v.PostgresEndpointConfig.EndpointSpec.ForceSendFields, "EndpointType")) { w.missing(n, "endpoint_type") } - if !(v.PostgresEndpointConfig.EndpointId != "") && !n.has("endpoint_id") { + if !(v.PostgresEndpointConfig.EndpointId != "") { w.missing(n, "endpoint_id") } - if !(v.PostgresEndpointConfig.Parent != "") && !n.has("parent") { + if !(v.PostgresEndpointConfig.Parent != "") { w.missing(n, "parent") } - if c := n.child("group"); c.exists() { - requiredPostgresEndpointGroupSpec(w, &c, v.PostgresEndpointConfig.EndpointSpec.Group) + if x := v.PostgresEndpointConfig.EndpointSpec.Group; x != nil { + c := n.Child("group") + requiredPostgresEndpointGroupSpec(w, &c, x) + } else if walk.Forced(v.PostgresEndpointConfig.EndpointSpec.ForceSendFields, "Group") || n.IsRef("group") { + c := n.Child("group") + requiredPostgresEndpointGroupSpec(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesPostgresProject(w *walker, n *node, v *resources.PostgresProject) { +func requiredResourcesPostgresProject(w *walker, n *walk.Node, v *resources.PostgresProject) { if v == nil { - v = &resources.PostgresProject{} + w.missing(n, "project_id") + return } - if !(v.PostgresProjectConfig.ProjectId != "") && !n.has("project_id") { + if !(v.PostgresProjectConfig.ProjectId != "" || walk.Forced(v.PostgresProjectConfig.ForceSendFields, "ProjectId")) { w.missing(n, "project_id") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesPostgresRole(w *walker, n *node, v *resources.PostgresRole) { +func requiredResourcesPostgresRole(w *walker, n *walk.Node, v *resources.PostgresRole) { if v == nil { - v = &resources.PostgresRole{} + w.missing(n, "role_id") + w.missing(n, "parent") + return } - if !(v.PostgresRoleConfig.RoleId != "") && !n.has("role_id") { + if !(v.PostgresRoleConfig.RoleId != "") { w.missing(n, "role_id") } - if !(v.PostgresRoleConfig.Parent != "") && !n.has("parent") { + if !(v.PostgresRoleConfig.Parent != "") { w.missing(n, "parent") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesPostgresSnapshotSchedule(w *walker, n *node, v *resources.PostgresSnapshotSchedule) { +func requiredResourcesPostgresSnapshotSchedule(w *walker, n *walk.Node, v *resources.PostgresSnapshotSchedule) { if v == nil { - v = &resources.PostgresSnapshotSchedule{} + w.missing(n, "branch") + return } - if !(v.PostgresSnapshotScheduleConfig.Branch != "") && !n.has("branch") { + if !(v.PostgresSnapshotScheduleConfig.Branch != "" || walk.Forced(v.PostgresSnapshotScheduleConfig.ForceSendFields, "Branch")) { w.missing(n, "branch") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.PostgresSnapshotScheduleConfig.Schedule) > 0 { - c := n.child("schedule") - for i0 := range v.PostgresSnapshotScheduleConfig.Schedule { - e := c.elem(i0) - requiredPostgresScheduleCadence(w, &e, &v.PostgresSnapshotScheduleConfig.Schedule[i0]) + c := n.Child("schedule") + for i := range v.PostgresSnapshotScheduleConfig.Schedule { + e := c.Elem(i) + requiredPostgresScheduleCadence(w, &e, &v.PostgresSnapshotScheduleConfig.Schedule[i]) } } } -func requiredResourcesPostgresSyncedTable(w *walker, n *node, v *resources.PostgresSyncedTable) { +func requiredResourcesPostgresSyncedTable(w *walker, n *walk.Node, v *resources.PostgresSyncedTable) { if v == nil { - v = &resources.PostgresSyncedTable{} + w.missing(n, "synced_table_id") + return } - if !(v.PostgresSyncedTableConfig.SyncedTableId != "") && !n.has("synced_table_id") { + if !(v.PostgresSyncedTableConfig.SyncedTableId != "") { w.missing(n, "synced_table_id") } if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns) > 0 { - c := n.child("extra_columns") - for i0 := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns { - e := c.elem(i0) - requiredPostgresSyncedTableSyncedTableSpecExtraColumn(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns[i0]) + c := n.Child("extra_columns") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns { + e := c.Elem(i) + requiredPostgresSyncedTableSyncedTableSpecExtraColumn(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns[i]) } } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides) > 0 { - c := n.child("type_overrides") - for i0 := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides { - e := c.elem(i0) - requiredPostgresSyncedTableSyncedTableSpecTypeOverride(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides[i0]) + c := n.Child("type_overrides") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides { + e := c.Elem(i) + requiredPostgresSyncedTableSyncedTableSpecTypeOverride(w, &e, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides[i]) } } } -func requiredResourcesQualityMonitor(w *walker, n *node, v *resources.QualityMonitor) { +func requiredResourcesQualityMonitor(w *walker, n *walk.Node, v *resources.QualityMonitor) { if v == nil { - v = &resources.QualityMonitor{} + w.missing(n, "assets_dir") + w.missing(n, "output_schema_name") + w.missing(n, "table_name") + return } - if !(v.CreateMonitor.AssetsDir != "") && !n.has("assets_dir") { + if !(v.CreateMonitor.AssetsDir != "" || walk.Forced(v.CreateMonitor.ForceSendFields, "AssetsDir")) { w.missing(n, "assets_dir") } - if !(v.CreateMonitor.OutputSchemaName != "") && !n.has("output_schema_name") { + if !(v.CreateMonitor.OutputSchemaName != "" || walk.Forced(v.CreateMonitor.ForceSendFields, "OutputSchemaName")) { w.missing(n, "output_schema_name") } - if !(v.TableName != "") && !n.has("table_name") { + if !(v.TableName != "") { w.missing(n, "table_name") } if len(v.CreateMonitor.CustomMetrics) > 0 { - c := n.child("custom_metrics") - for i0 := range v.CreateMonitor.CustomMetrics { - e := c.elem(i0) - requiredCatalogMonitorMetric(w, &e, &v.CreateMonitor.CustomMetrics[i0]) + c := n.Child("custom_metrics") + for i := range v.CreateMonitor.CustomMetrics { + e := c.Elem(i) + requiredCatalogMonitorMetric(w, &e, &v.CreateMonitor.CustomMetrics[i]) } } - if c := n.child("inference_log"); c.exists() { - requiredCatalogMonitorInferenceLog(w, &c, v.CreateMonitor.InferenceLog) + if x := v.CreateMonitor.InferenceLog; x != nil { + c := n.Child("inference_log") + requiredCatalogMonitorInferenceLog(w, &c, x) + } else if walk.Forced(v.CreateMonitor.ForceSendFields, "InferenceLog") || n.IsRef("inference_log") { + c := n.Child("inference_log") + requiredCatalogMonitorInferenceLog(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } - if c := n.child("schedule"); c.exists() { - requiredCatalogMonitorCronSchedule(w, &c, v.CreateMonitor.Schedule) + if x := v.CreateMonitor.Schedule; x != nil { + c := n.Child("schedule") + requiredCatalogMonitorCronSchedule(w, &c, x) + } else if walk.Forced(v.CreateMonitor.ForceSendFields, "Schedule") || n.IsRef("schedule") { + c := n.Child("schedule") + requiredCatalogMonitorCronSchedule(w, &c, nil) } - if c := n.child("time_series"); c.exists() { - requiredCatalogMonitorTimeSeries(w, &c, v.CreateMonitor.TimeSeries) + if x := v.CreateMonitor.TimeSeries; x != nil { + c := n.Child("time_series") + requiredCatalogMonitorTimeSeries(w, &c, x) + } else if walk.Forced(v.CreateMonitor.ForceSendFields, "TimeSeries") || n.IsRef("time_series") { + c := n.Child("time_series") + requiredCatalogMonitorTimeSeries(w, &c, nil) } } -func requiredResourcesRegisteredModel(w *walker, n *node, v *resources.RegisteredModel) { +func requiredResourcesRegisteredModel(w *walker, n *walk.Node, v *resources.RegisteredModel) { if v == nil { - v = &resources.RegisteredModel{} + return } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesSchema(w *walker, n *node, v *resources.Schema) { +func requiredResourcesSchema(w *walker, n *walk.Node, v *resources.Schema) { if v == nil { - v = &resources.Schema{} + w.missing(n, "catalog_name") + w.missing(n, "name") + return } - if !(v.CreateSchema.CatalogName != "") && !n.has("catalog_name") { + if !(v.CreateSchema.CatalogName != "" || walk.Forced(v.CreateSchema.ForceSendFields, "CatalogName")) { w.missing(n, "catalog_name") } - if !(v.CreateSchema.Name != "") && !n.has("name") { + if !(v.CreateSchema.Name != "" || walk.Forced(v.CreateSchema.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesSecretScope(w *walker, n *node, v *resources.SecretScope) { +func requiredResourcesSecretScope(w *walker, n *walk.Node, v *resources.SecretScope) { if v == nil { - v = &resources.SecretScope{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } - if c := n.child("keyvault_metadata"); c.exists() { - requiredWorkspaceAzureKeyVaultSecretScopeMetadata(w, &c, v.KeyvaultMetadata) + if x := v.KeyvaultMetadata; x != nil { + c := n.Child("keyvault_metadata") + requiredWorkspaceAzureKeyVaultSecretScopeMetadata(w, &c, x) + } else if n.IsRef("keyvault_metadata") { + c := n.Child("keyvault_metadata") + requiredWorkspaceAzureKeyVaultSecretScopeMetadata(w, &c, nil) } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesSecretScopePermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesSecretScopePermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesSecret(w *walker, n *node, v *resources.Secret) { +func requiredResourcesSecret(w *walker, n *walk.Node, v *resources.Secret) { if v == nil { - v = &resources.Secret{} + w.missing(n, "catalog_name") + w.missing(n, "name") + w.missing(n, "schema_name") + w.missing(n, "value") + return } - if !(v.Secret.CatalogName != "") && !n.has("catalog_name") { + if !(v.Secret.CatalogName != "" || walk.Forced(v.Secret.ForceSendFields, "CatalogName")) { w.missing(n, "catalog_name") } - if !(v.Secret.Name != "") && !n.has("name") { + if !(v.Secret.Name != "" || walk.Forced(v.Secret.ForceSendFields, "Name")) { w.missing(n, "name") } - if !(v.Secret.SchemaName != "") && !n.has("schema_name") { + if !(v.Secret.SchemaName != "" || walk.Forced(v.Secret.ForceSendFields, "SchemaName")) { w.missing(n, "schema_name") } - if !(v.Secret.Value != "") && !n.has("value") { + if !(v.Secret.Value != "" || walk.Forced(v.Secret.ForceSendFields, "Value")) { w.missing(n, "value") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesSqlWarehouse(w *walker, n *node, v *resources.SqlWarehouse) { +func requiredResourcesSqlWarehouse(w *walker, n *walk.Node, v *resources.SqlWarehouse) { if v == nil { - v = &resources.SqlWarehouse{} + return } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycleWithStarted(w, &c, v.Lifecycle) + if x := v.Lifecycle; x != nil { + c := n.Child("lifecycle") + requiredResourcesLifecycleWithStarted(w, &c, x) + } else if n.IsRef("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycleWithStarted(w, &c, nil) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesSqlWarehousePermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesSqlWarehousePermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesSyncedDatabaseTable(w *walker, n *node, v *resources.SyncedDatabaseTable) { +func requiredResourcesSyncedDatabaseTable(w *walker, n *walk.Node, v *resources.SyncedDatabaseTable) { if v == nil { - v = &resources.SyncedDatabaseTable{} + w.missing(n, "name") + return } - if !(v.SyncedDatabaseTable.Name != "") && !n.has("name") { + if !(v.SyncedDatabaseTable.Name != "" || walk.Forced(v.SyncedDatabaseTable.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } - if c := n.child("spec"); c.exists() { - requiredDatabaseSyncedTableSpec(w, &c, v.SyncedDatabaseTable.Spec) + if x := v.SyncedDatabaseTable.Spec; x != nil { + c := n.Child("spec") + requiredDatabaseSyncedTableSpec(w, &c, x) + } else if walk.Forced(v.SyncedDatabaseTable.ForceSendFields, "Spec") || n.IsRef("spec") { + c := n.Child("spec") + requiredDatabaseSyncedTableSpec(w, &c, nil) } } -func requiredResourcesVectorSearchEndpoint(w *walker, n *node, v *resources.VectorSearchEndpoint) { +func requiredResourcesVectorSearchEndpoint(w *walker, n *walk.Node, v *resources.VectorSearchEndpoint) { if v == nil { - v = &resources.VectorSearchEndpoint{} + w.missing(n, "endpoint_type") + w.missing(n, "name") + return } - if !(v.CreateEndpoint.EndpointType != "") && !n.has("endpoint_type") { + if !(v.CreateEndpoint.EndpointType != "" || walk.Forced(v.CreateEndpoint.ForceSendFields, "EndpointType")) { w.missing(n, "endpoint_type") } - if !(v.CreateEndpoint.Name != "") && !n.has("name") { + if !(v.CreateEndpoint.Name != "" || walk.Forced(v.CreateEndpoint.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } if len(v.Permissions) > 0 { - c := n.child("permissions") - for i0 := range v.Permissions { - e := c.elem(i0) - requiredResourcesVectorSearchEndpointPermission(w, &e, &v.Permissions[i0]) + c := n.Child("permissions") + for i := range v.Permissions { + e := c.Elem(i) + requiredResourcesVectorSearchEndpointPermission(w, &e, &v.Permissions[i]) } } } -func requiredResourcesVectorSearchIndex(w *walker, n *node, v *resources.VectorSearchIndex) { +func requiredResourcesVectorSearchIndex(w *walker, n *walk.Node, v *resources.VectorSearchIndex) { if v == nil { - v = &resources.VectorSearchIndex{} + w.missing(n, "endpoint_name") + w.missing(n, "index_type") + w.missing(n, "name") + w.missing(n, "primary_key") + return } - if !(v.CreateVectorIndexRequest.EndpointName != "") && !n.has("endpoint_name") { + if !(v.CreateVectorIndexRequest.EndpointName != "") { w.missing(n, "endpoint_name") } - if !(v.CreateVectorIndexRequest.IndexType != "") && !n.has("index_type") { + if !(v.CreateVectorIndexRequest.IndexType != "") { w.missing(n, "index_type") } - if !(v.CreateVectorIndexRequest.Name != "") && !n.has("name") { + if !(v.CreateVectorIndexRequest.Name != "") { w.missing(n, "name") } - if !(v.CreateVectorIndexRequest.PrimaryKey != "") && !n.has("primary_key") { + if !(v.CreateVectorIndexRequest.PrimaryKey != "") { w.missing(n, "primary_key") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredResourcesVolume(w *walker, n *node, v *resources.Volume) { +func requiredResourcesVolume(w *walker, n *walk.Node, v *resources.Volume) { if v == nil { - v = &resources.Volume{} + w.missing(n, "catalog_name") + w.missing(n, "name") + w.missing(n, "schema_name") + w.missing(n, "volume_type") + return } - if !(v.CreateVolumeRequestContent.CatalogName != "") && !n.has("catalog_name") { + if !(v.CreateVolumeRequestContent.CatalogName != "" || walk.Forced(v.CreateVolumeRequestContent.ForceSendFields, "CatalogName")) { w.missing(n, "catalog_name") } - if !(v.CreateVolumeRequestContent.Name != "") && !n.has("name") { + if !(v.CreateVolumeRequestContent.Name != "" || walk.Forced(v.CreateVolumeRequestContent.ForceSendFields, "Name")) { w.missing(n, "name") } - if !(v.CreateVolumeRequestContent.SchemaName != "") && !n.has("schema_name") { + if !(v.CreateVolumeRequestContent.SchemaName != "" || walk.Forced(v.CreateVolumeRequestContent.ForceSendFields, "SchemaName")) { w.missing(n, "schema_name") } - if !(v.CreateVolumeRequestContent.VolumeType != "") && !n.has("volume_type") { + if !(v.CreateVolumeRequestContent.VolumeType != "" || walk.Forced(v.CreateVolumeRequestContent.ForceSendFields, "VolumeType")) { w.missing(n, "volume_type") } - if c := n.child("lifecycle"); c.exists() { - requiredResourcesLifecycle(w, &c, &v.BaseResource.Lifecycle) + if x := &v.BaseResource.Lifecycle; !walk.IsZeroStruct(x) || n.HasRefs("lifecycle") { + c := n.Child("lifecycle") + requiredResourcesLifecycle(w, &c, x) } } -func requiredSqlAlertV2Evaluation(w *walker, n *node, v *sql.AlertV2Evaluation) { +func requiredSqlAlertV2Evaluation(w *walker, n *walk.Node, v *sql.AlertV2Evaluation) { if v == nil { - v = &sql.AlertV2Evaluation{} + w.missing(n, "comparison_operator") + w.missing(n, "source") + return } - if !(v.ComparisonOperator != "") && !n.has("comparison_operator") { + if !(v.ComparisonOperator != "" || walk.Forced(v.ForceSendFields, "ComparisonOperator")) { w.missing(n, "comparison_operator") } - if !n.has("source") { + if !(!walk.IsZeroStruct(&v.Source) || walk.Forced(v.ForceSendFields, "Source") || n.HasRefs("source")) { w.missing(n, "source") } - if c := n.child("source"); c.exists() { - requiredSqlAlertV2OperandColumn(w, &c, &v.Source) + if x := &v.Source; !walk.IsZeroStruct(x) || walk.Forced(v.ForceSendFields, "Source") || n.HasRefs("source") { + c := n.Child("source") + requiredSqlAlertV2OperandColumn(w, &c, x) } - if c := n.child("threshold"); c.exists() { - requiredSqlAlertV2Operand(w, &c, v.Threshold) + if x := v.Threshold; x != nil { + c := n.Child("threshold") + requiredSqlAlertV2Operand(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Threshold") || n.IsRef("threshold") { + c := n.Child("threshold") + requiredSqlAlertV2Operand(w, &c, nil) } } -func requiredResourcesLifecycle(w *walker, n *node, v *resources.Lifecycle) { +func requiredResourcesLifecycle(w *walker, n *walk.Node, v *resources.Lifecycle) { if v == nil { - v = &resources.Lifecycle{} + return } if len(v.DependsOn) > 0 { - c := n.child("depends_on") - for i0 := range v.DependsOn { - e := c.elem(i0) - requiredResourcesDependsOn(w, &e, &v.DependsOn[i0]) + c := n.Child("depends_on") + for i := range v.DependsOn { + e := c.Elem(i) + requiredResourcesDependsOn(w, &e, &v.DependsOn[i]) } } } -func requiredSqlAlertStatementParameter(w *walker, n *node, v *sql.AlertStatementParameter) { +func requiredSqlAlertStatementParameter(w *walker, n *walk.Node, v *sql.AlertStatementParameter) { if v == nil { - v = &sql.AlertStatementParameter{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { w.missing(n, "name") } } -func requiredSqlCronSchedule(w *walker, n *node, v *sql.CronSchedule) { +func requiredSqlCronSchedule(w *walker, n *walk.Node, v *sql.CronSchedule) { if v == nil { - v = &sql.CronSchedule{} + w.missing(n, "quartz_cron_schedule") + w.missing(n, "timezone_id") + return } - if !(v.QuartzCronSchedule != "") && !n.has("quartz_cron_schedule") { + if !(v.QuartzCronSchedule != "") { w.missing(n, "quartz_cron_schedule") } - if !(v.TimezoneId != "") && !n.has("timezone_id") { + if !(v.TimezoneId != "") { w.missing(n, "timezone_id") } } -func requiredAppsAppDeployment(w *walker, n *node, v *apps.AppDeployment) { +func requiredAppsAppDeployment(w *walker, n *walk.Node, v *apps.AppDeployment) { if v == nil { - v = &apps.AppDeployment{} + return } - if c := n.child("git_source"); c.exists() { - requiredAppsGitSource(w, &c, v.GitSource) + if x := v.GitSource; x != nil { + c := n.Child("git_source") + requiredAppsGitSource(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "GitSource") || n.IsRef("git_source") { + c := n.Child("git_source") + requiredAppsGitSource(w, &c, nil) } } -func requiredResourcesAppConfig(w *walker, n *node, v *resources.AppConfig) { +func requiredResourcesAppConfig(w *walker, n *walk.Node, v *resources.AppConfig) { if v == nil { - v = &resources.AppConfig{} + return } if len(v.Env) > 0 { - c := n.child("env") - for i0 := range v.Env { - e := c.elem(i0) - requiredResourcesAppEnvVar(w, &e, &v.Env[i0]) + c := n.Child("env") + for i := range v.Env { + e := c.Elem(i) + requiredResourcesAppEnvVar(w, &e, &v.Env[i]) } } } -func requiredAppsGitSource(w *walker, n *node, v *apps.GitSource) { +func requiredAppsGitSource(w *walker, n *walk.Node, v *apps.GitSource) { if v == nil { - v = &apps.GitSource{} + return } - if c := n.child("git_repository"); c.exists() { - requiredAppsGitRepository(w, &c, v.GitRepository) + if x := v.GitRepository; x != nil { + c := n.Child("git_repository") + requiredAppsGitRepository(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "GitRepository") || n.IsRef("git_repository") { + c := n.Child("git_repository") + requiredAppsGitRepository(w, &c, nil) } } -func requiredAppsGitRepository(w *walker, n *node, v *apps.GitRepository) { +func requiredAppsGitRepository(w *walker, n *walk.Node, v *apps.GitRepository) { if v == nil { - v = &apps.GitRepository{} + w.missing(n, "provider") + w.missing(n, "url") + return } - if !(v.Provider != "") && !n.has("provider") { + if !(v.Provider != "" || walk.Forced(v.ForceSendFields, "Provider")) { w.missing(n, "provider") } - if !(v.Url != "") && !n.has("url") { + if !(v.Url != "" || walk.Forced(v.ForceSendFields, "Url")) { w.missing(n, "url") } } -func requiredResourcesLifecycleWithStarted(w *walker, n *node, v *resources.LifecycleWithStarted) { +func requiredResourcesLifecycleWithStarted(w *walker, n *walk.Node, v *resources.LifecycleWithStarted) { if v == nil { - v = &resources.LifecycleWithStarted{} + return } if len(v.Lifecycle.DependsOn) > 0 { - c := n.child("depends_on") - for i0 := range v.Lifecycle.DependsOn { - e := c.elem(i0) - requiredResourcesDependsOn(w, &e, &v.Lifecycle.DependsOn[i0]) + c := n.Child("depends_on") + for i := range v.Lifecycle.DependsOn { + e := c.Elem(i) + requiredResourcesDependsOn(w, &e, &v.Lifecycle.DependsOn[i]) } } } -func requiredResourcesAppPermission(w *walker, n *node, v *resources.AppPermission) { +func requiredResourcesAppPermission(w *walker, n *walk.Node, v *resources.AppPermission) { if v == nil { - v = &resources.AppPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredAppsAppResource(w *walker, n *node, v *apps.AppResource) { +func requiredAppsAppResource(w *walker, n *walk.Node, v *apps.AppResource) { if v == nil { - v = &apps.AppResource{} - } - if !(v.Name != "") && !n.has("name") { w.missing(n, "name") + return } - if c := n.child("database"); c.exists() { - requiredAppsAppResourceDatabase(w, &c, v.Database) + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { + w.missing(n, "name") } - if c := n.child("experiment"); c.exists() { - requiredAppsAppResourceExperiment(w, &c, v.Experiment) + if x := v.Database; x != nil { + c := n.Child("database") + requiredAppsAppResourceDatabase(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Database") || n.IsRef("database") { + c := n.Child("database") + requiredAppsAppResourceDatabase(w, &c, nil) + } + if x := v.Experiment; x != nil { + c := n.Child("experiment") + requiredAppsAppResourceExperiment(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Experiment") || n.IsRef("experiment") { + c := n.Child("experiment") + requiredAppsAppResourceExperiment(w, &c, nil) } - if c := n.child("genie_space"); c.exists() { - requiredAppsAppResourceGenieSpace(w, &c, v.GenieSpace) + if x := v.GenieSpace; x != nil { + c := n.Child("genie_space") + requiredAppsAppResourceGenieSpace(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "GenieSpace") || n.IsRef("genie_space") { + c := n.Child("genie_space") + requiredAppsAppResourceGenieSpace(w, &c, nil) } - if c := n.child("job"); c.exists() { - requiredAppsAppResourceJob(w, &c, v.Job) + if x := v.Job; x != nil { + c := n.Child("job") + requiredAppsAppResourceJob(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Job") || n.IsRef("job") { + c := n.Child("job") + requiredAppsAppResourceJob(w, &c, nil) } - if c := n.child("secret"); c.exists() { - requiredAppsAppResourceSecret(w, &c, v.Secret) + if x := v.Secret; x != nil { + c := n.Child("secret") + requiredAppsAppResourceSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Secret") || n.IsRef("secret") { + c := n.Child("secret") + requiredAppsAppResourceSecret(w, &c, nil) } - if c := n.child("serving_endpoint"); c.exists() { - requiredAppsAppResourceServingEndpoint(w, &c, v.ServingEndpoint) + if x := v.ServingEndpoint; x != nil { + c := n.Child("serving_endpoint") + requiredAppsAppResourceServingEndpoint(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ServingEndpoint") || n.IsRef("serving_endpoint") { + c := n.Child("serving_endpoint") + requiredAppsAppResourceServingEndpoint(w, &c, nil) } - if c := n.child("sql_warehouse"); c.exists() { - requiredAppsAppResourceSqlWarehouse(w, &c, v.SqlWarehouse) + if x := v.SqlWarehouse; x != nil { + c := n.Child("sql_warehouse") + requiredAppsAppResourceSqlWarehouse(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SqlWarehouse") || n.IsRef("sql_warehouse") { + c := n.Child("sql_warehouse") + requiredAppsAppResourceSqlWarehouse(w, &c, nil) } - if c := n.child("uc_securable"); c.exists() { - requiredAppsAppResourceUcSecurable(w, &c, v.UcSecurable) + if x := v.UcSecurable; x != nil { + c := n.Child("uc_securable") + requiredAppsAppResourceUcSecurable(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "UcSecurable") || n.IsRef("uc_securable") { + c := n.Child("uc_securable") + requiredAppsAppResourceUcSecurable(w, &c, nil) } } -func requiredAppsTelemetryExportDestination(w *walker, n *node, v *apps.TelemetryExportDestination) { +func requiredAppsTelemetryExportDestination(w *walker, n *walk.Node, v *apps.TelemetryExportDestination) { if v == nil { - v = &apps.TelemetryExportDestination{} + return } - if c := n.child("unity_catalog"); c.exists() { - requiredAppsUnityCatalog(w, &c, v.UnityCatalog) + if x := v.UnityCatalog; x != nil { + c := n.Child("unity_catalog") + requiredAppsUnityCatalog(w, &c, x) + } else if n.IsRef("unity_catalog") { + c := n.Child("unity_catalog") + requiredAppsUnityCatalog(w, &c, nil) } } -func requiredCatalogEncryptionSettings(w *walker, n *node, v *catalog.EncryptionSettings) { +func requiredCatalogEncryptionSettings(w *walker, n *walk.Node, v *catalog.EncryptionSettings) { if v == nil { - v = &catalog.EncryptionSettings{} + return } - if c := n.child("azure_encryption_settings"); c.exists() { - requiredCatalogAzureEncryptionSettings(w, &c, v.AzureEncryptionSettings) + if x := v.AzureEncryptionSettings; x != nil { + c := n.Child("azure_encryption_settings") + requiredCatalogAzureEncryptionSettings(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AzureEncryptionSettings") || n.IsRef("azure_encryption_settings") { + c := n.Child("azure_encryption_settings") + requiredCatalogAzureEncryptionSettings(w, &c, nil) } } -func requiredComputeLibrary(w *walker, n *node, v *compute.Library) { +func requiredComputeLibrary(w *walker, n *walk.Node, v *compute.Library) { if v == nil { - v = &compute.Library{} + return } - if c := n.child("cran"); c.exists() { - requiredComputeRCranLibrary(w, &c, v.Cran) + if x := v.Cran; x != nil { + c := n.Child("cran") + requiredComputeRCranLibrary(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Cran") || n.IsRef("cran") { + c := n.Child("cran") + requiredComputeRCranLibrary(w, &c, nil) } - if c := n.child("maven"); c.exists() { - requiredComputeMavenLibrary(w, &c, v.Maven) + if x := v.Maven; x != nil { + c := n.Child("maven") + requiredComputeMavenLibrary(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Maven") || n.IsRef("maven") { + c := n.Child("maven") + requiredComputeMavenLibrary(w, &c, nil) } - if c := n.child("pypi"); c.exists() { - requiredComputePythonPyPiLibrary(w, &c, v.Pypi) + if x := v.Pypi; x != nil { + c := n.Child("pypi") + requiredComputePythonPyPiLibrary(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Pypi") || n.IsRef("pypi") { + c := n.Child("pypi") + requiredComputePythonPyPiLibrary(w, &c, nil) } } -func requiredResourcesClusterPolicyPermission(w *walker, n *node, v *resources.ClusterPolicyPermission) { +func requiredResourcesClusterPolicyPermission(w *walker, n *walk.Node, v *resources.ClusterPolicyPermission) { if v == nil { - v = &resources.ClusterPolicyPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredComputeClusterLogConf(w *walker, n *node, v *compute.ClusterLogConf) { +func requiredComputeClusterLogConf(w *walker, n *walk.Node, v *compute.ClusterLogConf) { if v == nil { - v = &compute.ClusterLogConf{} + return } - if c := n.child("dbfs"); c.exists() { - requiredComputeDbfsStorageInfo(w, &c, v.Dbfs) + if x := v.Dbfs; x != nil { + c := n.Child("dbfs") + requiredComputeDbfsStorageInfo(w, &c, x) + } else if n.IsRef("dbfs") { + c := n.Child("dbfs") + requiredComputeDbfsStorageInfo(w, &c, nil) } - if c := n.child("s3"); c.exists() { - requiredComputeS3StorageInfo(w, &c, v.S3) + if x := v.S3; x != nil { + c := n.Child("s3") + requiredComputeS3StorageInfo(w, &c, x) + } else if n.IsRef("s3") { + c := n.Child("s3") + requiredComputeS3StorageInfo(w, &c, nil) } - if c := n.child("volumes"); c.exists() { - requiredComputeVolumesStorageInfo(w, &c, v.Volumes) + if x := v.Volumes; x != nil { + c := n.Child("volumes") + requiredComputeVolumesStorageInfo(w, &c, x) + } else if n.IsRef("volumes") { + c := n.Child("volumes") + requiredComputeVolumesStorageInfo(w, &c, nil) } } -func requiredComputeInitScriptInfo(w *walker, n *node, v *compute.InitScriptInfo) { +func requiredComputeInitScriptInfo(w *walker, n *walk.Node, v *compute.InitScriptInfo) { if v == nil { - v = &compute.InitScriptInfo{} + return } - if c := n.child("abfss"); c.exists() { - requiredComputeAdlsgen2Info(w, &c, v.Abfss) + if x := v.Abfss; x != nil { + c := n.Child("abfss") + requiredComputeAdlsgen2Info(w, &c, x) + } else if n.IsRef("abfss") { + c := n.Child("abfss") + requiredComputeAdlsgen2Info(w, &c, nil) } - if c := n.child("dbfs"); c.exists() { - requiredComputeDbfsStorageInfo(w, &c, v.Dbfs) + if x := v.Dbfs; x != nil { + c := n.Child("dbfs") + requiredComputeDbfsStorageInfo(w, &c, x) + } else if n.IsRef("dbfs") { + c := n.Child("dbfs") + requiredComputeDbfsStorageInfo(w, &c, nil) } - if c := n.child("file"); c.exists() { - requiredComputeLocalFileInfo(w, &c, v.File) + if x := v.File; x != nil { + c := n.Child("file") + requiredComputeLocalFileInfo(w, &c, x) + } else if n.IsRef("file") { + c := n.Child("file") + requiredComputeLocalFileInfo(w, &c, nil) } - if c := n.child("gcs"); c.exists() { - requiredComputeGcsStorageInfo(w, &c, v.Gcs) + if x := v.Gcs; x != nil { + c := n.Child("gcs") + requiredComputeGcsStorageInfo(w, &c, x) + } else if n.IsRef("gcs") { + c := n.Child("gcs") + requiredComputeGcsStorageInfo(w, &c, nil) } - if c := n.child("s3"); c.exists() { - requiredComputeS3StorageInfo(w, &c, v.S3) + if x := v.S3; x != nil { + c := n.Child("s3") + requiredComputeS3StorageInfo(w, &c, x) + } else if n.IsRef("s3") { + c := n.Child("s3") + requiredComputeS3StorageInfo(w, &c, nil) } - if c := n.child("volumes"); c.exists() { - requiredComputeVolumesStorageInfo(w, &c, v.Volumes) + if x := v.Volumes; x != nil { + c := n.Child("volumes") + requiredComputeVolumesStorageInfo(w, &c, x) + } else if n.IsRef("volumes") { + c := n.Child("volumes") + requiredComputeVolumesStorageInfo(w, &c, nil) } - if c := n.child("workspace"); c.exists() { - requiredComputeWorkspaceStorageInfo(w, &c, v.Workspace) + if x := v.Workspace; x != nil { + c := n.Child("workspace") + requiredComputeWorkspaceStorageInfo(w, &c, x) + } else if n.IsRef("workspace") { + c := n.Child("workspace") + requiredComputeWorkspaceStorageInfo(w, &c, nil) } } -func requiredResourcesClusterPermission(w *walker, n *node, v *resources.ClusterPermission) { +func requiredResourcesClusterPermission(w *walker, n *walk.Node, v *resources.ClusterPermission) { if v == nil { - v = &resources.ClusterPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredComputeWorkloadType(w *walker, n *node, v *compute.WorkloadType) { +func requiredComputeWorkloadType(w *walker, n *walk.Node, v *compute.WorkloadType) { if v == nil { - v = &compute.WorkloadType{} + w.missing(n, "clients") + return } - if !n.has("clients") { + if !(!walk.IsZeroStruct(&v.Clients) || n.HasRefs("clients")) { w.missing(n, "clients") } } -func requiredResourcesMlflowExperimentPermission(w *walker, n *node, v *resources.MlflowExperimentPermission) { +func requiredResourcesMlflowExperimentPermission(w *walker, n *walk.Node, v *resources.MlflowExperimentPermission) { if v == nil { - v = &resources.MlflowExperimentPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredMlExperimentTraceLocation(w *walker, n *node, v *ml.ExperimentTraceLocation) { +func requiredMlExperimentTraceLocation(w *walker, n *walk.Node, v *ml.ExperimentTraceLocation) { if v == nil { - v = &ml.ExperimentTraceLocation{} + return } - if c := n.child("uc_trace_location"); c.exists() { - requiredMlUcTraceLocation(w, &c, v.UcTraceLocation) + if x := v.UcTraceLocation; x != nil { + c := n.Child("uc_trace_location") + requiredMlUcTraceLocation(w, &c, x) + } else if n.IsRef("uc_trace_location") { + c := n.Child("uc_trace_location") + requiredMlUcTraceLocation(w, &c, nil) } } -func requiredMlEntityColumn(w *walker, n *node, v *ml.EntityColumn) { +func requiredMlEntityColumn(w *walker, n *walk.Node, v *ml.EntityColumn) { if v == nil { - v = &ml.EntityColumn{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredMlFunction(w *walker, n *node, v *ml.Function) { +func requiredMlFunction(w *walker, n *walk.Node, v *ml.Function) { if v == nil { - v = &ml.Function{} + return } - if c := n.child("aggregation_function"); c.exists() { - requiredMlAggregationFunction(w, &c, v.AggregationFunction) + if x := v.AggregationFunction; x != nil { + c := n.Child("aggregation_function") + requiredMlAggregationFunction(w, &c, x) + } else if n.IsRef("aggregation_function") { + c := n.Child("aggregation_function") + requiredMlAggregationFunction(w, &c, nil) } - if c := n.child("column_selection"); c.exists() { - requiredMlColumnSelection(w, &c, v.ColumnSelection) + if x := v.ColumnSelection; x != nil { + c := n.Child("column_selection") + requiredMlColumnSelection(w, &c, x) + } else if n.IsRef("column_selection") { + c := n.Child("column_selection") + requiredMlColumnSelection(w, &c, nil) } - if c := n.child("custom_udf"); c.exists() { - requiredMlCustomUdf(w, &c, v.CustomUdf) + if x := v.CustomUdf; x != nil { + c := n.Child("custom_udf") + requiredMlCustomUdf(w, &c, x) + } else if n.IsRef("custom_udf") { + c := n.Child("custom_udf") + requiredMlCustomUdf(w, &c, nil) } if len(v.ExtraParameters) > 0 { - c := n.child("extra_parameters") - for i0 := range v.ExtraParameters { - e := c.elem(i0) - requiredMlFunctionExtraParameter(w, &e, &v.ExtraParameters[i0]) + c := n.Child("extra_parameters") + for i := range v.ExtraParameters { + e := c.Elem(i) + requiredMlFunctionExtraParameter(w, &e, &v.ExtraParameters[i]) } } } -func requiredMlDataSource(w *walker, n *node, v *ml.DataSource) { +func requiredMlDataSource(w *walker, n *walk.Node, v *ml.DataSource) { if v == nil { - v = &ml.DataSource{} + return } - if c := n.child("delta_table_source"); c.exists() { - requiredMlDeltaTableSource(w, &c, v.DeltaTableSource) + if x := v.DeltaTableSource; x != nil { + c := n.Child("delta_table_source") + requiredMlDeltaTableSource(w, &c, x) + } else if n.IsRef("delta_table_source") { + c := n.Child("delta_table_source") + requiredMlDeltaTableSource(w, &c, nil) } - if c := n.child("feature_view_source"); c.exists() { - requiredMlFeatureViewSource(w, &c, v.FeatureViewSource) + if x := v.FeatureViewSource; x != nil { + c := n.Child("feature_view_source") + requiredMlFeatureViewSource(w, &c, x) + } else if n.IsRef("feature_view_source") { + c := n.Child("feature_view_source") + requiredMlFeatureViewSource(w, &c, nil) } - if c := n.child("kafka_source"); c.exists() { - requiredMlKafkaSource(w, &c, v.KafkaSource) + if x := v.KafkaSource; x != nil { + c := n.Child("kafka_source") + requiredMlKafkaSource(w, &c, x) + } else if n.IsRef("kafka_source") { + c := n.Child("kafka_source") + requiredMlKafkaSource(w, &c, nil) } - if c := n.child("request_source"); c.exists() { - requiredMlRequestSource(w, &c, v.RequestSource) + if x := v.RequestSource; x != nil { + c := n.Child("request_source") + requiredMlRequestSource(w, &c, x) + } else if n.IsRef("request_source") { + c := n.Child("request_source") + requiredMlRequestSource(w, &c, nil) } - if c := n.child("stream_source"); c.exists() { - requiredMlStreamSource(w, &c, v.StreamSource) + if x := v.StreamSource; x != nil { + c := n.Child("stream_source") + requiredMlStreamSource(w, &c, x) + } else if n.IsRef("stream_source") { + c := n.Child("stream_source") + requiredMlStreamSource(w, &c, nil) } } -func requiredMlTimeWindow(w *walker, n *node, v *ml.TimeWindow) { +func requiredMlTimeWindow(w *walker, n *walk.Node, v *ml.TimeWindow) { if v == nil { - v = &ml.TimeWindow{} + return } - if c := n.child("continuous"); c.exists() { - requiredMlContinuousWindow(w, &c, v.Continuous) + if x := v.Continuous; x != nil { + c := n.Child("continuous") + requiredMlContinuousWindow(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Continuous") || n.IsRef("continuous") { + c := n.Child("continuous") + requiredMlContinuousWindow(w, &c, nil) } - if c := n.child("sliding"); c.exists() { - requiredMlSlidingWindow(w, &c, v.Sliding) + if x := v.Sliding; x != nil { + c := n.Child("sliding") + requiredMlSlidingWindow(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Sliding") || n.IsRef("sliding") { + c := n.Child("sliding") + requiredMlSlidingWindow(w, &c, nil) } - if c := n.child("tumbling"); c.exists() { - requiredMlTumblingWindow(w, &c, v.Tumbling) + if x := v.Tumbling; x != nil { + c := n.Child("tumbling") + requiredMlTumblingWindow(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Tumbling") || n.IsRef("tumbling") { + c := n.Child("tumbling") + requiredMlTumblingWindow(w, &c, nil) } } -func requiredMlTimeseriesColumn(w *walker, n *node, v *ml.TimeseriesColumn) { +func requiredMlTimeseriesColumn(w *walker, n *walk.Node, v *ml.TimeseriesColumn) { if v == nil { - v = &ml.TimeseriesColumn{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredResourcesInstancePoolPermission(w *walker, n *node, v *resources.InstancePoolPermission) { +func requiredResourcesInstancePoolPermission(w *walker, n *walk.Node, v *resources.InstancePoolPermission) { if v == nil { - v = &resources.InstancePoolPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredResourcesJobRunLifecycle(w *walker, n *node, v *resources.JobRunLifecycle) { +func requiredResourcesJobRunLifecycle(w *walker, n *walk.Node, v *resources.JobRunLifecycle) { if v == nil { - v = &resources.JobRunLifecycle{} + return } if len(v.Lifecycle.DependsOn) > 0 { - c := n.child("depends_on") - for i0 := range v.Lifecycle.DependsOn { - e := c.elem(i0) - requiredResourcesDependsOn(w, &e, &v.Lifecycle.DependsOn[i0]) + c := n.Child("depends_on") + for i := range v.Lifecycle.DependsOn { + e := c.Elem(i) + requiredResourcesDependsOn(w, &e, &v.Lifecycle.DependsOn[i]) } } } -func requiredJobsQueueSettings(w *walker, n *node, v *jobs.QueueSettings) { +func requiredJobsQueueSettings(w *walker, n *walk.Node, v *jobs.QueueSettings) { if v == nil { - v = &jobs.QueueSettings{} + w.missing(n, "enabled") + return } - if !(v.Enabled) && !n.has("enabled") { + if !(v.Enabled || n.IsRef("enabled")) { w.missing(n, "enabled") } } -func requiredJobsContinuous(w *walker, n *node, v *jobs.Continuous) { +func requiredJobsContinuous(w *walker, n *walk.Node, v *jobs.Continuous) { if v == nil { - v = &jobs.Continuous{} + return } - if c := n.child("maintenance_window"); c.exists() { - requiredJobsMaintenanceWindow(w, &c, v.MaintenanceWindow) + if x := v.MaintenanceWindow; x != nil { + c := n.Child("maintenance_window") + requiredJobsMaintenanceWindow(w, &c, x) + } else if n.IsRef("maintenance_window") { + c := n.Child("maintenance_window") + requiredJobsMaintenanceWindow(w, &c, nil) } } -func requiredJobsJobDeployment(w *walker, n *node, v *jobs.JobDeployment) { +func requiredJobsJobDeployment(w *walker, n *walk.Node, v *jobs.JobDeployment) { if v == nil { - v = &jobs.JobDeployment{} + w.missing(n, "kind") + return } - if !(v.Kind != "") && !n.has("kind") { + if !(v.Kind != "" || walk.Forced(v.ForceSendFields, "Kind")) { w.missing(n, "kind") } } -func requiredJobsJobEnvironmentVariables(w *walker, n *node, v *jobs.JobEnvironmentVariables) { +func requiredJobsJobEnvironmentVariables(w *walker, n *walk.Node, v *jobs.JobEnvironmentVariables) { if v == nil { - v = &jobs.JobEnvironmentVariables{} + w.missing(n, "environment_variables_key") + return } - if !(v.EnvironmentVariablesKey != "") && !n.has("environment_variables_key") { + if !(v.EnvironmentVariablesKey != "") { w.missing(n, "environment_variables_key") } } -func requiredJobsJobEnvironment(w *walker, n *node, v *jobs.JobEnvironment) { +func requiredJobsJobEnvironment(w *walker, n *walk.Node, v *jobs.JobEnvironment) { if v == nil { - v = &jobs.JobEnvironment{} + w.missing(n, "environment_key") + return } - if !(v.EnvironmentKey != "") && !n.has("environment_key") { + if !(v.EnvironmentKey != "") { w.missing(n, "environment_key") } } -func requiredJobsGitSource(w *walker, n *node, v *jobs.GitSource) { +func requiredJobsGitSource(w *walker, n *walk.Node, v *jobs.GitSource) { if v == nil { - v = &jobs.GitSource{} + w.missing(n, "git_provider") + w.missing(n, "git_url") + return } - if !(v.GitProvider != "") && !n.has("git_provider") { + if !(v.GitProvider != "" || walk.Forced(v.ForceSendFields, "GitProvider")) { w.missing(n, "git_provider") } - if !(v.GitUrl != "") && !n.has("git_url") { + if !(v.GitUrl != "" || walk.Forced(v.ForceSendFields, "GitUrl")) { w.missing(n, "git_url") } - if c := n.child("job_source"); c.exists() { - requiredJobsJobSource(w, &c, v.JobSource) + if x := v.JobSource; x != nil { + c := n.Child("job_source") + requiredJobsJobSource(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "JobSource") || n.IsRef("job_source") { + c := n.Child("job_source") + requiredJobsJobSource(w, &c, nil) } } -func requiredJobsJobsHealthRules(w *walker, n *node, v *jobs.JobsHealthRules) { +func requiredJobsJobsHealthRules(w *walker, n *walk.Node, v *jobs.JobsHealthRules) { if v == nil { - v = &jobs.JobsHealthRules{} + return } if len(v.Rules) > 0 { - c := n.child("rules") - for i0 := range v.Rules { - e := c.elem(i0) - requiredJobsJobsHealthRule(w, &e, &v.Rules[i0]) + c := n.Child("rules") + for i := range v.Rules { + e := c.Elem(i) + requiredJobsJobsHealthRule(w, &e, &v.Rules[i]) } } } -func requiredJobsJobCluster(w *walker, n *node, v *jobs.JobCluster) { +func requiredJobsJobCluster(w *walker, n *walk.Node, v *jobs.JobCluster) { if v == nil { - v = &jobs.JobCluster{} + w.missing(n, "job_cluster_key") + return } - if !(v.JobClusterKey != "") && !n.has("job_cluster_key") { + if !(v.JobClusterKey != "" || walk.Forced(v.ForceSendFields, "JobClusterKey")) { w.missing(n, "job_cluster_key") } - if c := n.child("new_cluster"); c.exists() { - requiredComputeClusterSpec(w, &c, v.NewCluster) + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + requiredComputeClusterSpec(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "NewCluster") || n.IsRef("new_cluster") { + c := n.Child("new_cluster") + requiredComputeClusterSpec(w, &c, nil) } } -func requiredJobsJobParameterDefinition(w *walker, n *node, v *jobs.JobParameterDefinition) { +func requiredJobsJobParameterDefinition(w *walker, n *walk.Node, v *jobs.JobParameterDefinition) { if v == nil { - v = &jobs.JobParameterDefinition{} - } - if !(v.Default != "") && !n.has("default") { w.missing(n, "default") + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredResourcesJobPermission(w *walker, n *node, v *resources.JobPermission) { +func requiredResourcesJobPermission(w *walker, n *walk.Node, v *resources.JobPermission) { if v == nil { - v = &resources.JobPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredJobsCronSchedule(w *walker, n *node, v *jobs.CronSchedule) { +func requiredJobsCronSchedule(w *walker, n *walk.Node, v *jobs.CronSchedule) { if v == nil { - v = &jobs.CronSchedule{} + w.missing(n, "quartz_cron_expression") + w.missing(n, "timezone_id") + return } - if !(v.QuartzCronExpression != "") && !n.has("quartz_cron_expression") { + if !(v.QuartzCronExpression != "") { w.missing(n, "quartz_cron_expression") } - if !(v.TimezoneId != "") && !n.has("timezone_id") { + if !(v.TimezoneId != "") { w.missing(n, "timezone_id") } - if c := n.child("sql_condition"); c.exists() { - requiredJobsSqlConditionConfiguration(w, &c, v.SqlCondition) + if x := v.SqlCondition; x != nil { + c := n.Child("sql_condition") + requiredJobsSqlConditionConfiguration(w, &c, x) + } else if n.IsRef("sql_condition") { + c := n.Child("sql_condition") + requiredJobsSqlConditionConfiguration(w, &c, nil) } } -func requiredJobsTask(w *walker, n *node, v *jobs.Task) { +func requiredJobsTask(w *walker, n *walk.Node, v *jobs.Task) { if v == nil { - v = &jobs.Task{} - } - if !(v.TaskKey != "") && !n.has("task_key") { w.missing(n, "task_key") + return } - if c := n.child("ai_runtime_task"); c.exists() { - requiredJobsAiRuntimeTask(w, &c, v.AiRuntimeTask) - } - if c := n.child("clean_rooms_notebook_task"); c.exists() { - requiredJobsCleanRoomsNotebookTask(w, &c, v.CleanRoomsNotebookTask) - } - if c := n.child("condition_task"); c.exists() { - requiredJobsConditionTask(w, &c, v.ConditionTask) - } - if c := n.child("dashboard_task"); c.exists() { - requiredJobsDashboardTask(w, &c, v.DashboardTask) + if !(v.TaskKey != "" || walk.Forced(v.ForceSendFields, "TaskKey")) { + w.missing(n, "task_key") } - if c := n.child("dbt_task"); c.exists() { - requiredJobsDbtTask(w, &c, v.DbtTask) + if x := v.AiRuntimeTask; x != nil { + c := n.Child("ai_runtime_task") + requiredJobsAiRuntimeTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AiRuntimeTask") || n.IsRef("ai_runtime_task") { + c := n.Child("ai_runtime_task") + requiredJobsAiRuntimeTask(w, &c, nil) + } + if x := v.CleanRoomsNotebookTask; x != nil { + c := n.Child("clean_rooms_notebook_task") + requiredJobsCleanRoomsNotebookTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "CleanRoomsNotebookTask") || n.IsRef("clean_rooms_notebook_task") { + c := n.Child("clean_rooms_notebook_task") + requiredJobsCleanRoomsNotebookTask(w, &c, nil) + } + if x := v.ConditionTask; x != nil { + c := n.Child("condition_task") + requiredJobsConditionTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ConditionTask") || n.IsRef("condition_task") { + c := n.Child("condition_task") + requiredJobsConditionTask(w, &c, nil) + } + if x := v.DashboardTask; x != nil { + c := n.Child("dashboard_task") + requiredJobsDashboardTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "DashboardTask") || n.IsRef("dashboard_task") { + c := n.Child("dashboard_task") + requiredJobsDashboardTask(w, &c, nil) + } + if x := v.DbtTask; x != nil { + c := n.Child("dbt_task") + requiredJobsDbtTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "DbtTask") || n.IsRef("dbt_task") { + c := n.Child("dbt_task") + requiredJobsDbtTask(w, &c, nil) } if len(v.DependsOn) > 0 { - c := n.child("depends_on") - for i0 := range v.DependsOn { - e := c.elem(i0) - requiredJobsTaskDependency(w, &e, &v.DependsOn[i0]) + c := n.Child("depends_on") + for i := range v.DependsOn { + e := c.Elem(i) + requiredJobsTaskDependency(w, &e, &v.DependsOn[i]) } } - if c := n.child("for_each_task"); c.exists() { - requiredJobsForEachTask(w, &c, v.ForEachTask) - } - if c := n.child("gen_ai_compute_task"); c.exists() { - requiredJobsGenAiComputeTask(w, &c, v.GenAiComputeTask) - } - if c := n.child("health"); c.exists() { - requiredJobsJobsHealthRules(w, &c, v.Health) + if x := v.ForEachTask; x != nil { + c := n.Child("for_each_task") + requiredJobsForEachTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ForEachTask") || n.IsRef("for_each_task") { + c := n.Child("for_each_task") + requiredJobsForEachTask(w, &c, nil) + } + if x := v.GenAiComputeTask; x != nil { + c := n.Child("gen_ai_compute_task") + requiredJobsGenAiComputeTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "GenAiComputeTask") || n.IsRef("gen_ai_compute_task") { + c := n.Child("gen_ai_compute_task") + requiredJobsGenAiComputeTask(w, &c, nil) + } + if x := v.Health; x != nil { + c := n.Child("health") + requiredJobsJobsHealthRules(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Health") || n.IsRef("health") { + c := n.Child("health") + requiredJobsJobsHealthRules(w, &c, nil) } if len(v.Libraries) > 0 { - c := n.child("libraries") - for i0 := range v.Libraries { - e := c.elem(i0) - requiredComputeLibrary(w, &e, &v.Libraries[i0]) + c := n.Child("libraries") + for i := range v.Libraries { + e := c.Elem(i) + requiredComputeLibrary(w, &e, &v.Libraries[i]) } } - if c := n.child("new_cluster"); c.exists() { - requiredComputeClusterSpec(w, &c, v.NewCluster) - } - if c := n.child("notebook_task"); c.exists() { - requiredJobsNotebookTask(w, &c, v.NotebookTask) - } - if c := n.child("pipeline_task"); c.exists() { - requiredJobsPipelineTask(w, &c, v.PipelineTask) - } - if c := n.child("python_wheel_task"); c.exists() { - requiredJobsPythonWheelTask(w, &c, v.PythonWheelTask) - } - if c := n.child("run_job_task"); c.exists() { - requiredJobsRunJobTask(w, &c, v.RunJobTask) - } - if c := n.child("spark_python_task"); c.exists() { - requiredJobsSparkPythonTask(w, &c, v.SparkPythonTask) - } - if c := n.child("sql_task"); c.exists() { - requiredJobsSqlTask(w, &c, v.SqlTask) - } - if c := n.child("webhook_notifications"); c.exists() { - requiredJobsWebhookNotifications(w, &c, v.WebhookNotifications) - } -} - -func requiredJobsTriggerSettings(w *walker, n *node, v *jobs.TriggerSettings) { - if v == nil { - v = &jobs.TriggerSettings{} - } - if c := n.child("file_arrival"); c.exists() { - requiredJobsFileArrivalTriggerConfiguration(w, &c, v.FileArrival) - } - if c := n.child("model"); c.exists() { - requiredJobsModelTriggerConfiguration(w, &c, v.Model) - } - if c := n.child("periodic"); c.exists() { - requiredJobsPeriodicTriggerConfiguration(w, &c, v.Periodic) - } - if c := n.child("sql_condition"); c.exists() { - requiredJobsSqlConditionConfiguration(w, &c, v.SqlCondition) - } - if c := n.child("table_update"); c.exists() { - requiredJobsTableUpdateTriggerConfiguration(w, &c, v.TableUpdate) - } -} - -func requiredJobsTriggerConfiguration(w *walker, n *node, v *jobs.TriggerConfiguration) { - if v == nil { - v = &jobs.TriggerConfiguration{} - } - if c := n.child("continuous"); c.exists() { - requiredJobsContinuousTriggerConfiguration(w, &c, v.Continuous) - } - if c := n.child("file_arrival"); c.exists() { - requiredJobsFileArrivalTriggerConfiguration(w, &c, v.FileArrival) - } - if c := n.child("model"); c.exists() { - requiredJobsModelTriggerConfiguration(w, &c, v.Model) - } - if c := n.child("periodic"); c.exists() { - requiredJobsPeriodicTriggerConfiguration(w, &c, v.Periodic) - } - if c := n.child("schedule"); c.exists() { - requiredJobsCronTriggerConfiguration(w, &c, v.Schedule) - } - if c := n.child("sql_condition"); c.exists() { - requiredJobsSqlConditionConfiguration(w, &c, v.SqlCondition) - } - if c := n.child("table_update"); c.exists() { - requiredJobsTableUpdateTriggerConfiguration(w, &c, v.TableUpdate) - } -} - -func requiredJobsWebhookNotifications(w *walker, n *node, v *jobs.WebhookNotifications) { + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + requiredComputeClusterSpec(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "NewCluster") || n.IsRef("new_cluster") { + c := n.Child("new_cluster") + requiredComputeClusterSpec(w, &c, nil) + } + if x := v.NotebookTask; x != nil { + c := n.Child("notebook_task") + requiredJobsNotebookTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "NotebookTask") || n.IsRef("notebook_task") { + c := n.Child("notebook_task") + requiredJobsNotebookTask(w, &c, nil) + } + if x := v.PipelineTask; x != nil { + c := n.Child("pipeline_task") + requiredJobsPipelineTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "PipelineTask") || n.IsRef("pipeline_task") { + c := n.Child("pipeline_task") + requiredJobsPipelineTask(w, &c, nil) + } + if x := v.PythonWheelTask; x != nil { + c := n.Child("python_wheel_task") + requiredJobsPythonWheelTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "PythonWheelTask") || n.IsRef("python_wheel_task") { + c := n.Child("python_wheel_task") + requiredJobsPythonWheelTask(w, &c, nil) + } + if x := v.RunJobTask; x != nil { + c := n.Child("run_job_task") + requiredJobsRunJobTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "RunJobTask") || n.IsRef("run_job_task") { + c := n.Child("run_job_task") + requiredJobsRunJobTask(w, &c, nil) + } + if x := v.SparkPythonTask; x != nil { + c := n.Child("spark_python_task") + requiredJobsSparkPythonTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SparkPythonTask") || n.IsRef("spark_python_task") { + c := n.Child("spark_python_task") + requiredJobsSparkPythonTask(w, &c, nil) + } + if x := v.SqlTask; x != nil { + c := n.Child("sql_task") + requiredJobsSqlTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SqlTask") || n.IsRef("sql_task") { + c := n.Child("sql_task") + requiredJobsSqlTask(w, &c, nil) + } + if x := v.WebhookNotifications; x != nil { + c := n.Child("webhook_notifications") + requiredJobsWebhookNotifications(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "WebhookNotifications") || n.IsRef("webhook_notifications") { + c := n.Child("webhook_notifications") + requiredJobsWebhookNotifications(w, &c, nil) + } +} + +func requiredJobsTriggerSettings(w *walker, n *walk.Node, v *jobs.TriggerSettings) { + if v == nil { + return + } + if x := v.FileArrival; x != nil { + c := n.Child("file_arrival") + requiredJobsFileArrivalTriggerConfiguration(w, &c, x) + } else if n.IsRef("file_arrival") { + c := n.Child("file_arrival") + requiredJobsFileArrivalTriggerConfiguration(w, &c, nil) + } + if x := v.Model; x != nil { + c := n.Child("model") + requiredJobsModelTriggerConfiguration(w, &c, x) + } else if n.IsRef("model") { + c := n.Child("model") + requiredJobsModelTriggerConfiguration(w, &c, nil) + } + if x := v.Periodic; x != nil { + c := n.Child("periodic") + requiredJobsPeriodicTriggerConfiguration(w, &c, x) + } else if n.IsRef("periodic") { + c := n.Child("periodic") + requiredJobsPeriodicTriggerConfiguration(w, &c, nil) + } + if x := v.SqlCondition; x != nil { + c := n.Child("sql_condition") + requiredJobsSqlConditionConfiguration(w, &c, x) + } else if n.IsRef("sql_condition") { + c := n.Child("sql_condition") + requiredJobsSqlConditionConfiguration(w, &c, nil) + } + if x := v.TableUpdate; x != nil { + c := n.Child("table_update") + requiredJobsTableUpdateTriggerConfiguration(w, &c, x) + } else if n.IsRef("table_update") { + c := n.Child("table_update") + requiredJobsTableUpdateTriggerConfiguration(w, &c, nil) + } +} + +func requiredJobsTriggerConfiguration(w *walker, n *walk.Node, v *jobs.TriggerConfiguration) { + if v == nil { + return + } + if x := v.Continuous; x != nil { + c := n.Child("continuous") + requiredJobsContinuousTriggerConfiguration(w, &c, x) + } else if n.IsRef("continuous") { + c := n.Child("continuous") + requiredJobsContinuousTriggerConfiguration(w, &c, nil) + } + if x := v.FileArrival; x != nil { + c := n.Child("file_arrival") + requiredJobsFileArrivalTriggerConfiguration(w, &c, x) + } else if n.IsRef("file_arrival") { + c := n.Child("file_arrival") + requiredJobsFileArrivalTriggerConfiguration(w, &c, nil) + } + if x := v.Model; x != nil { + c := n.Child("model") + requiredJobsModelTriggerConfiguration(w, &c, x) + } else if n.IsRef("model") { + c := n.Child("model") + requiredJobsModelTriggerConfiguration(w, &c, nil) + } + if x := v.Periodic; x != nil { + c := n.Child("periodic") + requiredJobsPeriodicTriggerConfiguration(w, &c, x) + } else if n.IsRef("periodic") { + c := n.Child("periodic") + requiredJobsPeriodicTriggerConfiguration(w, &c, nil) + } + if x := v.Schedule; x != nil { + c := n.Child("schedule") + requiredJobsCronTriggerConfiguration(w, &c, x) + } else if n.IsRef("schedule") { + c := n.Child("schedule") + requiredJobsCronTriggerConfiguration(w, &c, nil) + } + if x := v.SqlCondition; x != nil { + c := n.Child("sql_condition") + requiredJobsSqlConditionConfiguration(w, &c, x) + } else if n.IsRef("sql_condition") { + c := n.Child("sql_condition") + requiredJobsSqlConditionConfiguration(w, &c, nil) + } + if x := v.TableUpdate; x != nil { + c := n.Child("table_update") + requiredJobsTableUpdateTriggerConfiguration(w, &c, x) + } else if n.IsRef("table_update") { + c := n.Child("table_update") + requiredJobsTableUpdateTriggerConfiguration(w, &c, nil) + } +} + +func requiredJobsWebhookNotifications(w *walker, n *walk.Node, v *jobs.WebhookNotifications) { if v == nil { - v = &jobs.WebhookNotifications{} + return } if len(v.OnDurationWarningThresholdExceeded) > 0 { - c := n.child("on_duration_warning_threshold_exceeded") - for i0 := range v.OnDurationWarningThresholdExceeded { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnDurationWarningThresholdExceeded[i0]) + c := n.Child("on_duration_warning_threshold_exceeded") + for i := range v.OnDurationWarningThresholdExceeded { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnDurationWarningThresholdExceeded[i]) } } if len(v.OnFailure) > 0 { - c := n.child("on_failure") - for i0 := range v.OnFailure { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnFailure[i0]) + c := n.Child("on_failure") + for i := range v.OnFailure { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnFailure[i]) } } if len(v.OnMaintenanceComplete) > 0 { - c := n.child("on_maintenance_complete") - for i0 := range v.OnMaintenanceComplete { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnMaintenanceComplete[i0]) + c := n.Child("on_maintenance_complete") + for i := range v.OnMaintenanceComplete { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnMaintenanceComplete[i]) } } if len(v.OnMaintenanceStart) > 0 { - c := n.child("on_maintenance_start") - for i0 := range v.OnMaintenanceStart { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnMaintenanceStart[i0]) + c := n.Child("on_maintenance_start") + for i := range v.OnMaintenanceStart { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnMaintenanceStart[i]) } } if len(v.OnStart) > 0 { - c := n.child("on_start") - for i0 := range v.OnStart { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnStart[i0]) + c := n.Child("on_start") + for i := range v.OnStart { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnStart[i]) } } if len(v.OnStreamingBacklogExceeded) > 0 { - c := n.child("on_streaming_backlog_exceeded") - for i0 := range v.OnStreamingBacklogExceeded { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnStreamingBacklogExceeded[i0]) + c := n.Child("on_streaming_backlog_exceeded") + for i := range v.OnStreamingBacklogExceeded { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnStreamingBacklogExceeded[i]) } } if len(v.OnSuccess) > 0 { - c := n.child("on_success") - for i0 := range v.OnSuccess { - e := c.elem(i0) - requiredJobsWebhook(w, &e, &v.OnSuccess[i0]) + c := n.Child("on_success") + for i := range v.OnSuccess { + e := c.Elem(i) + requiredJobsWebhook(w, &e, &v.OnSuccess[i]) } } } -func requiredCatalogMcpServiceConfig(w *walker, n *node, v *catalog.McpServiceConfig) { +func requiredCatalogMcpServiceConfig(w *walker, n *walk.Node, v *catalog.McpServiceConfig) { if v == nil { - v = &catalog.McpServiceConfig{} + return } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - requiredCatalogRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + requiredCatalogRateLimit(w, &e, &v.RateLimits[i]) } } - if c := n.child("source_connection"); c.exists() { - requiredCatalogMcpServiceConfigSourceConnection(w, &c, v.SourceConnection) - } -} - -func requiredCatalogModelProviderServiceConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfig) { - if v == nil { - v = &catalog.ModelProviderServiceConfig{} - } - if c := n.child("amazon_bedrock"); c.exists() { - requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w, &c, v.AmazonBedrock) - } - if c := n.child("anthropic"); c.exists() { - requiredCatalogModelProviderServiceConfigAnthropicProviderConfig(w, &c, v.Anthropic) - } - if c := n.child("azure_openai"); c.exists() { - requiredCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(w, &c, v.AzureOpenai) - } - if c := n.child("bedrock_mantle"); c.exists() { - requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w, &c, v.BedrockMantle) - } - if c := n.child("custom"); c.exists() { - requiredCatalogModelProviderServiceConfigCustomProviderConfig(w, &c, v.Custom) - } - if c := n.child("gemini_enterprise"); c.exists() { - requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(w, &c, v.GeminiEnterprise) - } - if c := n.child("inference_table"); c.exists() { - requiredCatalogInferenceTableConfig(w, &c, v.InferenceTable) - } - if c := n.child("microsoft_foundry"); c.exists() { - requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(w, &c, v.MicrosoftFoundry) - } - if c := n.child("openai"); c.exists() { - requiredCatalogModelProviderServiceConfigOpenAiProviderConfig(w, &c, v.Openai) + if x := v.SourceConnection; x != nil { + c := n.Child("source_connection") + requiredCatalogMcpServiceConfigSourceConnection(w, &c, x) + } else if n.IsRef("source_connection") { + c := n.Child("source_connection") + requiredCatalogMcpServiceConfigSourceConnection(w, &c, nil) + } +} + +func requiredCatalogModelProviderServiceConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfig) { + if v == nil { + return + } + if x := v.AmazonBedrock; x != nil { + c := n.Child("amazon_bedrock") + requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AmazonBedrock") || n.IsRef("amazon_bedrock") { + c := n.Child("amazon_bedrock") + requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w, &c, nil) + } + if x := v.Anthropic; x != nil { + c := n.Child("anthropic") + requiredCatalogModelProviderServiceConfigAnthropicProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Anthropic") || n.IsRef("anthropic") { + c := n.Child("anthropic") + requiredCatalogModelProviderServiceConfigAnthropicProviderConfig(w, &c, nil) + } + if x := v.AzureOpenai; x != nil { + c := n.Child("azure_openai") + requiredCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AzureOpenai") || n.IsRef("azure_openai") { + c := n.Child("azure_openai") + requiredCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(w, &c, nil) + } + if x := v.BedrockMantle; x != nil { + c := n.Child("bedrock_mantle") + requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "BedrockMantle") || n.IsRef("bedrock_mantle") { + c := n.Child("bedrock_mantle") + requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w, &c, nil) + } + if x := v.Custom; x != nil { + c := n.Child("custom") + requiredCatalogModelProviderServiceConfigCustomProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Custom") || n.IsRef("custom") { + c := n.Child("custom") + requiredCatalogModelProviderServiceConfigCustomProviderConfig(w, &c, nil) + } + if x := v.GeminiEnterprise; x != nil { + c := n.Child("gemini_enterprise") + requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "GeminiEnterprise") || n.IsRef("gemini_enterprise") { + c := n.Child("gemini_enterprise") + requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(w, &c, nil) + } + if x := v.InferenceTable; x != nil { + c := n.Child("inference_table") + requiredCatalogInferenceTableConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "InferenceTable") || n.IsRef("inference_table") { + c := n.Child("inference_table") + requiredCatalogInferenceTableConfig(w, &c, nil) + } + if x := v.MicrosoftFoundry; x != nil { + c := n.Child("microsoft_foundry") + requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "MicrosoftFoundry") || n.IsRef("microsoft_foundry") { + c := n.Child("microsoft_foundry") + requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(w, &c, nil) + } + if x := v.Openai; x != nil { + c := n.Child("openai") + requiredCatalogModelProviderServiceConfigOpenAiProviderConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Openai") || n.IsRef("openai") { + c := n.Child("openai") + requiredCatalogModelProviderServiceConfigOpenAiProviderConfig(w, &c, nil) } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - requiredCatalogRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + requiredCatalogRateLimit(w, &e, &v.RateLimits[i]) } } if len(v.Targets) > 0 { - c := n.child("targets") - for i0 := range v.Targets { - e := c.elem(i0) - requiredCatalogModelProviderServiceConfigModelTargetConfig(w, &e, &v.Targets[i0]) + c := n.Child("targets") + for i := range v.Targets { + e := c.Elem(i) + requiredCatalogModelProviderServiceConfigModelTargetConfig(w, &e, &v.Targets[i]) } } } -func requiredCatalogModelServiceConfig(w *walker, n *node, v *catalog.ModelServiceConfig) { +func requiredCatalogModelServiceConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfig) { if v == nil { - v = &catalog.ModelServiceConfig{} + return } - if c := n.child("inference_table"); c.exists() { - requiredCatalogInferenceTableConfig(w, &c, v.InferenceTable) + if x := v.InferenceTable; x != nil { + c := n.Child("inference_table") + requiredCatalogInferenceTableConfig(w, &c, x) + } else if n.IsRef("inference_table") { + c := n.Child("inference_table") + requiredCatalogInferenceTableConfig(w, &c, nil) } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - requiredCatalogRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + requiredCatalogRateLimit(w, &e, &v.RateLimits[i]) } } - if c := n.child("routing"); c.exists() { - requiredCatalogModelServiceConfigRoutingConfig(w, &c, v.Routing) + if x := v.Routing; x != nil { + c := n.Child("routing") + requiredCatalogModelServiceConfigRoutingConfig(w, &c, x) + } else if n.IsRef("routing") { + c := n.Child("routing") + requiredCatalogModelServiceConfigRoutingConfig(w, &c, nil) } } -func requiredServingAiGatewayConfig(w *walker, n *node, v *serving.AiGatewayConfig) { +func requiredServingAiGatewayConfig(w *walker, n *walk.Node, v *serving.AiGatewayConfig) { if v == nil { - v = &serving.AiGatewayConfig{} + return } - if c := n.child("fallback_config"); c.exists() { - requiredServingFallbackConfig(w, &c, v.FallbackConfig) + if x := v.FallbackConfig; x != nil { + c := n.Child("fallback_config") + requiredServingFallbackConfig(w, &c, x) + } else if n.IsRef("fallback_config") { + c := n.Child("fallback_config") + requiredServingFallbackConfig(w, &c, nil) } if len(v.RateLimits) > 0 { - c := n.child("rate_limits") - for i0 := range v.RateLimits { - e := c.elem(i0) - requiredServingAiGatewayRateLimit(w, &e, &v.RateLimits[i0]) + c := n.Child("rate_limits") + for i := range v.RateLimits { + e := c.Elem(i) + requiredServingAiGatewayRateLimit(w, &e, &v.RateLimits[i]) } } } -func requiredServingEndpointCoreConfigInput(w *walker, n *node, v *serving.EndpointCoreConfigInput) { +func requiredServingEndpointCoreConfigInput(w *walker, n *walk.Node, v *serving.EndpointCoreConfigInput) { if v == nil { - v = &serving.EndpointCoreConfigInput{} + return } if len(v.ServedEntities) > 0 { - c := n.child("served_entities") - for i0 := range v.ServedEntities { - e := c.elem(i0) - requiredServingServedEntityInput(w, &e, &v.ServedEntities[i0]) + c := n.Child("served_entities") + for i := range v.ServedEntities { + e := c.Elem(i) + requiredServingServedEntityInput(w, &e, &v.ServedEntities[i]) } } if len(v.ServedModels) > 0 { - c := n.child("served_models") - for i0 := range v.ServedModels { - e := c.elem(i0) - requiredServingServedModelInput(w, &e, &v.ServedModels[i0]) + c := n.Child("served_models") + for i := range v.ServedModels { + e := c.Elem(i) + requiredServingServedModelInput(w, &e, &v.ServedModels[i]) } } - if c := n.child("traffic_config"); c.exists() { - requiredServingTrafficConfig(w, &c, v.TrafficConfig) + if x := v.TrafficConfig; x != nil { + c := n.Child("traffic_config") + requiredServingTrafficConfig(w, &c, x) + } else if n.IsRef("traffic_config") { + c := n.Child("traffic_config") + requiredServingTrafficConfig(w, &c, nil) } } -func requiredResourcesModelServingEndpointPermission(w *walker, n *node, v *resources.ModelServingEndpointPermission) { +func requiredResourcesModelServingEndpointPermission(w *walker, n *walk.Node, v *resources.ModelServingEndpointPermission) { if v == nil { - v = &resources.ModelServingEndpointPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredServingRateLimit(w *walker, n *node, v *serving.RateLimit) { +func requiredServingRateLimit(w *walker, n *walk.Node, v *serving.RateLimit) { if v == nil { - v = &serving.RateLimit{} + w.missing(n, "calls") + w.missing(n, "renewal_period") + return } - if !(v.Calls != 0) && !n.has("calls") { + if !(v.Calls != 0 || n.IsRef("calls")) { w.missing(n, "calls") } - if !(v.RenewalPeriod != "") && !n.has("renewal_period") { + if !(v.RenewalPeriod != "") { w.missing(n, "renewal_period") } } -func requiredServingEndpointTag(w *walker, n *node, v *serving.EndpointTag) { +func requiredServingEndpointTag(w *walker, n *walk.Node, v *serving.EndpointTag) { if v == nil { - v = &serving.EndpointTag{} + w.missing(n, "key") + return } - if !(v.Key != "") && !n.has("key") { + if !(v.Key != "" || walk.Forced(v.ForceSendFields, "Key")) { w.missing(n, "key") } } -func requiredResourcesMlflowModelPermission(w *walker, n *node, v *resources.MlflowModelPermission) { +func requiredResourcesMlflowModelPermission(w *walker, n *walk.Node, v *resources.MlflowModelPermission) { if v == nil { - v = &resources.MlflowModelPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredPipelinesPipelineCluster(w *walker, n *node, v *pipelines.PipelineCluster) { +func requiredPipelinesPipelineCluster(w *walker, n *walk.Node, v *pipelines.PipelineCluster) { if v == nil { - v = &pipelines.PipelineCluster{} + return } - if c := n.child("autoscale"); c.exists() { - requiredPipelinesPipelineClusterAutoscale(w, &c, v.Autoscale) + if x := v.Autoscale; x != nil { + c := n.Child("autoscale") + requiredPipelinesPipelineClusterAutoscale(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Autoscale") || n.IsRef("autoscale") { + c := n.Child("autoscale") + requiredPipelinesPipelineClusterAutoscale(w, &c, nil) } - if c := n.child("cluster_log_conf"); c.exists() { - requiredComputeClusterLogConf(w, &c, v.ClusterLogConf) + if x := v.ClusterLogConf; x != nil { + c := n.Child("cluster_log_conf") + requiredComputeClusterLogConf(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ClusterLogConf") || n.IsRef("cluster_log_conf") { + c := n.Child("cluster_log_conf") + requiredComputeClusterLogConf(w, &c, nil) } if len(v.InitScripts) > 0 { - c := n.child("init_scripts") - for i0 := range v.InitScripts { - e := c.elem(i0) - requiredComputeInitScriptInfo(w, &e, &v.InitScripts[i0]) + c := n.Child("init_scripts") + for i := range v.InitScripts { + e := c.Elem(i) + requiredComputeInitScriptInfo(w, &e, &v.InitScripts[i]) } } } -func requiredPipelinesPipelineDeployment(w *walker, n *node, v *pipelines.PipelineDeployment) { +func requiredPipelinesPipelineDeployment(w *walker, n *walk.Node, v *pipelines.PipelineDeployment) { if v == nil { - v = &pipelines.PipelineDeployment{} + w.missing(n, "kind") + return } - if !(v.Kind != "") && !n.has("kind") { + if !(v.Kind != "" || walk.Forced(v.ForceSendFields, "Kind")) { w.missing(n, "kind") } } -func requiredPipelinesIngestionGatewayPipelineDefinition(w *walker, n *node, v *pipelines.IngestionGatewayPipelineDefinition) { +func requiredPipelinesIngestionGatewayPipelineDefinition(w *walker, n *walk.Node, v *pipelines.IngestionGatewayPipelineDefinition) { if v == nil { - v = &pipelines.IngestionGatewayPipelineDefinition{} + w.missing(n, "connection_name") + w.missing(n, "gateway_storage_catalog") + w.missing(n, "gateway_storage_schema") + return } - if !(v.ConnectionName != "") && !n.has("connection_name") { + if !(v.ConnectionName != "" || walk.Forced(v.ForceSendFields, "ConnectionName")) { w.missing(n, "connection_name") } - if !(v.GatewayStorageCatalog != "") && !n.has("gateway_storage_catalog") { + if !(v.GatewayStorageCatalog != "" || walk.Forced(v.ForceSendFields, "GatewayStorageCatalog")) { w.missing(n, "gateway_storage_catalog") } - if !(v.GatewayStorageSchema != "") && !n.has("gateway_storage_schema") { + if !(v.GatewayStorageSchema != "" || walk.Forced(v.ForceSendFields, "GatewayStorageSchema")) { w.missing(n, "gateway_storage_schema") } } -func requiredPipelinesIngestionPipelineDefinition(w *walker, n *node, v *pipelines.IngestionPipelineDefinition) { +func requiredPipelinesIngestionPipelineDefinition(w *walker, n *walk.Node, v *pipelines.IngestionPipelineDefinition) { if v == nil { - v = &pipelines.IngestionPipelineDefinition{} + return } - if c := n.child("data_staging_options"); c.exists() { - requiredPipelinesDataStagingOptions(w, &c, v.DataStagingOptions) + if x := v.DataStagingOptions; x != nil { + c := n.Child("data_staging_options") + requiredPipelinesDataStagingOptions(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "DataStagingOptions") || n.IsRef("data_staging_options") { + c := n.Child("data_staging_options") + requiredPipelinesDataStagingOptions(w, &c, nil) } - if c := n.child("full_refresh_window"); c.exists() { - requiredPipelinesOperationTimeWindow(w, &c, v.FullRefreshWindow) + if x := v.FullRefreshWindow; x != nil { + c := n.Child("full_refresh_window") + requiredPipelinesOperationTimeWindow(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "FullRefreshWindow") || n.IsRef("full_refresh_window") { + c := n.Child("full_refresh_window") + requiredPipelinesOperationTimeWindow(w, &c, nil) } if len(v.Objects) > 0 { - c := n.child("objects") - for i0 := range v.Objects { - e := c.elem(i0) - requiredPipelinesIngestionConfig(w, &e, &v.Objects[i0]) + c := n.Child("objects") + for i := range v.Objects { + e := c.Elem(i) + requiredPipelinesIngestionConfig(w, &e, &v.Objects[i]) } } - if c := n.child("table_configuration"); c.exists() { - requiredPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "TableConfiguration") || n.IsRef("table_configuration") { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, nil) } } -func requiredPipelinesPipelineLibrary(w *walker, n *node, v *pipelines.PipelineLibrary) { +func requiredPipelinesPipelineLibrary(w *walker, n *walk.Node, v *pipelines.PipelineLibrary) { if v == nil { - v = &pipelines.PipelineLibrary{} + return } - if c := n.child("maven"); c.exists() { - requiredComputeMavenLibrary(w, &c, v.Maven) + if x := v.Maven; x != nil { + c := n.Child("maven") + requiredComputeMavenLibrary(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Maven") || n.IsRef("maven") { + c := n.Child("maven") + requiredComputeMavenLibrary(w, &c, nil) } } -func requiredResourcesPipelinePermission(w *walker, n *node, v *resources.PipelinePermission) { +func requiredResourcesPipelinePermission(w *walker, n *walk.Node, v *resources.PipelinePermission) { if v == nil { - v = &resources.PipelinePermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredPipelinesRestartWindow(w *walker, n *node, v *pipelines.RestartWindow) { +func requiredPipelinesRestartWindow(w *walker, n *walk.Node, v *pipelines.RestartWindow) { if v == nil { - v = &pipelines.RestartWindow{} + w.missing(n, "start_hour") + return } - if !(v.StartHour != 0) && !n.has("start_hour") { + if !(v.StartHour != 0 || walk.Forced(v.ForceSendFields, "StartHour") || n.IsRef("start_hour")) { w.missing(n, "start_hour") } } -func requiredPostgresEndpointGroupSpec(w *walker, n *node, v *postgres.EndpointGroupSpec) { +func requiredPostgresEndpointGroupSpec(w *walker, n *walk.Node, v *postgres.EndpointGroupSpec) { if v == nil { - v = &postgres.EndpointGroupSpec{} + w.missing(n, "max") + w.missing(n, "min") + return } - if !(v.Max != 0) && !n.has("max") { + if !(v.Max != 0 || walk.Forced(v.ForceSendFields, "Max") || n.IsRef("max")) { w.missing(n, "max") } - if !(v.Min != 0) && !n.has("min") { + if !(v.Min != 0 || walk.Forced(v.ForceSendFields, "Min") || n.IsRef("min")) { w.missing(n, "min") } } -func requiredPostgresScheduleCadence(w *walker, n *node, v *postgres.ScheduleCadence) { +func requiredPostgresScheduleCadence(w *walker, n *walk.Node, v *postgres.ScheduleCadence) { if v == nil { - v = &postgres.ScheduleCadence{} + w.missing(n, "retention") + return } - if !n.has("retention") { + if !(!walk.IsZeroStruct(&v.Retention) || n.HasRefs("retention")) { w.missing(n, "retention") } - if c := n.child("monthly_schedule"); c.exists() { - requiredPostgresMonthlySchedule(w, &c, v.MonthlySchedule) + if x := v.MonthlySchedule; x != nil { + c := n.Child("monthly_schedule") + requiredPostgresMonthlySchedule(w, &c, x) + } else if n.IsRef("monthly_schedule") { + c := n.Child("monthly_schedule") + requiredPostgresMonthlySchedule(w, &c, nil) } - if c := n.child("weekly_schedule"); c.exists() { - requiredPostgresWeeklySchedule(w, &c, v.WeeklySchedule) + if x := v.WeeklySchedule; x != nil { + c := n.Child("weekly_schedule") + requiredPostgresWeeklySchedule(w, &c, x) + } else if n.IsRef("weekly_schedule") { + c := n.Child("weekly_schedule") + requiredPostgresWeeklySchedule(w, &c, nil) } } -func requiredPostgresSyncedTableSyncedTableSpecExtraColumn(w *walker, n *node, v *postgres.SyncedTableSyncedTableSpecExtraColumn) { +func requiredPostgresSyncedTableSyncedTableSpecExtraColumn(w *walker, n *walk.Node, v *postgres.SyncedTableSyncedTableSpecExtraColumn) { if v == nil { - v = &postgres.SyncedTableSyncedTableSpecExtraColumn{} + w.missing(n, "column_name") + w.missing(n, "column_type") + return } - if !(v.ColumnName != "") && !n.has("column_name") { + if !(v.ColumnName != "" || walk.Forced(v.ForceSendFields, "ColumnName")) { w.missing(n, "column_name") } - if !(v.ColumnType != "") && !n.has("column_type") { + if !(v.ColumnType != "" || walk.Forced(v.ForceSendFields, "ColumnType")) { w.missing(n, "column_type") } } -func requiredPostgresSyncedTableSyncedTableSpecTypeOverride(w *walker, n *node, v *postgres.SyncedTableSyncedTableSpecTypeOverride) { +func requiredPostgresSyncedTableSyncedTableSpecTypeOverride(w *walker, n *walk.Node, v *postgres.SyncedTableSyncedTableSpecTypeOverride) { if v == nil { - v = &postgres.SyncedTableSyncedTableSpecTypeOverride{} + w.missing(n, "column_name") + w.missing(n, "pg_type") + return } - if !(v.ColumnName != "") && !n.has("column_name") { + if !(v.ColumnName != "" || walk.Forced(v.ForceSendFields, "ColumnName")) { w.missing(n, "column_name") } - if !(v.PgType != "") && !n.has("pg_type") { + if !(v.PgType != "" || walk.Forced(v.ForceSendFields, "PgType")) { w.missing(n, "pg_type") } } -func requiredCatalogMonitorMetric(w *walker, n *node, v *catalog.MonitorMetric) { +func requiredCatalogMonitorMetric(w *walker, n *walk.Node, v *catalog.MonitorMetric) { if v == nil { - v = &catalog.MonitorMetric{} + w.missing(n, "definition") + w.missing(n, "input_columns") + w.missing(n, "name") + w.missing(n, "output_data_type") + w.missing(n, "type") + return } - if !(v.Definition != "") && !n.has("definition") { + if !(v.Definition != "") { w.missing(n, "definition") } - if !(v.InputColumns != nil) && !n.has("input_columns") { + if !(v.InputColumns != nil || n.IsRef("input_columns")) { w.missing(n, "input_columns") } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } - if !(v.OutputDataType != "") && !n.has("output_data_type") { + if !(v.OutputDataType != "") { w.missing(n, "output_data_type") } - if !(v.Type != "") && !n.has("type") { + if !(v.Type != "") { w.missing(n, "type") } } -func requiredCatalogMonitorInferenceLog(w *walker, n *node, v *catalog.MonitorInferenceLog) { +func requiredCatalogMonitorInferenceLog(w *walker, n *walk.Node, v *catalog.MonitorInferenceLog) { if v == nil { - v = &catalog.MonitorInferenceLog{} + w.missing(n, "granularities") + w.missing(n, "model_id_col") + w.missing(n, "prediction_col") + w.missing(n, "problem_type") + w.missing(n, "timestamp_col") + return } - if !(v.Granularities != nil) && !n.has("granularities") { + if !(v.Granularities != nil || n.IsRef("granularities")) { w.missing(n, "granularities") } - if !(v.ModelIdCol != "") && !n.has("model_id_col") { + if !(v.ModelIdCol != "" || walk.Forced(v.ForceSendFields, "ModelIdCol")) { w.missing(n, "model_id_col") } - if !(v.PredictionCol != "") && !n.has("prediction_col") { + if !(v.PredictionCol != "" || walk.Forced(v.ForceSendFields, "PredictionCol")) { w.missing(n, "prediction_col") } - if !(v.ProblemType != "") && !n.has("problem_type") { + if !(v.ProblemType != "" || walk.Forced(v.ForceSendFields, "ProblemType")) { w.missing(n, "problem_type") } - if !(v.TimestampCol != "") && !n.has("timestamp_col") { + if !(v.TimestampCol != "" || walk.Forced(v.ForceSendFields, "TimestampCol")) { w.missing(n, "timestamp_col") } } -func requiredCatalogMonitorCronSchedule(w *walker, n *node, v *catalog.MonitorCronSchedule) { +func requiredCatalogMonitorCronSchedule(w *walker, n *walk.Node, v *catalog.MonitorCronSchedule) { if v == nil { - v = &catalog.MonitorCronSchedule{} + w.missing(n, "quartz_cron_expression") + w.missing(n, "timezone_id") + return } - if !(v.QuartzCronExpression != "") && !n.has("quartz_cron_expression") { + if !(v.QuartzCronExpression != "") { w.missing(n, "quartz_cron_expression") } - if !(v.TimezoneId != "") && !n.has("timezone_id") { + if !(v.TimezoneId != "") { w.missing(n, "timezone_id") } } -func requiredCatalogMonitorTimeSeries(w *walker, n *node, v *catalog.MonitorTimeSeries) { +func requiredCatalogMonitorTimeSeries(w *walker, n *walk.Node, v *catalog.MonitorTimeSeries) { if v == nil { - v = &catalog.MonitorTimeSeries{} + w.missing(n, "granularities") + w.missing(n, "timestamp_col") + return } - if !(v.Granularities != nil) && !n.has("granularities") { + if !(v.Granularities != nil || n.IsRef("granularities")) { w.missing(n, "granularities") } - if !(v.TimestampCol != "") && !n.has("timestamp_col") { + if !(v.TimestampCol != "") { w.missing(n, "timestamp_col") } } -func requiredWorkspaceAzureKeyVaultSecretScopeMetadata(w *walker, n *node, v *workspace.AzureKeyVaultSecretScopeMetadata) { +func requiredWorkspaceAzureKeyVaultSecretScopeMetadata(w *walker, n *walk.Node, v *workspace.AzureKeyVaultSecretScopeMetadata) { if v == nil { - v = &workspace.AzureKeyVaultSecretScopeMetadata{} + w.missing(n, "dns_name") + w.missing(n, "resource_id") + return } - if !(v.DnsName != "") && !n.has("dns_name") { + if !(v.DnsName != "") { w.missing(n, "dns_name") } - if !(v.ResourceId != "") && !n.has("resource_id") { + if !(v.ResourceId != "") { w.missing(n, "resource_id") } } -func requiredResourcesSecretScopePermission(w *walker, n *node, v *resources.SecretScopePermission) { +func requiredResourcesSecretScopePermission(w *walker, n *walk.Node, v *resources.SecretScopePermission) { if v == nil { - v = &resources.SecretScopePermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredResourcesSqlWarehousePermission(w *walker, n *node, v *resources.SqlWarehousePermission) { +func requiredResourcesSqlWarehousePermission(w *walker, n *walk.Node, v *resources.SqlWarehousePermission) { if v == nil { - v = &resources.SqlWarehousePermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredDatabaseSyncedTableSpec(w *walker, n *node, v *database.SyncedTableSpec) { +func requiredDatabaseSyncedTableSpec(w *walker, n *walk.Node, v *database.SyncedTableSpec) { if v == nil { - v = &database.SyncedTableSpec{} + return } if len(v.TypeOverrides) > 0 { - c := n.child("type_overrides") - for i0 := range v.TypeOverrides { - e := c.elem(i0) - requiredDatabaseSyncedTableSpecTypeOverride(w, &e, &v.TypeOverrides[i0]) + c := n.Child("type_overrides") + for i := range v.TypeOverrides { + e := c.Elem(i) + requiredDatabaseSyncedTableSpecTypeOverride(w, &e, &v.TypeOverrides[i]) } } } -func requiredResourcesVectorSearchEndpointPermission(w *walker, n *node, v *resources.VectorSearchEndpointPermission) { +func requiredResourcesVectorSearchEndpointPermission(w *walker, n *walk.Node, v *resources.VectorSearchEndpointPermission) { if v == nil { - v = &resources.VectorSearchEndpointPermission{} + w.missing(n, "level") + return } - if !(v.Level != "") && !n.has("level") { + if !(v.Level != "") { w.missing(n, "level") } } -func requiredSqlAlertV2OperandColumn(w *walker, n *node, v *sql.AlertV2OperandColumn) { +func requiredSqlAlertV2OperandColumn(w *walker, n *walk.Node, v *sql.AlertV2OperandColumn) { if v == nil { - v = &sql.AlertV2OperandColumn{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { w.missing(n, "name") } } -func requiredSqlAlertV2Operand(w *walker, n *node, v *sql.AlertV2Operand) { +func requiredSqlAlertV2Operand(w *walker, n *walk.Node, v *sql.AlertV2Operand) { if v == nil { - v = &sql.AlertV2Operand{} + return } - if c := n.child("column"); c.exists() { - requiredSqlAlertV2OperandColumn2(w, &c, v.Column) + if x := v.Column; x != nil { + c := n.Child("column") + requiredSqlAlertV2OperandColumn2(w, &c, x) + } else if n.IsRef("column") { + c := n.Child("column") + requiredSqlAlertV2OperandColumn2(w, &c, nil) } } -func requiredResourcesDependsOn(w *walker, n *node, v *resources.DependsOn) { +func requiredResourcesDependsOn(w *walker, n *walk.Node, v *resources.DependsOn) { if v == nil { - v = &resources.DependsOn{} + w.missing(n, "resource") + return } - if !(v.Resource != "") && !n.has("resource") { + if !(v.Resource != "") { w.missing(n, "resource") } } -func requiredResourcesAppEnvVar(w *walker, n *node, v *resources.AppEnvVar) { +func requiredResourcesAppEnvVar(w *walker, n *walk.Node, v *resources.AppEnvVar) { if v == nil { - v = &resources.AppEnvVar{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredAppsAppResourceDatabase(w *walker, n *node, v *apps.AppResourceDatabase) { +func requiredAppsAppResourceDatabase(w *walker, n *walk.Node, v *apps.AppResourceDatabase) { if v == nil { - v = &apps.AppResourceDatabase{} + w.missing(n, "database_name") + w.missing(n, "instance_name") + w.missing(n, "permission") + return } - if !(v.DatabaseName != "") && !n.has("database_name") { + if !(v.DatabaseName != "") { w.missing(n, "database_name") } - if !(v.InstanceName != "") && !n.has("instance_name") { + if !(v.InstanceName != "") { w.missing(n, "instance_name") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } } -func requiredAppsAppResourceExperiment(w *walker, n *node, v *apps.AppResourceExperiment) { +func requiredAppsAppResourceExperiment(w *walker, n *walk.Node, v *apps.AppResourceExperiment) { if v == nil { - v = &apps.AppResourceExperiment{} + w.missing(n, "experiment_id") + w.missing(n, "permission") + return } - if !(v.ExperimentId != "") && !n.has("experiment_id") { + if !(v.ExperimentId != "") { w.missing(n, "experiment_id") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } } -func requiredAppsAppResourceGenieSpace(w *walker, n *node, v *apps.AppResourceGenieSpace) { +func requiredAppsAppResourceGenieSpace(w *walker, n *walk.Node, v *apps.AppResourceGenieSpace) { if v == nil { - v = &apps.AppResourceGenieSpace{} + w.missing(n, "name") + w.missing(n, "permission") + w.missing(n, "space_id") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } - if !(v.SpaceId != "") && !n.has("space_id") { + if !(v.SpaceId != "") { w.missing(n, "space_id") } } -func requiredAppsAppResourceJob(w *walker, n *node, v *apps.AppResourceJob) { +func requiredAppsAppResourceJob(w *walker, n *walk.Node, v *apps.AppResourceJob) { if v == nil { - v = &apps.AppResourceJob{} + w.missing(n, "id") + w.missing(n, "permission") + return } - if !(v.Id != "") && !n.has("id") { + if !(v.Id != "") { w.missing(n, "id") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } } -func requiredAppsAppResourceSecret(w *walker, n *node, v *apps.AppResourceSecret) { +func requiredAppsAppResourceSecret(w *walker, n *walk.Node, v *apps.AppResourceSecret) { if v == nil { - v = &apps.AppResourceSecret{} + w.missing(n, "key") + w.missing(n, "permission") + w.missing(n, "scope") + return } - if !(v.Key != "") && !n.has("key") { + if !(v.Key != "") { w.missing(n, "key") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } - if !(v.Scope != "") && !n.has("scope") { + if !(v.Scope != "") { w.missing(n, "scope") } } -func requiredAppsAppResourceServingEndpoint(w *walker, n *node, v *apps.AppResourceServingEndpoint) { +func requiredAppsAppResourceServingEndpoint(w *walker, n *walk.Node, v *apps.AppResourceServingEndpoint) { if v == nil { - v = &apps.AppResourceServingEndpoint{} + w.missing(n, "name") + w.missing(n, "permission") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } } -func requiredAppsAppResourceSqlWarehouse(w *walker, n *node, v *apps.AppResourceSqlWarehouse) { +func requiredAppsAppResourceSqlWarehouse(w *walker, n *walk.Node, v *apps.AppResourceSqlWarehouse) { if v == nil { - v = &apps.AppResourceSqlWarehouse{} + w.missing(n, "id") + w.missing(n, "permission") + return } - if !(v.Id != "") && !n.has("id") { + if !(v.Id != "") { w.missing(n, "id") } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "") { w.missing(n, "permission") } } -func requiredAppsAppResourceUcSecurable(w *walker, n *node, v *apps.AppResourceUcSecurable) { +func requiredAppsAppResourceUcSecurable(w *walker, n *walk.Node, v *apps.AppResourceUcSecurable) { if v == nil { - v = &apps.AppResourceUcSecurable{} + w.missing(n, "permission") + w.missing(n, "securable_full_name") + w.missing(n, "securable_type") + return } - if !(v.Permission != "") && !n.has("permission") { + if !(v.Permission != "" || walk.Forced(v.ForceSendFields, "Permission")) { w.missing(n, "permission") } - if !(v.SecurableFullName != "") && !n.has("securable_full_name") { + if !(v.SecurableFullName != "" || walk.Forced(v.ForceSendFields, "SecurableFullName")) { w.missing(n, "securable_full_name") } - if !(v.SecurableType != "") && !n.has("securable_type") { + if !(v.SecurableType != "" || walk.Forced(v.ForceSendFields, "SecurableType")) { w.missing(n, "securable_type") } } -func requiredAppsUnityCatalog(w *walker, n *node, v *apps.UnityCatalog) { +func requiredAppsUnityCatalog(w *walker, n *walk.Node, v *apps.UnityCatalog) { if v == nil { - v = &apps.UnityCatalog{} + w.missing(n, "logs_table") + w.missing(n, "metrics_table") + w.missing(n, "traces_table") + return } - if !(v.LogsTable != "") && !n.has("logs_table") { + if !(v.LogsTable != "") { w.missing(n, "logs_table") } - if !(v.MetricsTable != "") && !n.has("metrics_table") { + if !(v.MetricsTable != "") { w.missing(n, "metrics_table") } - if !(v.TracesTable != "") && !n.has("traces_table") { + if !(v.TracesTable != "") { w.missing(n, "traces_table") } } -func requiredCatalogAzureEncryptionSettings(w *walker, n *node, v *catalog.AzureEncryptionSettings) { +func requiredCatalogAzureEncryptionSettings(w *walker, n *walk.Node, v *catalog.AzureEncryptionSettings) { if v == nil { - v = &catalog.AzureEncryptionSettings{} + w.missing(n, "azure_tenant_id") + return } - if !(v.AzureTenantId != "") && !n.has("azure_tenant_id") { + if !(v.AzureTenantId != "" || walk.Forced(v.ForceSendFields, "AzureTenantId")) { w.missing(n, "azure_tenant_id") } } -func requiredComputeRCranLibrary(w *walker, n *node, v *compute.RCranLibrary) { +func requiredComputeRCranLibrary(w *walker, n *walk.Node, v *compute.RCranLibrary) { if v == nil { - v = &compute.RCranLibrary{} + w.missing(n, "package") + return } - if !(v.Package != "") && !n.has("package") { + if !(v.Package != "" || walk.Forced(v.ForceSendFields, "Package")) { w.missing(n, "package") } } -func requiredComputeMavenLibrary(w *walker, n *node, v *compute.MavenLibrary) { +func requiredComputeMavenLibrary(w *walker, n *walk.Node, v *compute.MavenLibrary) { if v == nil { - v = &compute.MavenLibrary{} + w.missing(n, "coordinates") + return } - if !(v.Coordinates != "") && !n.has("coordinates") { + if !(v.Coordinates != "" || walk.Forced(v.ForceSendFields, "Coordinates")) { w.missing(n, "coordinates") } } -func requiredComputePythonPyPiLibrary(w *walker, n *node, v *compute.PythonPyPiLibrary) { +func requiredComputePythonPyPiLibrary(w *walker, n *walk.Node, v *compute.PythonPyPiLibrary) { if v == nil { - v = &compute.PythonPyPiLibrary{} + w.missing(n, "package") + return } - if !(v.Package != "") && !n.has("package") { + if !(v.Package != "" || walk.Forced(v.ForceSendFields, "Package")) { w.missing(n, "package") } } -func requiredComputeDbfsStorageInfo(w *walker, n *node, v *compute.DbfsStorageInfo) { +func requiredComputeDbfsStorageInfo(w *walker, n *walk.Node, v *compute.DbfsStorageInfo) { if v == nil { - v = &compute.DbfsStorageInfo{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "") { w.missing(n, "destination") } } -func requiredComputeS3StorageInfo(w *walker, n *node, v *compute.S3StorageInfo) { +func requiredComputeS3StorageInfo(w *walker, n *walk.Node, v *compute.S3StorageInfo) { if v == nil { - v = &compute.S3StorageInfo{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "" || walk.Forced(v.ForceSendFields, "Destination")) { w.missing(n, "destination") } } -func requiredComputeVolumesStorageInfo(w *walker, n *node, v *compute.VolumesStorageInfo) { +func requiredComputeVolumesStorageInfo(w *walker, n *walk.Node, v *compute.VolumesStorageInfo) { if v == nil { - v = &compute.VolumesStorageInfo{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "") { w.missing(n, "destination") } } -func requiredComputeAdlsgen2Info(w *walker, n *node, v *compute.Adlsgen2Info) { +func requiredComputeAdlsgen2Info(w *walker, n *walk.Node, v *compute.Adlsgen2Info) { if v == nil { - v = &compute.Adlsgen2Info{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "") { w.missing(n, "destination") } } -func requiredComputeLocalFileInfo(w *walker, n *node, v *compute.LocalFileInfo) { +func requiredComputeLocalFileInfo(w *walker, n *walk.Node, v *compute.LocalFileInfo) { if v == nil { - v = &compute.LocalFileInfo{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "") { w.missing(n, "destination") } } -func requiredComputeGcsStorageInfo(w *walker, n *node, v *compute.GcsStorageInfo) { +func requiredComputeGcsStorageInfo(w *walker, n *walk.Node, v *compute.GcsStorageInfo) { if v == nil { - v = &compute.GcsStorageInfo{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "") { w.missing(n, "destination") } } -func requiredComputeWorkspaceStorageInfo(w *walker, n *node, v *compute.WorkspaceStorageInfo) { +func requiredComputeWorkspaceStorageInfo(w *walker, n *walk.Node, v *compute.WorkspaceStorageInfo) { if v == nil { - v = &compute.WorkspaceStorageInfo{} + w.missing(n, "destination") + return } - if !(v.Destination != "") && !n.has("destination") { + if !(v.Destination != "") { w.missing(n, "destination") } } -func requiredMlUcTraceLocation(w *walker, n *node, v *ml.UcTraceLocation) { +func requiredMlUcTraceLocation(w *walker, n *walk.Node, v *ml.UcTraceLocation) { if v == nil { - v = &ml.UcTraceLocation{} - } - if !(v.Catalog != "") && !n.has("catalog") { w.missing(n, "catalog") - } - if !(v.Schema != "") && !n.has("schema") { w.missing(n, "schema") + return } -} - -func requiredMlAggregationFunction(w *walker, n *node, v *ml.AggregationFunction) { - if v == nil { - v = &ml.AggregationFunction{} - } - if c := n.child("approx_count_distinct"); c.exists() { - requiredMlApproxCountDistinctFunction(w, &c, v.ApproxCountDistinct) - } - if c := n.child("approx_percentile"); c.exists() { - requiredMlApproxPercentileFunction(w, &c, v.ApproxPercentile) - } - if c := n.child("avg"); c.exists() { - requiredMlAvgFunction(w, &c, v.Avg) - } - if c := n.child("count_function"); c.exists() { - requiredMlCountFunction(w, &c, v.CountFunction) - } - if c := n.child("first"); c.exists() { - requiredMlFirstFunction(w, &c, v.First) - } - if c := n.child("first_distinct"); c.exists() { - requiredMlFirstDistinctFunction(w, &c, v.FirstDistinct) - } - if c := n.child("first_n"); c.exists() { - requiredMlFirstNFunction(w, &c, v.FirstN) - } - if c := n.child("last"); c.exists() { - requiredMlLastFunction(w, &c, v.Last) - } - if c := n.child("last_distinct"); c.exists() { - requiredMlLastDistinctFunction(w, &c, v.LastDistinct) - } - if c := n.child("last_n"); c.exists() { - requiredMlLastNFunction(w, &c, v.LastN) - } - if c := n.child("max"); c.exists() { - requiredMlMaxFunction(w, &c, v.Max) - } - if c := n.child("min"); c.exists() { - requiredMlMinFunction(w, &c, v.Min) - } - if c := n.child("stddev_pop"); c.exists() { - requiredMlStddevPopFunction(w, &c, v.StddevPop) - } - if c := n.child("stddev_samp"); c.exists() { - requiredMlStddevSampFunction(w, &c, v.StddevSamp) - } - if c := n.child("sum"); c.exists() { - requiredMlSumFunction(w, &c, v.Sum) - } - if c := n.child("time_window"); c.exists() { - requiredMlTimeWindow(w, &c, v.TimeWindow) - } - if c := n.child("var_pop"); c.exists() { - requiredMlVarPopFunction(w, &c, v.VarPop) + if !(v.Catalog != "" || walk.Forced(v.ForceSendFields, "Catalog")) { + w.missing(n, "catalog") } - if c := n.child("var_samp"); c.exists() { - requiredMlVarSampFunction(w, &c, v.VarSamp) + if !(v.Schema != "" || walk.Forced(v.ForceSendFields, "Schema")) { + w.missing(n, "schema") } } -func requiredMlColumnSelection(w *walker, n *node, v *ml.ColumnSelection) { +func requiredMlAggregationFunction(w *walker, n *walk.Node, v *ml.AggregationFunction) { + if v == nil { + return + } + if x := v.ApproxCountDistinct; x != nil { + c := n.Child("approx_count_distinct") + requiredMlApproxCountDistinctFunction(w, &c, x) + } else if n.IsRef("approx_count_distinct") { + c := n.Child("approx_count_distinct") + requiredMlApproxCountDistinctFunction(w, &c, nil) + } + if x := v.ApproxPercentile; x != nil { + c := n.Child("approx_percentile") + requiredMlApproxPercentileFunction(w, &c, x) + } else if n.IsRef("approx_percentile") { + c := n.Child("approx_percentile") + requiredMlApproxPercentileFunction(w, &c, nil) + } + if x := v.Avg; x != nil { + c := n.Child("avg") + requiredMlAvgFunction(w, &c, x) + } else if n.IsRef("avg") { + c := n.Child("avg") + requiredMlAvgFunction(w, &c, nil) + } + if x := v.CountFunction; x != nil { + c := n.Child("count_function") + requiredMlCountFunction(w, &c, x) + } else if n.IsRef("count_function") { + c := n.Child("count_function") + requiredMlCountFunction(w, &c, nil) + } + if x := v.First; x != nil { + c := n.Child("first") + requiredMlFirstFunction(w, &c, x) + } else if n.IsRef("first") { + c := n.Child("first") + requiredMlFirstFunction(w, &c, nil) + } + if x := v.FirstDistinct; x != nil { + c := n.Child("first_distinct") + requiredMlFirstDistinctFunction(w, &c, x) + } else if n.IsRef("first_distinct") { + c := n.Child("first_distinct") + requiredMlFirstDistinctFunction(w, &c, nil) + } + if x := v.FirstN; x != nil { + c := n.Child("first_n") + requiredMlFirstNFunction(w, &c, x) + } else if n.IsRef("first_n") { + c := n.Child("first_n") + requiredMlFirstNFunction(w, &c, nil) + } + if x := v.Last; x != nil { + c := n.Child("last") + requiredMlLastFunction(w, &c, x) + } else if n.IsRef("last") { + c := n.Child("last") + requiredMlLastFunction(w, &c, nil) + } + if x := v.LastDistinct; x != nil { + c := n.Child("last_distinct") + requiredMlLastDistinctFunction(w, &c, x) + } else if n.IsRef("last_distinct") { + c := n.Child("last_distinct") + requiredMlLastDistinctFunction(w, &c, nil) + } + if x := v.LastN; x != nil { + c := n.Child("last_n") + requiredMlLastNFunction(w, &c, x) + } else if n.IsRef("last_n") { + c := n.Child("last_n") + requiredMlLastNFunction(w, &c, nil) + } + if x := v.Max; x != nil { + c := n.Child("max") + requiredMlMaxFunction(w, &c, x) + } else if n.IsRef("max") { + c := n.Child("max") + requiredMlMaxFunction(w, &c, nil) + } + if x := v.Min; x != nil { + c := n.Child("min") + requiredMlMinFunction(w, &c, x) + } else if n.IsRef("min") { + c := n.Child("min") + requiredMlMinFunction(w, &c, nil) + } + if x := v.StddevPop; x != nil { + c := n.Child("stddev_pop") + requiredMlStddevPopFunction(w, &c, x) + } else if n.IsRef("stddev_pop") { + c := n.Child("stddev_pop") + requiredMlStddevPopFunction(w, &c, nil) + } + if x := v.StddevSamp; x != nil { + c := n.Child("stddev_samp") + requiredMlStddevSampFunction(w, &c, x) + } else if n.IsRef("stddev_samp") { + c := n.Child("stddev_samp") + requiredMlStddevSampFunction(w, &c, nil) + } + if x := v.Sum; x != nil { + c := n.Child("sum") + requiredMlSumFunction(w, &c, x) + } else if n.IsRef("sum") { + c := n.Child("sum") + requiredMlSumFunction(w, &c, nil) + } + if x := v.TimeWindow; x != nil { + c := n.Child("time_window") + requiredMlTimeWindow(w, &c, x) + } else if n.IsRef("time_window") { + c := n.Child("time_window") + requiredMlTimeWindow(w, &c, nil) + } + if x := v.VarPop; x != nil { + c := n.Child("var_pop") + requiredMlVarPopFunction(w, &c, x) + } else if n.IsRef("var_pop") { + c := n.Child("var_pop") + requiredMlVarPopFunction(w, &c, nil) + } + if x := v.VarSamp; x != nil { + c := n.Child("var_samp") + requiredMlVarSampFunction(w, &c, x) + } else if n.IsRef("var_samp") { + c := n.Child("var_samp") + requiredMlVarSampFunction(w, &c, nil) + } +} + +func requiredMlColumnSelection(w *walker, n *walk.Node, v *ml.ColumnSelection) { if v == nil { - v = &ml.ColumnSelection{} + w.missing(n, "column") + return } - if !(v.Column != "") && !n.has("column") { + if !(v.Column != "") { w.missing(n, "column") } } -func requiredMlCustomUdf(w *walker, n *node, v *ml.CustomUdf) { +func requiredMlCustomUdf(w *walker, n *walk.Node, v *ml.CustomUdf) { if v == nil { - v = &ml.CustomUdf{} + w.missing(n, "function_path") + return } - if !(v.FunctionPath != "") && !n.has("function_path") { + if !(v.FunctionPath != "") { w.missing(n, "function_path") } if len(v.InputBindings) > 0 { - c := n.child("input_bindings") - for i0 := range v.InputBindings { - e := c.elem(i0) - requiredMlInputBinding(w, &e, &v.InputBindings[i0]) + c := n.Child("input_bindings") + for i := range v.InputBindings { + e := c.Elem(i) + requiredMlInputBinding(w, &e, &v.InputBindings[i]) } } } -func requiredMlFunctionExtraParameter(w *walker, n *node, v *ml.FunctionExtraParameter) { +func requiredMlFunctionExtraParameter(w *walker, n *walk.Node, v *ml.FunctionExtraParameter) { if v == nil { - v = &ml.FunctionExtraParameter{} + w.missing(n, "key") + w.missing(n, "value") + return } - if !(v.Key != "") && !n.has("key") { + if !(v.Key != "") { w.missing(n, "key") } - if !(v.Value != "") && !n.has("value") { + if !(v.Value != "") { w.missing(n, "value") } } -func requiredMlDeltaTableSource(w *walker, n *node, v *ml.DeltaTableSource) { +func requiredMlDeltaTableSource(w *walker, n *walk.Node, v *ml.DeltaTableSource) { if v == nil { - v = &ml.DeltaTableSource{} + w.missing(n, "full_name") + return } - if !(v.FullName != "") && !n.has("full_name") { + if !(v.FullName != "" || walk.Forced(v.ForceSendFields, "FullName")) { w.missing(n, "full_name") } } -func requiredMlFeatureViewSource(w *walker, n *node, v *ml.FeatureViewSource) { +func requiredMlFeatureViewSource(w *walker, n *walk.Node, v *ml.FeatureViewSource) { if v == nil { - v = &ml.FeatureViewSource{} + return } if len(v.FeatureReferences) > 0 { - c := n.child("feature_references") - for i0 := range v.FeatureReferences { - e := c.elem(i0) - requiredMlFeatureReference(w, &e, &v.FeatureReferences[i0]) + c := n.Child("feature_references") + for i := range v.FeatureReferences { + e := c.Elem(i) + requiredMlFeatureReference(w, &e, &v.FeatureReferences[i]) } } } -func requiredMlKafkaSource(w *walker, n *node, v *ml.KafkaSource) { +func requiredMlKafkaSource(w *walker, n *walk.Node, v *ml.KafkaSource) { if v == nil { - v = &ml.KafkaSource{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { w.missing(n, "name") } if len(v.EntityColumnIdentifiers) > 0 { - c := n.child("entity_column_identifiers") - for i0 := range v.EntityColumnIdentifiers { - e := c.elem(i0) - requiredMlColumnIdentifier(w, &e, &v.EntityColumnIdentifiers[i0]) + c := n.Child("entity_column_identifiers") + for i := range v.EntityColumnIdentifiers { + e := c.Elem(i) + requiredMlColumnIdentifier(w, &e, &v.EntityColumnIdentifiers[i]) } } - if c := n.child("timeseries_column_identifier"); c.exists() { - requiredMlColumnIdentifier2(w, &c, v.TimeseriesColumnIdentifier) + if x := v.TimeseriesColumnIdentifier; x != nil { + c := n.Child("timeseries_column_identifier") + requiredMlColumnIdentifier2(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "TimeseriesColumnIdentifier") || n.IsRef("timeseries_column_identifier") { + c := n.Child("timeseries_column_identifier") + requiredMlColumnIdentifier2(w, &c, nil) } } -func requiredMlRequestSource(w *walker, n *node, v *ml.RequestSource) { +func requiredMlRequestSource(w *walker, n *walk.Node, v *ml.RequestSource) { if v == nil { - v = &ml.RequestSource{} + return } - if c := n.child("flat_schema"); c.exists() { - requiredMlFlatSchema(w, &c, v.FlatSchema) + if x := v.FlatSchema; x != nil { + c := n.Child("flat_schema") + requiredMlFlatSchema(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "FlatSchema") || n.IsRef("flat_schema") { + c := n.Child("flat_schema") + requiredMlFlatSchema(w, &c, nil) } } -func requiredMlStreamSource(w *walker, n *node, v *ml.StreamSource) { +func requiredMlStreamSource(w *walker, n *walk.Node, v *ml.StreamSource) { if v == nil { - v = &ml.StreamSource{} + w.missing(n, "full_name") + return } - if !(v.FullName != "") && !n.has("full_name") { + if !(v.FullName != "" || walk.Forced(v.ForceSendFields, "FullName")) { w.missing(n, "full_name") } } -func requiredMlContinuousWindow(w *walker, n *node, v *ml.ContinuousWindow) { +func requiredMlContinuousWindow(w *walker, n *walk.Node, v *ml.ContinuousWindow) { if v == nil { - v = &ml.ContinuousWindow{} + w.missing(n, "window_duration") + return } - if !(v.WindowDuration != "") && !n.has("window_duration") { + if !(v.WindowDuration != "" || walk.Forced(v.ForceSendFields, "WindowDuration")) { w.missing(n, "window_duration") } } -func requiredMlSlidingWindow(w *walker, n *node, v *ml.SlidingWindow) { +func requiredMlSlidingWindow(w *walker, n *walk.Node, v *ml.SlidingWindow) { if v == nil { - v = &ml.SlidingWindow{} + w.missing(n, "slide_duration") + return } - if !(v.SlideDuration != "") && !n.has("slide_duration") { + if !(v.SlideDuration != "" || walk.Forced(v.ForceSendFields, "SlideDuration")) { w.missing(n, "slide_duration") } } -func requiredMlTumblingWindow(w *walker, n *node, v *ml.TumblingWindow) { +func requiredMlTumblingWindow(w *walker, n *walk.Node, v *ml.TumblingWindow) { if v == nil { - v = &ml.TumblingWindow{} + w.missing(n, "window_duration") + return } - if !(v.WindowDuration != "") && !n.has("window_duration") { + if !(v.WindowDuration != "" || walk.Forced(v.ForceSendFields, "WindowDuration")) { w.missing(n, "window_duration") } } -func requiredJobsMaintenanceWindow(w *walker, n *node, v *jobs.MaintenanceWindow) { +func requiredJobsMaintenanceWindow(w *walker, n *walk.Node, v *jobs.MaintenanceWindow) { if v == nil { - v = &jobs.MaintenanceWindow{} + w.missing(n, "day_of_week") + w.missing(n, "start_hour") + w.missing(n, "timezone_id") + return } - if !(v.DayOfWeek != "") && !n.has("day_of_week") { + if !(v.DayOfWeek != "") { w.missing(n, "day_of_week") } - if !(v.StartHour != 0) && !n.has("start_hour") { + if !(v.StartHour != 0 || n.IsRef("start_hour")) { w.missing(n, "start_hour") } - if !(v.TimezoneId != "") && !n.has("timezone_id") { + if !(v.TimezoneId != "") { w.missing(n, "timezone_id") } } -func requiredJobsJobSource(w *walker, n *node, v *jobs.JobSource) { +func requiredJobsJobSource(w *walker, n *walk.Node, v *jobs.JobSource) { if v == nil { - v = &jobs.JobSource{} + w.missing(n, "import_from_git_branch") + w.missing(n, "job_config_path") + return } - if !(v.ImportFromGitBranch != "") && !n.has("import_from_git_branch") { + if !(v.ImportFromGitBranch != "") { w.missing(n, "import_from_git_branch") } - if !(v.JobConfigPath != "") && !n.has("job_config_path") { + if !(v.JobConfigPath != "") { w.missing(n, "job_config_path") } } -func requiredJobsJobsHealthRule(w *walker, n *node, v *jobs.JobsHealthRule) { +func requiredJobsJobsHealthRule(w *walker, n *walk.Node, v *jobs.JobsHealthRule) { if v == nil { - v = &jobs.JobsHealthRule{} + w.missing(n, "metric") + w.missing(n, "op") + w.missing(n, "value") + return } - if !(v.Metric != "") && !n.has("metric") { + if !(v.Metric != "") { w.missing(n, "metric") } - if !(v.Op != "") && !n.has("op") { + if !(v.Op != "") { w.missing(n, "op") } - if !(v.Value != 0) && !n.has("value") { + if !(v.Value != 0 || n.IsRef("value")) { w.missing(n, "value") } } -func requiredComputeClusterSpec(w *walker, n *node, v *compute.ClusterSpec) { +func requiredComputeClusterSpec(w *walker, n *walk.Node, v *compute.ClusterSpec) { if v == nil { - v = &compute.ClusterSpec{} + return } - if c := n.child("cluster_log_conf"); c.exists() { - requiredComputeClusterLogConf(w, &c, v.ClusterLogConf) + if x := v.ClusterLogConf; x != nil { + c := n.Child("cluster_log_conf") + requiredComputeClusterLogConf(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ClusterLogConf") || n.IsRef("cluster_log_conf") { + c := n.Child("cluster_log_conf") + requiredComputeClusterLogConf(w, &c, nil) } if len(v.InitScripts) > 0 { - c := n.child("init_scripts") - for i0 := range v.InitScripts { - e := c.elem(i0) - requiredComputeInitScriptInfo(w, &e, &v.InitScripts[i0]) + c := n.Child("init_scripts") + for i := range v.InitScripts { + e := c.Elem(i) + requiredComputeInitScriptInfo(w, &e, &v.InitScripts[i]) } } - if c := n.child("workload_type"); c.exists() { - requiredComputeWorkloadType(w, &c, v.WorkloadType) + if x := v.WorkloadType; x != nil { + c := n.Child("workload_type") + requiredComputeWorkloadType(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "WorkloadType") || n.IsRef("workload_type") { + c := n.Child("workload_type") + requiredComputeWorkloadType(w, &c, nil) } } -func requiredJobsSqlConditionConfiguration(w *walker, n *node, v *jobs.SqlConditionConfiguration) { +func requiredJobsSqlConditionConfiguration(w *walker, n *walk.Node, v *jobs.SqlConditionConfiguration) { if v == nil { - v = &jobs.SqlConditionConfiguration{} + w.missing(n, "sql_query_id") + w.missing(n, "warehouse_id") + return } - if !(v.SqlQueryId != "") && !n.has("sql_query_id") { + if !(v.SqlQueryId != "") { w.missing(n, "sql_query_id") } - if !(v.WarehouseId != "") && !n.has("warehouse_id") { + if !(v.WarehouseId != "") { w.missing(n, "warehouse_id") } } -func requiredJobsAiRuntimeTask(w *walker, n *node, v *jobs.AiRuntimeTask) { +func requiredJobsAiRuntimeTask(w *walker, n *walk.Node, v *jobs.AiRuntimeTask) { if v == nil { - v = &jobs.AiRuntimeTask{} + w.missing(n, "deployments") + w.missing(n, "experiment") + return } - if !(v.Deployments != nil) && !n.has("deployments") { + if !(v.Deployments != nil || n.IsRef("deployments")) { w.missing(n, "deployments") } - if !(v.Experiment != "") && !n.has("experiment") { + if !(v.Experiment != "" || walk.Forced(v.ForceSendFields, "Experiment")) { w.missing(n, "experiment") } if len(v.Deployments) > 0 { - c := n.child("deployments") - for i0 := range v.Deployments { - e := c.elem(i0) - requiredJobsDeploymentSpec(w, &e, &v.Deployments[i0]) + c := n.Child("deployments") + for i := range v.Deployments { + e := c.Elem(i) + requiredJobsDeploymentSpec(w, &e, &v.Deployments[i]) } } } -func requiredJobsCleanRoomsNotebookTask(w *walker, n *node, v *jobs.CleanRoomsNotebookTask) { +func requiredJobsCleanRoomsNotebookTask(w *walker, n *walk.Node, v *jobs.CleanRoomsNotebookTask) { if v == nil { - v = &jobs.CleanRoomsNotebookTask{} + w.missing(n, "clean_room_name") + w.missing(n, "notebook_name") + return } - if !(v.CleanRoomName != "") && !n.has("clean_room_name") { + if !(v.CleanRoomName != "" || walk.Forced(v.ForceSendFields, "CleanRoomName")) { w.missing(n, "clean_room_name") } - if !(v.NotebookName != "") && !n.has("notebook_name") { + if !(v.NotebookName != "" || walk.Forced(v.ForceSendFields, "NotebookName")) { w.missing(n, "notebook_name") } } -func requiredJobsConditionTask(w *walker, n *node, v *jobs.ConditionTask) { +func requiredJobsConditionTask(w *walker, n *walk.Node, v *jobs.ConditionTask) { if v == nil { - v = &jobs.ConditionTask{} + w.missing(n, "left") + w.missing(n, "op") + w.missing(n, "right") + return } - if !(v.Left != "") && !n.has("left") { + if !(v.Left != "") { w.missing(n, "left") } - if !(v.Op != "") && !n.has("op") { + if !(v.Op != "") { w.missing(n, "op") } - if !(v.Right != "") && !n.has("right") { + if !(v.Right != "") { w.missing(n, "right") } } -func requiredJobsDashboardTask(w *walker, n *node, v *jobs.DashboardTask) { +func requiredJobsDashboardTask(w *walker, n *walk.Node, v *jobs.DashboardTask) { if v == nil { - v = &jobs.DashboardTask{} + return } - if c := n.child("subscription"); c.exists() { - requiredJobsSubscription(w, &c, v.Subscription) + if x := v.Subscription; x != nil { + c := n.Child("subscription") + requiredJobsSubscription(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Subscription") || n.IsRef("subscription") { + c := n.Child("subscription") + requiredJobsSubscription(w, &c, nil) } } -func requiredJobsDbtTask(w *walker, n *node, v *jobs.DbtTask) { +func requiredJobsDbtTask(w *walker, n *walk.Node, v *jobs.DbtTask) { if v == nil { - v = &jobs.DbtTask{} + w.missing(n, "commands") + return } - if !(v.Commands != nil) && !n.has("commands") { + if !(v.Commands != nil || n.IsRef("commands")) { w.missing(n, "commands") } } -func requiredJobsTaskDependency(w *walker, n *node, v *jobs.TaskDependency) { +func requiredJobsTaskDependency(w *walker, n *walk.Node, v *jobs.TaskDependency) { if v == nil { - v = &jobs.TaskDependency{} + w.missing(n, "task_key") + return } - if !(v.TaskKey != "") && !n.has("task_key") { + if !(v.TaskKey != "" || walk.Forced(v.ForceSendFields, "TaskKey")) { w.missing(n, "task_key") } } -func requiredJobsForEachTask(w *walker, n *node, v *jobs.ForEachTask) { +func requiredJobsForEachTask(w *walker, n *walk.Node, v *jobs.ForEachTask) { if v == nil { - v = &jobs.ForEachTask{} + w.missing(n, "inputs") + w.missing(n, "task") + return } - if !(v.Inputs != "") && !n.has("inputs") { + if !(v.Inputs != "" || walk.Forced(v.ForceSendFields, "Inputs")) { w.missing(n, "inputs") } - if !n.has("task") { + if !(!walk.IsZeroStruct(&v.Task) || walk.Forced(v.ForceSendFields, "Task") || n.HasRefs("task")) { w.missing(n, "task") } - if c := n.child("task"); c.exists() { - requiredJobsTask2(w, &c, &v.Task) + if x := &v.Task; !walk.IsZeroStruct(x) || walk.Forced(v.ForceSendFields, "Task") || n.HasRefs("task") { + c := n.Child("task") + requiredJobsTask2(w, &c, x) } } -func requiredJobsGenAiComputeTask(w *walker, n *node, v *jobs.GenAiComputeTask) { +func requiredJobsGenAiComputeTask(w *walker, n *walk.Node, v *jobs.GenAiComputeTask) { if v == nil { - v = &jobs.GenAiComputeTask{} + w.missing(n, "dl_runtime_image") + return } - if !(v.DlRuntimeImage != "") && !n.has("dl_runtime_image") { + if !(v.DlRuntimeImage != "" || walk.Forced(v.ForceSendFields, "DlRuntimeImage")) { w.missing(n, "dl_runtime_image") } - if c := n.child("compute"); c.exists() { - requiredJobsComputeConfig(w, &c, v.Compute) + if x := v.Compute; x != nil { + c := n.Child("compute") + requiredJobsComputeConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Compute") || n.IsRef("compute") { + c := n.Child("compute") + requiredJobsComputeConfig(w, &c, nil) } } -func requiredJobsNotebookTask(w *walker, n *node, v *jobs.NotebookTask) { +func requiredJobsNotebookTask(w *walker, n *walk.Node, v *jobs.NotebookTask) { if v == nil { - v = &jobs.NotebookTask{} + w.missing(n, "notebook_path") + return } - if !(v.NotebookPath != "") && !n.has("notebook_path") { + if !(v.NotebookPath != "" || walk.Forced(v.ForceSendFields, "NotebookPath")) { w.missing(n, "notebook_path") } } -func requiredJobsPipelineTask(w *walker, n *node, v *jobs.PipelineTask) { +func requiredJobsPipelineTask(w *walker, n *walk.Node, v *jobs.PipelineTask) { if v == nil { - v = &jobs.PipelineTask{} + w.missing(n, "pipeline_id") + return } - if !(v.PipelineId != "") && !n.has("pipeline_id") { + if !(v.PipelineId != "" || walk.Forced(v.ForceSendFields, "PipelineId")) { w.missing(n, "pipeline_id") } } -func requiredJobsPythonWheelTask(w *walker, n *node, v *jobs.PythonWheelTask) { +func requiredJobsPythonWheelTask(w *walker, n *walk.Node, v *jobs.PythonWheelTask) { if v == nil { - v = &jobs.PythonWheelTask{} + w.missing(n, "entry_point") + w.missing(n, "package_name") + return } - if !(v.EntryPoint != "") && !n.has("entry_point") { + if !(v.EntryPoint != "") { w.missing(n, "entry_point") } - if !(v.PackageName != "") && !n.has("package_name") { + if !(v.PackageName != "") { w.missing(n, "package_name") } } -func requiredJobsRunJobTask(w *walker, n *node, v *jobs.RunJobTask) { +func requiredJobsRunJobTask(w *walker, n *walk.Node, v *jobs.RunJobTask) { if v == nil { - v = &jobs.RunJobTask{} + w.missing(n, "job_id") + return } - if !(v.JobId != 0) && !n.has("job_id") { + if !(v.JobId != 0 || n.IsRef("job_id")) { w.missing(n, "job_id") } } -func requiredJobsSparkPythonTask(w *walker, n *node, v *jobs.SparkPythonTask) { +func requiredJobsSparkPythonTask(w *walker, n *walk.Node, v *jobs.SparkPythonTask) { if v == nil { - v = &jobs.SparkPythonTask{} + w.missing(n, "python_file") + return } - if !(v.PythonFile != "") && !n.has("python_file") { + if !(v.PythonFile != "") { w.missing(n, "python_file") } } -func requiredJobsSqlTask(w *walker, n *node, v *jobs.SqlTask) { +func requiredJobsSqlTask(w *walker, n *walk.Node, v *jobs.SqlTask) { if v == nil { - v = &jobs.SqlTask{} + w.missing(n, "warehouse_id") + return } - if !(v.WarehouseId != "") && !n.has("warehouse_id") { + if !(v.WarehouseId != "") { w.missing(n, "warehouse_id") } - if c := n.child("alert"); c.exists() { - requiredJobsSqlTaskAlert(w, &c, v.Alert) + if x := v.Alert; x != nil { + c := n.Child("alert") + requiredJobsSqlTaskAlert(w, &c, x) + } else if n.IsRef("alert") { + c := n.Child("alert") + requiredJobsSqlTaskAlert(w, &c, nil) } - if c := n.child("dashboard"); c.exists() { - requiredJobsSqlTaskDashboard(w, &c, v.Dashboard) + if x := v.Dashboard; x != nil { + c := n.Child("dashboard") + requiredJobsSqlTaskDashboard(w, &c, x) + } else if n.IsRef("dashboard") { + c := n.Child("dashboard") + requiredJobsSqlTaskDashboard(w, &c, nil) } - if c := n.child("file"); c.exists() { - requiredJobsSqlTaskFile(w, &c, v.File) + if x := v.File; x != nil { + c := n.Child("file") + requiredJobsSqlTaskFile(w, &c, x) + } else if n.IsRef("file") { + c := n.Child("file") + requiredJobsSqlTaskFile(w, &c, nil) } - if c := n.child("query"); c.exists() { - requiredJobsSqlTaskQuery(w, &c, v.Query) + if x := v.Query; x != nil { + c := n.Child("query") + requiredJobsSqlTaskQuery(w, &c, x) + } else if n.IsRef("query") { + c := n.Child("query") + requiredJobsSqlTaskQuery(w, &c, nil) } } -func requiredJobsFileArrivalTriggerConfiguration(w *walker, n *node, v *jobs.FileArrivalTriggerConfiguration) { +func requiredJobsFileArrivalTriggerConfiguration(w *walker, n *walk.Node, v *jobs.FileArrivalTriggerConfiguration) { if v == nil { - v = &jobs.FileArrivalTriggerConfiguration{} + w.missing(n, "url") + return } - if !(v.Url != "") && !n.has("url") { + if !(v.Url != "" || walk.Forced(v.ForceSendFields, "Url")) { w.missing(n, "url") } } -func requiredJobsModelTriggerConfiguration(w *walker, n *node, v *jobs.ModelTriggerConfiguration) { +func requiredJobsModelTriggerConfiguration(w *walker, n *walk.Node, v *jobs.ModelTriggerConfiguration) { if v == nil { - v = &jobs.ModelTriggerConfiguration{} + w.missing(n, "condition") + return } - if !(v.Condition != "") && !n.has("condition") { + if !(v.Condition != "" || walk.Forced(v.ForceSendFields, "Condition")) { w.missing(n, "condition") } } -func requiredJobsPeriodicTriggerConfiguration(w *walker, n *node, v *jobs.PeriodicTriggerConfiguration) { +func requiredJobsPeriodicTriggerConfiguration(w *walker, n *walk.Node, v *jobs.PeriodicTriggerConfiguration) { if v == nil { - v = &jobs.PeriodicTriggerConfiguration{} + w.missing(n, "interval") + w.missing(n, "unit") + return } - if !(v.Interval != 0) && !n.has("interval") { + if !(v.Interval != 0 || n.IsRef("interval")) { w.missing(n, "interval") } - if !(v.Unit != "") && !n.has("unit") { + if !(v.Unit != "") { w.missing(n, "unit") } } -func requiredJobsTableUpdateTriggerConfiguration(w *walker, n *node, v *jobs.TableUpdateTriggerConfiguration) { +func requiredJobsTableUpdateTriggerConfiguration(w *walker, n *walk.Node, v *jobs.TableUpdateTriggerConfiguration) { if v == nil { - v = &jobs.TableUpdateTriggerConfiguration{} + w.missing(n, "table_names") + return } - if !(v.TableNames != nil) && !n.has("table_names") { + if !(v.TableNames != nil || n.IsRef("table_names")) { w.missing(n, "table_names") } } -func requiredJobsContinuousTriggerConfiguration(w *walker, n *node, v *jobs.ContinuousTriggerConfiguration) { +func requiredJobsContinuousTriggerConfiguration(w *walker, n *walk.Node, v *jobs.ContinuousTriggerConfiguration) { if v == nil { - v = &jobs.ContinuousTriggerConfiguration{} + return } - if c := n.child("maintenance_window"); c.exists() { - requiredJobsMaintenanceWindow(w, &c, v.MaintenanceWindow) + if x := v.MaintenanceWindow; x != nil { + c := n.Child("maintenance_window") + requiredJobsMaintenanceWindow(w, &c, x) + } else if n.IsRef("maintenance_window") { + c := n.Child("maintenance_window") + requiredJobsMaintenanceWindow(w, &c, nil) } } -func requiredJobsCronTriggerConfiguration(w *walker, n *node, v *jobs.CronTriggerConfiguration) { +func requiredJobsCronTriggerConfiguration(w *walker, n *walk.Node, v *jobs.CronTriggerConfiguration) { if v == nil { - v = &jobs.CronTriggerConfiguration{} + w.missing(n, "quartz_cron_expression") + w.missing(n, "timezone_id") + return } - if !(v.QuartzCronExpression != "") && !n.has("quartz_cron_expression") { + if !(v.QuartzCronExpression != "") { w.missing(n, "quartz_cron_expression") } - if !(v.TimezoneId != "") && !n.has("timezone_id") { + if !(v.TimezoneId != "") { w.missing(n, "timezone_id") } } -func requiredJobsWebhook(w *walker, n *node, v *jobs.Webhook) { +func requiredJobsWebhook(w *walker, n *walk.Node, v *jobs.Webhook) { if v == nil { - v = &jobs.Webhook{} + w.missing(n, "id") + return } - if !(v.Id != "") && !n.has("id") { + if !(v.Id != "") { w.missing(n, "id") } } -func requiredCatalogRateLimit(w *walker, n *node, v *catalog.RateLimit) { +func requiredCatalogRateLimit(w *walker, n *walk.Node, v *catalog.RateLimit) { if v == nil { - v = &catalog.RateLimit{} + w.missing(n, "key") + w.missing(n, "renewal_period") + return } - if !(v.Key != "") && !n.has("key") { + if !(v.Key != "" || walk.Forced(v.ForceSendFields, "Key")) { w.missing(n, "key") } - if !(v.RenewalPeriod != "") && !n.has("renewal_period") { + if !(v.RenewalPeriod != "" || walk.Forced(v.ForceSendFields, "RenewalPeriod")) { w.missing(n, "renewal_period") } } -func requiredCatalogMcpServiceConfigSourceConnection(w *walker, n *node, v *catalog.McpServiceConfigSourceConnection) { +func requiredCatalogMcpServiceConfigSourceConnection(w *walker, n *walk.Node, v *catalog.McpServiceConfigSourceConnection) { if v == nil { - v = &catalog.McpServiceConfigSourceConnection{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { w.missing(n, "name") } } -func requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig) { +func requiredCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigAnthropicProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigAnthropicProviderConfig) { +func requiredCatalogModelProviderServiceConfigAnthropicProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAnthropicProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigAnthropicProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig) { +func requiredCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigCustomProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigCustomProviderConfig) { +func requiredCatalogModelProviderServiceConfigCustomProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigCustomProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigCustomProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigCustomProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigCustomProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigCustomProviderDirectConfig(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig) { +func requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(w, &c, nil) } } -func requiredCatalogInferenceTableConfig(w *walker, n *node, v *catalog.InferenceTableConfig) { +func requiredCatalogInferenceTableConfig(w *walker, n *walk.Node, v *catalog.InferenceTableConfig) { if v == nil { - v = &catalog.InferenceTableConfig{} + w.missing(n, "parent") + return } - if !(v.Parent != "") && !n.has("parent") { + if !(v.Parent != "" || walk.Forced(v.ForceSendFields, "Parent")) { w.missing(n, "parent") } } -func requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig) { +func requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigOpenAiProviderConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigOpenAiProviderConfig) { +func requiredCatalogModelProviderServiceConfigOpenAiProviderConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigOpenAiProviderConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigOpenAiProviderConfig{} + return } - if c := n.child("direct"); c.exists() { - requiredCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(w, &c, v.Direct) + if x := v.Direct; x != nil { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(w, &c, x) + } else if n.IsRef("direct") { + c := n.Child("direct") + requiredCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigModelTargetConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigModelTargetConfig) { +func requiredCatalogModelProviderServiceConfigModelTargetConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigModelTargetConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigModelTargetConfig{} + w.missing(n, "model") + return } - if !(v.Model != "") && !n.has("model") { + if !(v.Model != "" || walk.Forced(v.ForceSendFields, "Model")) { w.missing(n, "model") } } -func requiredCatalogModelServiceConfigRoutingConfig(w *walker, n *node, v *catalog.ModelServiceConfigRoutingConfig) { +func requiredCatalogModelServiceConfigRoutingConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigRoutingConfig) { if v == nil { - v = &catalog.ModelServiceConfigRoutingConfig{} + return } if len(v.Destinations) > 0 { - c := n.child("destinations") - for i0 := range v.Destinations { - e := c.elem(i0) - requiredCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i0]) + c := n.Child("destinations") + for i := range v.Destinations { + e := c.Elem(i) + requiredCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i]) } } - if c := n.child("fallback"); c.exists() { - requiredCatalogModelServiceConfigFallbackConfig(w, &c, v.Fallback) + if x := v.Fallback; x != nil { + c := n.Child("fallback") + requiredCatalogModelServiceConfigFallbackConfig(w, &c, x) + } else if n.IsRef("fallback") { + c := n.Child("fallback") + requiredCatalogModelServiceConfigFallbackConfig(w, &c, nil) } } -func requiredServingFallbackConfig(w *walker, n *node, v *serving.FallbackConfig) { +func requiredServingFallbackConfig(w *walker, n *walk.Node, v *serving.FallbackConfig) { if v == nil { - v = &serving.FallbackConfig{} + w.missing(n, "enabled") + return } - if !(v.Enabled) && !n.has("enabled") { + if !(v.Enabled || n.IsRef("enabled")) { w.missing(n, "enabled") } } -func requiredServingAiGatewayRateLimit(w *walker, n *node, v *serving.AiGatewayRateLimit) { +func requiredServingAiGatewayRateLimit(w *walker, n *walk.Node, v *serving.AiGatewayRateLimit) { if v == nil { - v = &serving.AiGatewayRateLimit{} + w.missing(n, "renewal_period") + return } - if !(v.RenewalPeriod != "") && !n.has("renewal_period") { + if !(v.RenewalPeriod != "" || walk.Forced(v.ForceSendFields, "RenewalPeriod")) { w.missing(n, "renewal_period") } } -func requiredServingServedEntityInput(w *walker, n *node, v *serving.ServedEntityInput) { +func requiredServingServedEntityInput(w *walker, n *walk.Node, v *serving.ServedEntityInput) { if v == nil { - v = &serving.ServedEntityInput{} + return } - if c := n.child("external_model"); c.exists() { - requiredServingExternalModel(w, &c, v.ExternalModel) + if x := v.ExternalModel; x != nil { + c := n.Child("external_model") + requiredServingExternalModel(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ExternalModel") || n.IsRef("external_model") { + c := n.Child("external_model") + requiredServingExternalModel(w, &c, nil) } } -func requiredServingServedModelInput(w *walker, n *node, v *serving.ServedModelInput) { +func requiredServingServedModelInput(w *walker, n *walk.Node, v *serving.ServedModelInput) { if v == nil { - v = &serving.ServedModelInput{} + w.missing(n, "model_name") + w.missing(n, "model_version") + w.missing(n, "scale_to_zero_enabled") + return } - if !(v.ModelName != "") && !n.has("model_name") { + if !(v.ModelName != "" || walk.Forced(v.ForceSendFields, "ModelName")) { w.missing(n, "model_name") } - if !(v.ModelVersion != "") && !n.has("model_version") { + if !(v.ModelVersion != "" || walk.Forced(v.ForceSendFields, "ModelVersion")) { w.missing(n, "model_version") } - if !(v.ScaleToZeroEnabled) && !n.has("scale_to_zero_enabled") { + if !(v.ScaleToZeroEnabled || walk.Forced(v.ForceSendFields, "ScaleToZeroEnabled") || n.IsRef("scale_to_zero_enabled")) { w.missing(n, "scale_to_zero_enabled") } } -func requiredServingTrafficConfig(w *walker, n *node, v *serving.TrafficConfig) { +func requiredServingTrafficConfig(w *walker, n *walk.Node, v *serving.TrafficConfig) { if v == nil { - v = &serving.TrafficConfig{} + return } if len(v.Routes) > 0 { - c := n.child("routes") - for i0 := range v.Routes { - e := c.elem(i0) - requiredServingRoute(w, &e, &v.Routes[i0]) + c := n.Child("routes") + for i := range v.Routes { + e := c.Elem(i) + requiredServingRoute(w, &e, &v.Routes[i]) } } } -func requiredPipelinesPipelineClusterAutoscale(w *walker, n *node, v *pipelines.PipelineClusterAutoscale) { +func requiredPipelinesPipelineClusterAutoscale(w *walker, n *walk.Node, v *pipelines.PipelineClusterAutoscale) { if v == nil { - v = &pipelines.PipelineClusterAutoscale{} + w.missing(n, "max_workers") + w.missing(n, "min_workers") + return } - if !(v.MaxWorkers != 0) && !n.has("max_workers") { + if !(v.MaxWorkers != 0 || n.IsRef("max_workers")) { w.missing(n, "max_workers") } - if !(v.MinWorkers != 0) && !n.has("min_workers") { + if !(v.MinWorkers != 0 || n.IsRef("min_workers")) { w.missing(n, "min_workers") } } -func requiredPipelinesDataStagingOptions(w *walker, n *node, v *pipelines.DataStagingOptions) { +func requiredPipelinesDataStagingOptions(w *walker, n *walk.Node, v *pipelines.DataStagingOptions) { if v == nil { - v = &pipelines.DataStagingOptions{} + w.missing(n, "catalog_name") + w.missing(n, "schema_name") + return } - if !(v.CatalogName != "") && !n.has("catalog_name") { + if !(v.CatalogName != "" || walk.Forced(v.ForceSendFields, "CatalogName")) { w.missing(n, "catalog_name") } - if !(v.SchemaName != "") && !n.has("schema_name") { + if !(v.SchemaName != "" || walk.Forced(v.ForceSendFields, "SchemaName")) { w.missing(n, "schema_name") } } -func requiredPipelinesOperationTimeWindow(w *walker, n *node, v *pipelines.OperationTimeWindow) { +func requiredPipelinesOperationTimeWindow(w *walker, n *walk.Node, v *pipelines.OperationTimeWindow) { if v == nil { - v = &pipelines.OperationTimeWindow{} + w.missing(n, "start_hour") + return } - if !(v.StartHour != 0) && !n.has("start_hour") { + if !(v.StartHour != 0 || walk.Forced(v.ForceSendFields, "StartHour") || n.IsRef("start_hour")) { w.missing(n, "start_hour") } } -func requiredPipelinesIngestionConfig(w *walker, n *node, v *pipelines.IngestionConfig) { +func requiredPipelinesIngestionConfig(w *walker, n *walk.Node, v *pipelines.IngestionConfig) { if v == nil { - v = &pipelines.IngestionConfig{} + return } - if c := n.child("report"); c.exists() { - requiredPipelinesReportSpec(w, &c, v.Report) + if x := v.Report; x != nil { + c := n.Child("report") + requiredPipelinesReportSpec(w, &c, x) + } else if n.IsRef("report") { + c := n.Child("report") + requiredPipelinesReportSpec(w, &c, nil) } - if c := n.child("schema"); c.exists() { - requiredPipelinesSchemaSpec(w, &c, v.Schema) + if x := v.Schema; x != nil { + c := n.Child("schema") + requiredPipelinesSchemaSpec(w, &c, x) + } else if n.IsRef("schema") { + c := n.Child("schema") + requiredPipelinesSchemaSpec(w, &c, nil) } - if c := n.child("table"); c.exists() { - requiredPipelinesTableSpec(w, &c, v.Table) + if x := v.Table; x != nil { + c := n.Child("table") + requiredPipelinesTableSpec(w, &c, x) + } else if n.IsRef("table") { + c := n.Child("table") + requiredPipelinesTableSpec(w, &c, nil) } } -func requiredPipelinesTableSpecificConfig(w *walker, n *node, v *pipelines.TableSpecificConfig) { +func requiredPipelinesTableSpecificConfig(w *walker, n *walk.Node, v *pipelines.TableSpecificConfig) { if v == nil { - v = &pipelines.TableSpecificConfig{} + return } - if c := n.child("auto_full_refresh_policy"); c.exists() { - requiredPipelinesAutoFullRefreshPolicy(w, &c, v.AutoFullRefreshPolicy) + if x := v.AutoFullRefreshPolicy; x != nil { + c := n.Child("auto_full_refresh_policy") + requiredPipelinesAutoFullRefreshPolicy(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AutoFullRefreshPolicy") || n.IsRef("auto_full_refresh_policy") { + c := n.Child("auto_full_refresh_policy") + requiredPipelinesAutoFullRefreshPolicy(w, &c, nil) } } -func requiredPostgresMonthlySchedule(w *walker, n *node, v *postgres.MonthlySchedule) { +func requiredPostgresMonthlySchedule(w *walker, n *walk.Node, v *postgres.MonthlySchedule) { if v == nil { - v = &postgres.MonthlySchedule{} + w.missing(n, "day") + return } - if !(v.Day != 0) && !n.has("day") { + if !(v.Day != 0 || walk.Forced(v.ForceSendFields, "Day") || n.IsRef("day")) { w.missing(n, "day") } } -func requiredPostgresWeeklySchedule(w *walker, n *node, v *postgres.WeeklySchedule) { +func requiredPostgresWeeklySchedule(w *walker, n *walk.Node, v *postgres.WeeklySchedule) { if v == nil { - v = &postgres.WeeklySchedule{} + w.missing(n, "day_of_week") + return } - if !(v.DayOfWeek != "") && !n.has("day_of_week") { + if !(v.DayOfWeek != "" || walk.Forced(v.ForceSendFields, "DayOfWeek")) { w.missing(n, "day_of_week") } } -func requiredDatabaseSyncedTableSpecTypeOverride(w *walker, n *node, v *database.SyncedTableSpecTypeOverride) { +func requiredDatabaseSyncedTableSpecTypeOverride(w *walker, n *walk.Node, v *database.SyncedTableSpecTypeOverride) { if v == nil { - v = &database.SyncedTableSpecTypeOverride{} + w.missing(n, "column_name") + w.missing(n, "pg_type") + return } - if !(v.ColumnName != "") && !n.has("column_name") { + if !(v.ColumnName != "" || walk.Forced(v.ForceSendFields, "ColumnName")) { w.missing(n, "column_name") } - if !(v.PgType != "") && !n.has("pg_type") { + if !(v.PgType != "" || walk.Forced(v.ForceSendFields, "PgType")) { w.missing(n, "pg_type") } } -func requiredSqlAlertV2OperandColumn2(w *walker, n *node, v *sql.AlertV2OperandColumn) { +func requiredSqlAlertV2OperandColumn2(w *walker, n *walk.Node, v *sql.AlertV2OperandColumn) { if v == nil { - v = &sql.AlertV2OperandColumn{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { w.missing(n, "name") } } -func requiredMlApproxCountDistinctFunction(w *walker, n *node, v *ml.ApproxCountDistinctFunction) { +func requiredMlApproxCountDistinctFunction(w *walker, n *walk.Node, v *ml.ApproxCountDistinctFunction) { if v == nil { - v = &ml.ApproxCountDistinctFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "" || walk.Forced(v.ForceSendFields, "Input")) { w.missing(n, "input") } } -func requiredMlApproxPercentileFunction(w *walker, n *node, v *ml.ApproxPercentileFunction) { +func requiredMlApproxPercentileFunction(w *walker, n *walk.Node, v *ml.ApproxPercentileFunction) { if v == nil { - v = &ml.ApproxPercentileFunction{} + w.missing(n, "input") + w.missing(n, "percentile") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "" || walk.Forced(v.ForceSendFields, "Input")) { w.missing(n, "input") } - if !(v.Percentile != 0) && !n.has("percentile") { + if !(v.Percentile != 0 || walk.Forced(v.ForceSendFields, "Percentile") || n.IsRef("percentile")) { w.missing(n, "percentile") } } -func requiredMlAvgFunction(w *walker, n *node, v *ml.AvgFunction) { +func requiredMlAvgFunction(w *walker, n *walk.Node, v *ml.AvgFunction) { if v == nil { - v = &ml.AvgFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlCountFunction(w *walker, n *node, v *ml.CountFunction) { +func requiredMlCountFunction(w *walker, n *walk.Node, v *ml.CountFunction) { if v == nil { - v = &ml.CountFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlFirstFunction(w *walker, n *node, v *ml.FirstFunction) { +func requiredMlFirstFunction(w *walker, n *walk.Node, v *ml.FirstFunction) { if v == nil { - v = &ml.FirstFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlFirstDistinctFunction(w *walker, n *node, v *ml.FirstDistinctFunction) { +func requiredMlFirstDistinctFunction(w *walker, n *walk.Node, v *ml.FirstDistinctFunction) { if v == nil { - v = &ml.FirstDistinctFunction{} + w.missing(n, "input") + w.missing(n, "n") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } - if !(v.N != 0) && !n.has("n") { + if !(v.N != 0 || n.IsRef("n")) { w.missing(n, "n") } } -func requiredMlFirstNFunction(w *walker, n *node, v *ml.FirstNFunction) { +func requiredMlFirstNFunction(w *walker, n *walk.Node, v *ml.FirstNFunction) { if v == nil { - v = &ml.FirstNFunction{} + w.missing(n, "input") + w.missing(n, "n") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } - if !(v.N != 0) && !n.has("n") { + if !(v.N != 0 || n.IsRef("n")) { w.missing(n, "n") } } -func requiredMlLastFunction(w *walker, n *node, v *ml.LastFunction) { +func requiredMlLastFunction(w *walker, n *walk.Node, v *ml.LastFunction) { if v == nil { - v = &ml.LastFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlLastDistinctFunction(w *walker, n *node, v *ml.LastDistinctFunction) { +func requiredMlLastDistinctFunction(w *walker, n *walk.Node, v *ml.LastDistinctFunction) { if v == nil { - v = &ml.LastDistinctFunction{} + w.missing(n, "input") + w.missing(n, "n") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } - if !(v.N != 0) && !n.has("n") { + if !(v.N != 0 || n.IsRef("n")) { w.missing(n, "n") } } -func requiredMlLastNFunction(w *walker, n *node, v *ml.LastNFunction) { +func requiredMlLastNFunction(w *walker, n *walk.Node, v *ml.LastNFunction) { if v == nil { - v = &ml.LastNFunction{} + w.missing(n, "input") + w.missing(n, "n") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } - if !(v.N != 0) && !n.has("n") { + if !(v.N != 0 || n.IsRef("n")) { w.missing(n, "n") } } -func requiredMlMaxFunction(w *walker, n *node, v *ml.MaxFunction) { +func requiredMlMaxFunction(w *walker, n *walk.Node, v *ml.MaxFunction) { if v == nil { - v = &ml.MaxFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlMinFunction(w *walker, n *node, v *ml.MinFunction) { +func requiredMlMinFunction(w *walker, n *walk.Node, v *ml.MinFunction) { if v == nil { - v = &ml.MinFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlStddevPopFunction(w *walker, n *node, v *ml.StddevPopFunction) { +func requiredMlStddevPopFunction(w *walker, n *walk.Node, v *ml.StddevPopFunction) { if v == nil { - v = &ml.StddevPopFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlStddevSampFunction(w *walker, n *node, v *ml.StddevSampFunction) { +func requiredMlStddevSampFunction(w *walker, n *walk.Node, v *ml.StddevSampFunction) { if v == nil { - v = &ml.StddevSampFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlSumFunction(w *walker, n *node, v *ml.SumFunction) { +func requiredMlSumFunction(w *walker, n *walk.Node, v *ml.SumFunction) { if v == nil { - v = &ml.SumFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlVarPopFunction(w *walker, n *node, v *ml.VarPopFunction) { +func requiredMlVarPopFunction(w *walker, n *walk.Node, v *ml.VarPopFunction) { if v == nil { - v = &ml.VarPopFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlVarSampFunction(w *walker, n *node, v *ml.VarSampFunction) { +func requiredMlVarSampFunction(w *walker, n *walk.Node, v *ml.VarSampFunction) { if v == nil { - v = &ml.VarSampFunction{} + w.missing(n, "input") + return } - if !(v.Input != "") && !n.has("input") { + if !(v.Input != "") { w.missing(n, "input") } } -func requiredMlInputBinding(w *walker, n *node, v *ml.InputBinding) { +func requiredMlInputBinding(w *walker, n *walk.Node, v *ml.InputBinding) { if v == nil { - v = &ml.InputBinding{} + w.missing(n, "column") + w.missing(n, "parameter") + return } - if !(v.Column != "") && !n.has("column") { + if !(v.Column != "") { w.missing(n, "column") } - if !(v.Parameter != "") && !n.has("parameter") { + if !(v.Parameter != "") { w.missing(n, "parameter") } } -func requiredMlFeatureReference(w *walker, n *node, v *ml.FeatureReference) { +func requiredMlFeatureReference(w *walker, n *walk.Node, v *ml.FeatureReference) { if v == nil { - v = &ml.FeatureReference{} + w.missing(n, "feature") + return } - if !(v.Feature != "") && !n.has("feature") { + if !(v.Feature != "") { w.missing(n, "feature") } } -func requiredMlColumnIdentifier(w *walker, n *node, v *ml.ColumnIdentifier) { +func requiredMlColumnIdentifier(w *walker, n *walk.Node, v *ml.ColumnIdentifier) { if v == nil { - v = &ml.ColumnIdentifier{} + w.missing(n, "variant_expr_path") + return } - if !(v.VariantExprPath != "") && !n.has("variant_expr_path") { + if !(v.VariantExprPath != "") { w.missing(n, "variant_expr_path") } } -func requiredMlColumnIdentifier2(w *walker, n *node, v *ml.ColumnIdentifier) { +func requiredMlColumnIdentifier2(w *walker, n *walk.Node, v *ml.ColumnIdentifier) { if v == nil { - v = &ml.ColumnIdentifier{} + w.missing(n, "variant_expr_path") + return } - if !(v.VariantExprPath != "") && !n.has("variant_expr_path") { + if !(v.VariantExprPath != "") { w.missing(n, "variant_expr_path") } } -func requiredMlFlatSchema(w *walker, n *node, v *ml.FlatSchema) { +func requiredMlFlatSchema(w *walker, n *walk.Node, v *ml.FlatSchema) { if v == nil { - v = &ml.FlatSchema{} + w.missing(n, "fields") + return } - if !(v.Fields != nil) && !n.has("fields") { + if !(v.Fields != nil || n.IsRef("fields")) { w.missing(n, "fields") } if len(v.Fields) > 0 { - c := n.child("fields") - for i0 := range v.Fields { - e := c.elem(i0) - requiredMlFieldDefinition(w, &e, &v.Fields[i0]) + c := n.Child("fields") + for i := range v.Fields { + e := c.Elem(i) + requiredMlFieldDefinition(w, &e, &v.Fields[i]) } } } -func requiredJobsDeploymentSpec(w *walker, n *node, v *jobs.DeploymentSpec) { +func requiredJobsDeploymentSpec(w *walker, n *walk.Node, v *jobs.DeploymentSpec) { if v == nil { - v = &jobs.DeploymentSpec{} + w.missing(n, "compute") + return } - if !n.has("compute") { + if !(!walk.IsZeroStruct(&v.Compute) || walk.Forced(v.ForceSendFields, "Compute") || n.HasRefs("compute")) { w.missing(n, "compute") } - if c := n.child("compute"); c.exists() { - requiredJobsComputeSpec(w, &c, &v.Compute) + if x := &v.Compute; !walk.IsZeroStruct(x) || walk.Forced(v.ForceSendFields, "Compute") || n.HasRefs("compute") { + c := n.Child("compute") + requiredJobsComputeSpec(w, &c, x) } } -func requiredJobsSubscription(w *walker, n *node, v *jobs.Subscription) { +func requiredJobsSubscription(w *walker, n *walk.Node, v *jobs.Subscription) { if v == nil { - v = &jobs.Subscription{} + return } - if c := n.child("attachment"); c.exists() { - requiredJobsSubscriptionAttachment(w, &c, v.Attachment) + if x := v.Attachment; x != nil { + c := n.Child("attachment") + requiredJobsSubscriptionAttachment(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Attachment") || n.IsRef("attachment") { + c := n.Child("attachment") + requiredJobsSubscriptionAttachment(w, &c, nil) } } -func requiredJobsTask2(w *walker, n *node, v *jobs.Task) { +func requiredJobsTask2(w *walker, n *walk.Node, v *jobs.Task) { if v == nil { - v = &jobs.Task{} - } - if !(v.TaskKey != "") && !n.has("task_key") { w.missing(n, "task_key") + return } - if c := n.child("ai_runtime_task"); c.exists() { - requiredJobsAiRuntimeTask(w, &c, v.AiRuntimeTask) - } - if c := n.child("clean_rooms_notebook_task"); c.exists() { - requiredJobsCleanRoomsNotebookTask(w, &c, v.CleanRoomsNotebookTask) - } - if c := n.child("condition_task"); c.exists() { - requiredJobsConditionTask(w, &c, v.ConditionTask) - } - if c := n.child("dashboard_task"); c.exists() { - requiredJobsDashboardTask(w, &c, v.DashboardTask) + if !(v.TaskKey != "" || walk.Forced(v.ForceSendFields, "TaskKey")) { + w.missing(n, "task_key") } - if c := n.child("dbt_task"); c.exists() { - requiredJobsDbtTask(w, &c, v.DbtTask) + if x := v.AiRuntimeTask; x != nil { + c := n.Child("ai_runtime_task") + requiredJobsAiRuntimeTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AiRuntimeTask") || n.IsRef("ai_runtime_task") { + c := n.Child("ai_runtime_task") + requiredJobsAiRuntimeTask(w, &c, nil) + } + if x := v.CleanRoomsNotebookTask; x != nil { + c := n.Child("clean_rooms_notebook_task") + requiredJobsCleanRoomsNotebookTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "CleanRoomsNotebookTask") || n.IsRef("clean_rooms_notebook_task") { + c := n.Child("clean_rooms_notebook_task") + requiredJobsCleanRoomsNotebookTask(w, &c, nil) + } + if x := v.ConditionTask; x != nil { + c := n.Child("condition_task") + requiredJobsConditionTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ConditionTask") || n.IsRef("condition_task") { + c := n.Child("condition_task") + requiredJobsConditionTask(w, &c, nil) + } + if x := v.DashboardTask; x != nil { + c := n.Child("dashboard_task") + requiredJobsDashboardTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "DashboardTask") || n.IsRef("dashboard_task") { + c := n.Child("dashboard_task") + requiredJobsDashboardTask(w, &c, nil) + } + if x := v.DbtTask; x != nil { + c := n.Child("dbt_task") + requiredJobsDbtTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "DbtTask") || n.IsRef("dbt_task") { + c := n.Child("dbt_task") + requiredJobsDbtTask(w, &c, nil) } if len(v.DependsOn) > 0 { - c := n.child("depends_on") - for i0 := range v.DependsOn { - e := c.elem(i0) - requiredJobsTaskDependency(w, &e, &v.DependsOn[i0]) + c := n.Child("depends_on") + for i := range v.DependsOn { + e := c.Elem(i) + requiredJobsTaskDependency(w, &e, &v.DependsOn[i]) } } - if c := n.child("for_each_task"); c.exists() { - requiredJobsForEachTask2(w, &c, v.ForEachTask) - } - if c := n.child("gen_ai_compute_task"); c.exists() { - requiredJobsGenAiComputeTask(w, &c, v.GenAiComputeTask) - } - if c := n.child("health"); c.exists() { - requiredJobsJobsHealthRules(w, &c, v.Health) + if x := v.ForEachTask; x != nil { + c := n.Child("for_each_task") + requiredJobsForEachTask2(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ForEachTask") || n.IsRef("for_each_task") { + c := n.Child("for_each_task") + requiredJobsForEachTask2(w, &c, nil) + } + if x := v.GenAiComputeTask; x != nil { + c := n.Child("gen_ai_compute_task") + requiredJobsGenAiComputeTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "GenAiComputeTask") || n.IsRef("gen_ai_compute_task") { + c := n.Child("gen_ai_compute_task") + requiredJobsGenAiComputeTask(w, &c, nil) + } + if x := v.Health; x != nil { + c := n.Child("health") + requiredJobsJobsHealthRules(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "Health") || n.IsRef("health") { + c := n.Child("health") + requiredJobsJobsHealthRules(w, &c, nil) } if len(v.Libraries) > 0 { - c := n.child("libraries") - for i0 := range v.Libraries { - e := c.elem(i0) - requiredComputeLibrary(w, &e, &v.Libraries[i0]) + c := n.Child("libraries") + for i := range v.Libraries { + e := c.Elem(i) + requiredComputeLibrary(w, &e, &v.Libraries[i]) } } - if c := n.child("new_cluster"); c.exists() { - requiredComputeClusterSpec(w, &c, v.NewCluster) - } - if c := n.child("notebook_task"); c.exists() { - requiredJobsNotebookTask(w, &c, v.NotebookTask) - } - if c := n.child("pipeline_task"); c.exists() { - requiredJobsPipelineTask(w, &c, v.PipelineTask) - } - if c := n.child("python_wheel_task"); c.exists() { - requiredJobsPythonWheelTask(w, &c, v.PythonWheelTask) - } - if c := n.child("run_job_task"); c.exists() { - requiredJobsRunJobTask(w, &c, v.RunJobTask) - } - if c := n.child("spark_python_task"); c.exists() { - requiredJobsSparkPythonTask(w, &c, v.SparkPythonTask) - } - if c := n.child("sql_task"); c.exists() { - requiredJobsSqlTask(w, &c, v.SqlTask) - } - if c := n.child("webhook_notifications"); c.exists() { - requiredJobsWebhookNotifications(w, &c, v.WebhookNotifications) - } -} - -func requiredJobsComputeConfig(w *walker, n *node, v *jobs.ComputeConfig) { + if x := v.NewCluster; x != nil { + c := n.Child("new_cluster") + requiredComputeClusterSpec(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "NewCluster") || n.IsRef("new_cluster") { + c := n.Child("new_cluster") + requiredComputeClusterSpec(w, &c, nil) + } + if x := v.NotebookTask; x != nil { + c := n.Child("notebook_task") + requiredJobsNotebookTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "NotebookTask") || n.IsRef("notebook_task") { + c := n.Child("notebook_task") + requiredJobsNotebookTask(w, &c, nil) + } + if x := v.PipelineTask; x != nil { + c := n.Child("pipeline_task") + requiredJobsPipelineTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "PipelineTask") || n.IsRef("pipeline_task") { + c := n.Child("pipeline_task") + requiredJobsPipelineTask(w, &c, nil) + } + if x := v.PythonWheelTask; x != nil { + c := n.Child("python_wheel_task") + requiredJobsPythonWheelTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "PythonWheelTask") || n.IsRef("python_wheel_task") { + c := n.Child("python_wheel_task") + requiredJobsPythonWheelTask(w, &c, nil) + } + if x := v.RunJobTask; x != nil { + c := n.Child("run_job_task") + requiredJobsRunJobTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "RunJobTask") || n.IsRef("run_job_task") { + c := n.Child("run_job_task") + requiredJobsRunJobTask(w, &c, nil) + } + if x := v.SparkPythonTask; x != nil { + c := n.Child("spark_python_task") + requiredJobsSparkPythonTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SparkPythonTask") || n.IsRef("spark_python_task") { + c := n.Child("spark_python_task") + requiredJobsSparkPythonTask(w, &c, nil) + } + if x := v.SqlTask; x != nil { + c := n.Child("sql_task") + requiredJobsSqlTask(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SqlTask") || n.IsRef("sql_task") { + c := n.Child("sql_task") + requiredJobsSqlTask(w, &c, nil) + } + if x := v.WebhookNotifications; x != nil { + c := n.Child("webhook_notifications") + requiredJobsWebhookNotifications(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "WebhookNotifications") || n.IsRef("webhook_notifications") { + c := n.Child("webhook_notifications") + requiredJobsWebhookNotifications(w, &c, nil) + } +} + +func requiredJobsComputeConfig(w *walker, n *walk.Node, v *jobs.ComputeConfig) { if v == nil { - v = &jobs.ComputeConfig{} + w.missing(n, "num_gpus") + return } - if !(v.NumGpus != 0) && !n.has("num_gpus") { + if !(v.NumGpus != 0 || walk.Forced(v.ForceSendFields, "NumGpus") || n.IsRef("num_gpus")) { w.missing(n, "num_gpus") } } -func requiredJobsSqlTaskAlert(w *walker, n *node, v *jobs.SqlTaskAlert) { +func requiredJobsSqlTaskAlert(w *walker, n *walk.Node, v *jobs.SqlTaskAlert) { if v == nil { - v = &jobs.SqlTaskAlert{} + w.missing(n, "alert_id") + return } - if !(v.AlertId != "") && !n.has("alert_id") { + if !(v.AlertId != "" || walk.Forced(v.ForceSendFields, "AlertId")) { w.missing(n, "alert_id") } } -func requiredJobsSqlTaskDashboard(w *walker, n *node, v *jobs.SqlTaskDashboard) { +func requiredJobsSqlTaskDashboard(w *walker, n *walk.Node, v *jobs.SqlTaskDashboard) { if v == nil { - v = &jobs.SqlTaskDashboard{} + w.missing(n, "dashboard_id") + return } - if !(v.DashboardId != "") && !n.has("dashboard_id") { + if !(v.DashboardId != "" || walk.Forced(v.ForceSendFields, "DashboardId")) { w.missing(n, "dashboard_id") } } -func requiredJobsSqlTaskFile(w *walker, n *node, v *jobs.SqlTaskFile) { +func requiredJobsSqlTaskFile(w *walker, n *walk.Node, v *jobs.SqlTaskFile) { if v == nil { - v = &jobs.SqlTaskFile{} + w.missing(n, "path") + return } - if !(v.Path != "") && !n.has("path") { + if !(v.Path != "") { w.missing(n, "path") } } -func requiredJobsSqlTaskQuery(w *walker, n *node, v *jobs.SqlTaskQuery) { +func requiredJobsSqlTaskQuery(w *walker, n *walk.Node, v *jobs.SqlTaskQuery) { if v == nil { - v = &jobs.SqlTaskQuery{} + w.missing(n, "query_id") + return } - if !(v.QueryId != "") && !n.has("query_id") { + if !(v.QueryId != "") { w.missing(n, "query_id") } } -func requiredCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig{} + return } - if c := n.child("aws_access_key"); c.exists() { - requiredCatalogModelProviderServiceConfigAwsAccessKey(w, &c, v.AwsAccessKey) + if x := v.AwsAccessKey; x != nil { + c := n.Child("aws_access_key") + requiredCatalogModelProviderServiceConfigAwsAccessKey(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "AwsAccessKey") || n.IsRef("aws_access_key") { + c := n.Child("aws_access_key") + requiredCatalogModelProviderServiceConfigAwsAccessKey(w, &c, nil) } - if c := n.child("service_credential"); c.exists() { - requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, v.ServiceCredential) + if x := v.ServiceCredential; x != nil { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ServiceCredential") || n.IsRef("service_credential") { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig{} + return } - if c := n.child("api_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKey) + if x := v.ApiKey; x != nil { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if n.IsRef("api_key") { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig{} + return } - if c := n.child("api_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKey) + if x := v.ApiKey; x != nil { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ApiKey") || n.IsRef("api_key") { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } - if c := n.child("entra_service_principal"); c.exists() { - requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w, &c, v.EntraServicePrincipal) + if x := v.EntraServicePrincipal; x != nil { + c := n.Child("entra_service_principal") + requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "EntraServicePrincipal") || n.IsRef("entra_service_principal") { + c := n.Child("entra_service_principal") + requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w, &c, nil) } - if c := n.child("service_credential"); c.exists() { - requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, v.ServiceCredential) + if x := v.ServiceCredential; x != nil { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ServiceCredential") || n.IsRef("service_credential") { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigCustomProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigCustomProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigCustomProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigCustomProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigCustomProviderDirectConfig{} + return } - if c := n.child("api_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKey) + if x := v.ApiKey; x != nil { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ApiKey") || n.IsRef("api_key") { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } - if c := n.child("header_auth"); c.exists() { - requiredCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(w, &c, v.HeaderAuth) + if x := v.HeaderAuth; x != nil { + c := n.Child("header_auth") + requiredCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "HeaderAuth") || n.IsRef("header_auth") { + c := n.Child("header_auth") + requiredCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig{} + return } - if c := n.child("api_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKey) + if x := v.ApiKey; x != nil { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ApiKey") || n.IsRef("api_key") { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } - if c := n.child("service_credential"); c.exists() { - requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, v.ServiceCredential) + if x := v.ServiceCredential; x != nil { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ServiceCredential") || n.IsRef("service_credential") { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig{} + return } - if c := n.child("api_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKey) + if x := v.ApiKey; x != nil { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ApiKey") || n.IsRef("api_key") { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } - if c := n.child("entra_service_principal"); c.exists() { - requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w, &c, v.EntraServicePrincipal) + if x := v.EntraServicePrincipal; x != nil { + c := n.Child("entra_service_principal") + requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "EntraServicePrincipal") || n.IsRef("entra_service_principal") { + c := n.Child("entra_service_principal") + requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w, &c, nil) } - if c := n.child("service_credential"); c.exists() { - requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, v.ServiceCredential) + if x := v.ServiceCredential; x != nil { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ServiceCredential") || n.IsRef("service_credential") { + c := n.Child("service_credential") + requiredCatalogModelProviderServiceConfigServiceCredential(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(w *walker, n *node, v *catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig) { +func requiredCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig) { if v == nil { - v = &catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig{} + return } - if c := n.child("api_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKey) + if x := v.ApiKey; x != nil { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ApiKey") || n.IsRef("api_key") { + c := n.Child("api_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } } -func requiredCatalogModelServiceConfigDestinationConfig(w *walker, n *node, v *catalog.ModelServiceConfigDestinationConfig) { +func requiredCatalogModelServiceConfigDestinationConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigDestinationConfig) { if v == nil { - v = &catalog.ModelServiceConfigDestinationConfig{} + w.missing(n, "destination_type") + w.missing(n, "name") + return } - if !(v.DestinationType != "") && !n.has("destination_type") { + if !(v.DestinationType != "" || walk.Forced(v.ForceSendFields, "DestinationType")) { w.missing(n, "destination_type") } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "" || walk.Forced(v.ForceSendFields, "Name")) { w.missing(n, "name") } - if c := n.child("external_model_config"); c.exists() { - requiredCatalogModelServiceConfigExternalModelConfig(w, &c, v.ExternalModelConfig) + if x := v.ExternalModelConfig; x != nil { + c := n.Child("external_model_config") + requiredCatalogModelServiceConfigExternalModelConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ExternalModelConfig") || n.IsRef("external_model_config") { + c := n.Child("external_model_config") + requiredCatalogModelServiceConfigExternalModelConfig(w, &c, nil) } - if c := n.child("pay_per_token_config"); c.exists() { - requiredCatalogModelServiceConfigPayPerTokenConfig(w, &c, v.PayPerTokenConfig) + if x := v.PayPerTokenConfig; x != nil { + c := n.Child("pay_per_token_config") + requiredCatalogModelServiceConfigPayPerTokenConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "PayPerTokenConfig") || n.IsRef("pay_per_token_config") { + c := n.Child("pay_per_token_config") + requiredCatalogModelServiceConfigPayPerTokenConfig(w, &c, nil) } - if c := n.child("provisioned_throughput_config"); c.exists() { - requiredCatalogModelServiceConfigProvisionedThroughputConfig(w, &c, v.ProvisionedThroughputConfig) + if x := v.ProvisionedThroughputConfig; x != nil { + c := n.Child("provisioned_throughput_config") + requiredCatalogModelServiceConfigProvisionedThroughputConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ProvisionedThroughputConfig") || n.IsRef("provisioned_throughput_config") { + c := n.Child("provisioned_throughput_config") + requiredCatalogModelServiceConfigProvisionedThroughputConfig(w, &c, nil) } } -func requiredCatalogModelServiceConfigFallbackConfig(w *walker, n *node, v *catalog.ModelServiceConfigFallbackConfig) { +func requiredCatalogModelServiceConfigFallbackConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigFallbackConfig) { if v == nil { - v = &catalog.ModelServiceConfigFallbackConfig{} + return } if len(v.Destinations) > 0 { - c := n.child("destinations") - for i0 := range v.Destinations { - e := c.elem(i0) - requiredCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i0]) + c := n.Child("destinations") + for i := range v.Destinations { + e := c.Elem(i) + requiredCatalogModelServiceConfigDestinationConfig(w, &e, &v.Destinations[i]) } } } -func requiredServingExternalModel(w *walker, n *node, v *serving.ExternalModel) { +func requiredServingExternalModel(w *walker, n *walk.Node, v *serving.ExternalModel) { if v == nil { - v = &serving.ExternalModel{} + w.missing(n, "name") + w.missing(n, "provider") + w.missing(n, "task") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } - if !(v.Provider != "") && !n.has("provider") { + if !(v.Provider != "") { w.missing(n, "provider") } - if !(v.Task != "") && !n.has("task") { + if !(v.Task != "") { w.missing(n, "task") } - if c := n.child("amazon_bedrock_config"); c.exists() { - requiredServingAmazonBedrockConfig(w, &c, v.AmazonBedrockConfig) + if x := v.AmazonBedrockConfig; x != nil { + c := n.Child("amazon_bedrock_config") + requiredServingAmazonBedrockConfig(w, &c, x) + } else if n.IsRef("amazon_bedrock_config") { + c := n.Child("amazon_bedrock_config") + requiredServingAmazonBedrockConfig(w, &c, nil) } - if c := n.child("custom_provider_config"); c.exists() { - requiredServingCustomProviderConfig(w, &c, v.CustomProviderConfig) + if x := v.CustomProviderConfig; x != nil { + c := n.Child("custom_provider_config") + requiredServingCustomProviderConfig(w, &c, x) + } else if n.IsRef("custom_provider_config") { + c := n.Child("custom_provider_config") + requiredServingCustomProviderConfig(w, &c, nil) } - if c := n.child("databricks_model_serving_config"); c.exists() { - requiredServingDatabricksModelServingConfig(w, &c, v.DatabricksModelServingConfig) + if x := v.DatabricksModelServingConfig; x != nil { + c := n.Child("databricks_model_serving_config") + requiredServingDatabricksModelServingConfig(w, &c, x) + } else if n.IsRef("databricks_model_serving_config") { + c := n.Child("databricks_model_serving_config") + requiredServingDatabricksModelServingConfig(w, &c, nil) } - if c := n.child("google_cloud_vertex_ai_config"); c.exists() { - requiredServingGoogleCloudVertexAiConfig(w, &c, v.GoogleCloudVertexAiConfig) + if x := v.GoogleCloudVertexAiConfig; x != nil { + c := n.Child("google_cloud_vertex_ai_config") + requiredServingGoogleCloudVertexAiConfig(w, &c, x) + } else if n.IsRef("google_cloud_vertex_ai_config") { + c := n.Child("google_cloud_vertex_ai_config") + requiredServingGoogleCloudVertexAiConfig(w, &c, nil) } } -func requiredServingRoute(w *walker, n *node, v *serving.Route) { +func requiredServingRoute(w *walker, n *walk.Node, v *serving.Route) { if v == nil { - v = &serving.Route{} + w.missing(n, "traffic_percentage") + return } - if !(v.TrafficPercentage != 0) && !n.has("traffic_percentage") { + if !(v.TrafficPercentage != 0 || walk.Forced(v.ForceSendFields, "TrafficPercentage") || n.IsRef("traffic_percentage")) { w.missing(n, "traffic_percentage") } } -func requiredPipelinesReportSpec(w *walker, n *node, v *pipelines.ReportSpec) { +func requiredPipelinesReportSpec(w *walker, n *walk.Node, v *pipelines.ReportSpec) { if v == nil { - v = &pipelines.ReportSpec{} + w.missing(n, "destination_catalog") + w.missing(n, "destination_schema") + w.missing(n, "source_url") + return } - if !(v.DestinationCatalog != "") && !n.has("destination_catalog") { + if !(v.DestinationCatalog != "" || walk.Forced(v.ForceSendFields, "DestinationCatalog")) { w.missing(n, "destination_catalog") } - if !(v.DestinationSchema != "") && !n.has("destination_schema") { + if !(v.DestinationSchema != "" || walk.Forced(v.ForceSendFields, "DestinationSchema")) { w.missing(n, "destination_schema") } - if !(v.SourceUrl != "") && !n.has("source_url") { + if !(v.SourceUrl != "" || walk.Forced(v.ForceSendFields, "SourceUrl")) { w.missing(n, "source_url") } - if c := n.child("table_configuration"); c.exists() { - requiredPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "TableConfiguration") || n.IsRef("table_configuration") { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, nil) } } -func requiredPipelinesSchemaSpec(w *walker, n *node, v *pipelines.SchemaSpec) { +func requiredPipelinesSchemaSpec(w *walker, n *walk.Node, v *pipelines.SchemaSpec) { if v == nil { - v = &pipelines.SchemaSpec{} + w.missing(n, "destination_catalog") + w.missing(n, "destination_schema") + return } - if !(v.DestinationCatalog != "") && !n.has("destination_catalog") { + if !(v.DestinationCatalog != "" || walk.Forced(v.ForceSendFields, "DestinationCatalog")) { w.missing(n, "destination_catalog") } - if !(v.DestinationSchema != "") && !n.has("destination_schema") { + if !(v.DestinationSchema != "" || walk.Forced(v.ForceSendFields, "DestinationSchema")) { w.missing(n, "destination_schema") } - if c := n.child("connector_options"); c.exists() { - requiredPipelinesConnectorOptions(w, &c, v.ConnectorOptions) + if x := v.ConnectorOptions; x != nil { + c := n.Child("connector_options") + requiredPipelinesConnectorOptions(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ConnectorOptions") || n.IsRef("connector_options") { + c := n.Child("connector_options") + requiredPipelinesConnectorOptions(w, &c, nil) } - if c := n.child("table_configuration"); c.exists() { - requiredPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "TableConfiguration") || n.IsRef("table_configuration") { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, nil) } } -func requiredPipelinesTableSpec(w *walker, n *node, v *pipelines.TableSpec) { +func requiredPipelinesTableSpec(w *walker, n *walk.Node, v *pipelines.TableSpec) { if v == nil { - v = &pipelines.TableSpec{} + w.missing(n, "destination_catalog") + w.missing(n, "destination_schema") + return } - if !(v.DestinationCatalog != "") && !n.has("destination_catalog") { + if !(v.DestinationCatalog != "" || walk.Forced(v.ForceSendFields, "DestinationCatalog")) { w.missing(n, "destination_catalog") } - if !(v.DestinationSchema != "") && !n.has("destination_schema") { + if !(v.DestinationSchema != "" || walk.Forced(v.ForceSendFields, "DestinationSchema")) { w.missing(n, "destination_schema") } - if c := n.child("connector_options"); c.exists() { - requiredPipelinesConnectorOptions(w, &c, v.ConnectorOptions) + if x := v.ConnectorOptions; x != nil { + c := n.Child("connector_options") + requiredPipelinesConnectorOptions(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ConnectorOptions") || n.IsRef("connector_options") { + c := n.Child("connector_options") + requiredPipelinesConnectorOptions(w, &c, nil) } - if c := n.child("table_configuration"); c.exists() { - requiredPipelinesTableSpecificConfig(w, &c, v.TableConfiguration) + if x := v.TableConfiguration; x != nil { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "TableConfiguration") || n.IsRef("table_configuration") { + c := n.Child("table_configuration") + requiredPipelinesTableSpecificConfig(w, &c, nil) } } -func requiredPipelinesAutoFullRefreshPolicy(w *walker, n *node, v *pipelines.AutoFullRefreshPolicy) { +func requiredPipelinesAutoFullRefreshPolicy(w *walker, n *walk.Node, v *pipelines.AutoFullRefreshPolicy) { if v == nil { - v = &pipelines.AutoFullRefreshPolicy{} + w.missing(n, "enabled") + return } - if !(v.Enabled) && !n.has("enabled") { + if !(v.Enabled || walk.Forced(v.ForceSendFields, "Enabled") || n.IsRef("enabled")) { w.missing(n, "enabled") } } -func requiredMlFieldDefinition(w *walker, n *node, v *ml.FieldDefinition) { +func requiredMlFieldDefinition(w *walker, n *walk.Node, v *ml.FieldDefinition) { if v == nil { - v = &ml.FieldDefinition{} + w.missing(n, "data_type") + w.missing(n, "name") + return } - if !(v.DataType != "") && !n.has("data_type") { + if !(v.DataType != "") { w.missing(n, "data_type") } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredJobsComputeSpec(w *walker, n *node, v *jobs.ComputeSpec) { +func requiredJobsComputeSpec(w *walker, n *walk.Node, v *jobs.ComputeSpec) { if v == nil { - v = &jobs.ComputeSpec{} + w.missing(n, "accelerator_count") + w.missing(n, "accelerator_type") + return } - if !(v.AcceleratorCount != 0) && !n.has("accelerator_count") { + if !(v.AcceleratorCount != 0 || walk.Forced(v.ForceSendFields, "AcceleratorCount") || n.IsRef("accelerator_count")) { w.missing(n, "accelerator_count") } - if !(v.AcceleratorType != "") && !n.has("accelerator_type") { + if !(v.AcceleratorType != "" || walk.Forced(v.ForceSendFields, "AcceleratorType")) { w.missing(n, "accelerator_type") } } -func requiredJobsSubscriptionAttachment(w *walker, n *node, v *jobs.SubscriptionAttachment) { +func requiredJobsSubscriptionAttachment(w *walker, n *walk.Node, v *jobs.SubscriptionAttachment) { if v == nil { - v = &jobs.SubscriptionAttachment{} + return } - if c := n.child("data_attachment"); c.exists() { - requiredJobsSubscriptionAttachmentDataAttachment(w, &c, v.DataAttachment) + if x := v.DataAttachment; x != nil { + c := n.Child("data_attachment") + requiredJobsSubscriptionAttachmentDataAttachment(w, &c, x) + } else if n.IsRef("data_attachment") { + c := n.Child("data_attachment") + requiredJobsSubscriptionAttachmentDataAttachment(w, &c, nil) } } -func requiredJobsForEachTask2(w *walker, n *node, v *jobs.ForEachTask) { +func requiredJobsForEachTask2(w *walker, n *walk.Node, v *jobs.ForEachTask) { if v == nil { - v = &jobs.ForEachTask{} + w.missing(n, "inputs") + w.missing(n, "task") + return } - if !(v.Inputs != "") && !n.has("inputs") { + if !(v.Inputs != "" || walk.Forced(v.ForceSendFields, "Inputs")) { w.missing(n, "inputs") } - if !n.has("task") { + if !(!walk.IsZeroStruct(&v.Task) || walk.Forced(v.ForceSendFields, "Task") || n.HasRefs("task")) { w.missing(n, "task") } } -func requiredCatalogModelProviderServiceConfigAwsAccessKey(w *walker, n *node, v *catalog.ModelProviderServiceConfigAwsAccessKey) { +func requiredCatalogModelProviderServiceConfigAwsAccessKey(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigAwsAccessKey) { if v == nil { - v = &catalog.ModelProviderServiceConfigAwsAccessKey{} + return } - if c := n.child("secret_access_key"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.SecretAccessKey) + if x := v.SecretAccessKey; x != nil { + c := n.Child("secret_access_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SecretAccessKey") || n.IsRef("secret_access_key") { + c := n.Child("secret_access_key") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigServiceCredential(w *walker, n *node, v *catalog.ModelProviderServiceConfigServiceCredential) { +func requiredCatalogModelProviderServiceConfigServiceCredential(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigServiceCredential) { if v == nil { - v = &catalog.ModelProviderServiceConfigServiceCredential{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredCatalogModelProviderServiceConfigProviderSecret(w *walker, n *node, v *catalog.ModelProviderServiceConfigProviderSecret) { +func requiredCatalogModelProviderServiceConfigProviderSecret(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigProviderSecret) { if v == nil { - v = &catalog.ModelProviderServiceConfigProviderSecret{} + return } - if c := n.child("secret_reference"); c.exists() { - requiredCatalogModelProviderServiceConfigSecretReference(w, &c, v.SecretReference) + if x := v.SecretReference; x != nil { + c := n.Child("secret_reference") + requiredCatalogModelProviderServiceConfigSecretReference(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "SecretReference") || n.IsRef("secret_reference") { + c := n.Child("secret_reference") + requiredCatalogModelProviderServiceConfigSecretReference(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w *walker, n *node, v *catalog.ModelProviderServiceConfigEntraServicePrincipal) { +func requiredCatalogModelProviderServiceConfigEntraServicePrincipal(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigEntraServicePrincipal) { if v == nil { - v = &catalog.ModelProviderServiceConfigEntraServicePrincipal{} + return } - if c := n.child("client_secret"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ClientSecret) + if x := v.ClientSecret; x != nil { + c := n.Child("client_secret") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ClientSecret") || n.IsRef("client_secret") { + c := n.Child("client_secret") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } } -func requiredCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(w *walker, n *node, v *catalog.ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth) { +func requiredCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth) { if v == nil { - v = &catalog.ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth{} + return } - if c := n.child("api_key_value"); c.exists() { - requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, v.ApiKeyValue) + if x := v.ApiKeyValue; x != nil { + c := n.Child("api_key_value") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "ApiKeyValue") || n.IsRef("api_key_value") { + c := n.Child("api_key_value") + requiredCatalogModelProviderServiceConfigProviderSecret(w, &c, nil) } } -func requiredCatalogModelServiceConfigExternalModelConfig(w *walker, n *node, v *catalog.ModelServiceConfigExternalModelConfig) { +func requiredCatalogModelServiceConfigExternalModelConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigExternalModelConfig) { if v == nil { - v = &catalog.ModelServiceConfigExternalModelConfig{} + w.missing(n, "model_provider_service") + w.missing(n, "target") + return } - if !(v.ModelProviderService != "") && !n.has("model_provider_service") { + if !(v.ModelProviderService != "") { w.missing(n, "model_provider_service") } - if !n.has("target") { + if !(!walk.IsZeroStruct(&v.Target) || n.HasRefs("target")) { w.missing(n, "target") } - if c := n.child("target"); c.exists() { - requiredCatalogModelProviderServiceConfigModelTargetConfig(w, &c, &v.Target) + if x := &v.Target; !walk.IsZeroStruct(x) || n.HasRefs("target") { + c := n.Child("target") + requiredCatalogModelProviderServiceConfigModelTargetConfig(w, &c, x) } } -func requiredCatalogModelServiceConfigPayPerTokenConfig(w *walker, n *node, v *catalog.ModelServiceConfigPayPerTokenConfig) { +func requiredCatalogModelServiceConfigPayPerTokenConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigPayPerTokenConfig) { if v == nil { - v = &catalog.ModelServiceConfigPayPerTokenConfig{} + w.missing(n, "model") + return } - if !(v.Model != "") && !n.has("model") { + if !(v.Model != "") { w.missing(n, "model") } } -func requiredCatalogModelServiceConfigProvisionedThroughputConfig(w *walker, n *node, v *catalog.ModelServiceConfigProvisionedThroughputConfig) { +func requiredCatalogModelServiceConfigProvisionedThroughputConfig(w *walker, n *walk.Node, v *catalog.ModelServiceConfigProvisionedThroughputConfig) { if v == nil { - v = &catalog.ModelServiceConfigProvisionedThroughputConfig{} + w.missing(n, "model_serving_endpoint") + return } - if !(v.ModelServingEndpoint != "") && !n.has("model_serving_endpoint") { + if !(v.ModelServingEndpoint != "" || walk.Forced(v.ForceSendFields, "ModelServingEndpoint")) { w.missing(n, "model_serving_endpoint") } } -func requiredServingAmazonBedrockConfig(w *walker, n *node, v *serving.AmazonBedrockConfig) { +func requiredServingAmazonBedrockConfig(w *walker, n *walk.Node, v *serving.AmazonBedrockConfig) { if v == nil { - v = &serving.AmazonBedrockConfig{} + w.missing(n, "aws_region") + w.missing(n, "bedrock_provider") + return } - if !(v.AwsRegion != "") && !n.has("aws_region") { + if !(v.AwsRegion != "" || walk.Forced(v.ForceSendFields, "AwsRegion")) { w.missing(n, "aws_region") } - if !(v.BedrockProvider != "") && !n.has("bedrock_provider") { + if !(v.BedrockProvider != "" || walk.Forced(v.ForceSendFields, "BedrockProvider")) { w.missing(n, "bedrock_provider") } } -func requiredServingCustomProviderConfig(w *walker, n *node, v *serving.CustomProviderConfig) { +func requiredServingCustomProviderConfig(w *walker, n *walk.Node, v *serving.CustomProviderConfig) { if v == nil { - v = &serving.CustomProviderConfig{} + w.missing(n, "custom_provider_url") + return } - if !(v.CustomProviderUrl != "") && !n.has("custom_provider_url") { + if !(v.CustomProviderUrl != "") { w.missing(n, "custom_provider_url") } - if c := n.child("api_key_auth"); c.exists() { - requiredServingApiKeyAuth(w, &c, v.ApiKeyAuth) + if x := v.ApiKeyAuth; x != nil { + c := n.Child("api_key_auth") + requiredServingApiKeyAuth(w, &c, x) + } else if n.IsRef("api_key_auth") { + c := n.Child("api_key_auth") + requiredServingApiKeyAuth(w, &c, nil) } } -func requiredServingDatabricksModelServingConfig(w *walker, n *node, v *serving.DatabricksModelServingConfig) { +func requiredServingDatabricksModelServingConfig(w *walker, n *walk.Node, v *serving.DatabricksModelServingConfig) { if v == nil { - v = &serving.DatabricksModelServingConfig{} + w.missing(n, "databricks_workspace_url") + return } - if !(v.DatabricksWorkspaceUrl != "") && !n.has("databricks_workspace_url") { + if !(v.DatabricksWorkspaceUrl != "" || walk.Forced(v.ForceSendFields, "DatabricksWorkspaceUrl")) { w.missing(n, "databricks_workspace_url") } } -func requiredServingGoogleCloudVertexAiConfig(w *walker, n *node, v *serving.GoogleCloudVertexAiConfig) { +func requiredServingGoogleCloudVertexAiConfig(w *walker, n *walk.Node, v *serving.GoogleCloudVertexAiConfig) { if v == nil { - v = &serving.GoogleCloudVertexAiConfig{} + w.missing(n, "project_id") + w.missing(n, "region") + return } - if !(v.ProjectId != "") && !n.has("project_id") { + if !(v.ProjectId != "" || walk.Forced(v.ForceSendFields, "ProjectId")) { w.missing(n, "project_id") } - if !(v.Region != "") && !n.has("region") { + if !(v.Region != "" || walk.Forced(v.ForceSendFields, "Region")) { w.missing(n, "region") } } -func requiredPipelinesConnectorOptions(w *walker, n *node, v *pipelines.ConnectorOptions) { +func requiredPipelinesConnectorOptions(w *walker, n *walk.Node, v *pipelines.ConnectorOptions) { if v == nil { - v = &pipelines.ConnectorOptions{} + return } - if c := n.child("google_ads_options"); c.exists() { - requiredPipelinesGoogleAdsOptions(w, &c, v.GoogleAdsOptions) + if x := v.GoogleAdsOptions; x != nil { + c := n.Child("google_ads_options") + requiredPipelinesGoogleAdsOptions(w, &c, x) + } else if n.IsRef("google_ads_options") { + c := n.Child("google_ads_options") + requiredPipelinesGoogleAdsOptions(w, &c, nil) } - if c := n.child("linkedin_ads_options"); c.exists() { - requiredPipelinesLinkedInAdsOptions(w, &c, v.LinkedinAdsOptions) + if x := v.LinkedinAdsOptions; x != nil { + c := n.Child("linkedin_ads_options") + requiredPipelinesLinkedInAdsOptions(w, &c, x) + } else if n.IsRef("linkedin_ads_options") { + c := n.Child("linkedin_ads_options") + requiredPipelinesLinkedInAdsOptions(w, &c, nil) } - if c := n.child("rabbitmq_options"); c.exists() { - requiredPipelinesRabbitmqOptions(w, &c, v.RabbitmqOptions) + if x := v.RabbitmqOptions; x != nil { + c := n.Child("rabbitmq_options") + requiredPipelinesRabbitmqOptions(w, &c, x) + } else if n.IsRef("rabbitmq_options") { + c := n.Child("rabbitmq_options") + requiredPipelinesRabbitmqOptions(w, &c, nil) } } -func requiredJobsSubscriptionAttachmentDataAttachment(w *walker, n *node, v *jobs.SubscriptionAttachmentDataAttachment) { +func requiredJobsSubscriptionAttachmentDataAttachment(w *walker, n *walk.Node, v *jobs.SubscriptionAttachmentDataAttachment) { if v == nil { - v = &jobs.SubscriptionAttachmentDataAttachment{} + w.missing(n, "file_format") + w.missing(n, "widgets") + return } - if !(v.FileFormat != "") && !n.has("file_format") { + if !(v.FileFormat != "" || walk.Forced(v.ForceSendFields, "FileFormat")) { w.missing(n, "file_format") } - if !(v.Widgets != nil) && !n.has("widgets") { + if !(v.Widgets != nil || n.IsRef("widgets")) { w.missing(n, "widgets") } if len(v.Widgets) > 0 { - c := n.child("widgets") - for i0 := range v.Widgets { - e := c.elem(i0) - requiredJobsSubscriptionAttachmentDataAttachmentWidget(w, &e, &v.Widgets[i0]) + c := n.Child("widgets") + for i := range v.Widgets { + e := c.Elem(i) + requiredJobsSubscriptionAttachmentDataAttachmentWidget(w, &e, &v.Widgets[i]) } } } -func requiredCatalogModelProviderServiceConfigSecretReference(w *walker, n *node, v *catalog.ModelProviderServiceConfigSecretReference) { +func requiredCatalogModelProviderServiceConfigSecretReference(w *walker, n *walk.Node, v *catalog.ModelProviderServiceConfigSecretReference) { if v == nil { - v = &catalog.ModelProviderServiceConfigSecretReference{} + w.missing(n, "name") + return } - if !(v.Name != "") && !n.has("name") { + if !(v.Name != "") { w.missing(n, "name") } } -func requiredServingApiKeyAuth(w *walker, n *node, v *serving.ApiKeyAuth) { +func requiredServingApiKeyAuth(w *walker, n *walk.Node, v *serving.ApiKeyAuth) { if v == nil { - v = &serving.ApiKeyAuth{} + w.missing(n, "key") + return } - if !(v.Key != "") && !n.has("key") { + if !(v.Key != "" || walk.Forced(v.ForceSendFields, "Key")) { w.missing(n, "key") } } -func requiredPipelinesGoogleAdsOptions(w *walker, n *node, v *pipelines.GoogleAdsOptions) { +func requiredPipelinesGoogleAdsOptions(w *walker, n *walk.Node, v *pipelines.GoogleAdsOptions) { if v == nil { - v = &pipelines.GoogleAdsOptions{} + w.missing(n, "manager_account_id") + return } - if !(v.ManagerAccountId != "") && !n.has("manager_account_id") { + if !(v.ManagerAccountId != "" || walk.Forced(v.ForceSendFields, "ManagerAccountId")) { w.missing(n, "manager_account_id") } - if c := n.child("custom_report_options"); c.exists() { - requiredPipelinesGoogleAdsCustomReportOptions(w, &c, v.CustomReportOptions) + if x := v.CustomReportOptions; x != nil { + c := n.Child("custom_report_options") + requiredPipelinesGoogleAdsCustomReportOptions(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "CustomReportOptions") || n.IsRef("custom_report_options") { + c := n.Child("custom_report_options") + requiredPipelinesGoogleAdsCustomReportOptions(w, &c, nil) } } -func requiredPipelinesLinkedInAdsOptions(w *walker, n *node, v *pipelines.LinkedInAdsOptions) { +func requiredPipelinesLinkedInAdsOptions(w *walker, n *walk.Node, v *pipelines.LinkedInAdsOptions) { if v == nil { - v = &pipelines.LinkedInAdsOptions{} + return } - if c := n.child("custom_report_options"); c.exists() { - requiredPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w, &c, v.CustomReportOptions) + if x := v.CustomReportOptions; x != nil { + c := n.Child("custom_report_options") + requiredPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w, &c, x) + } else if walk.Forced(v.ForceSendFields, "CustomReportOptions") || n.IsRef("custom_report_options") { + c := n.Child("custom_report_options") + requiredPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w, &c, nil) } } -func requiredPipelinesRabbitmqOptions(w *walker, n *node, v *pipelines.RabbitmqOptions) { +func requiredPipelinesRabbitmqOptions(w *walker, n *walk.Node, v *pipelines.RabbitmqOptions) { if v == nil { - v = &pipelines.RabbitmqOptions{} + w.missing(n, "queue") + return } - if !(v.Queue != "") && !n.has("queue") { + if !(v.Queue != "") { w.missing(n, "queue") } } -func requiredJobsSubscriptionAttachmentDataAttachmentWidget(w *walker, n *node, v *jobs.SubscriptionAttachmentDataAttachmentWidget) { +func requiredJobsSubscriptionAttachmentDataAttachmentWidget(w *walker, n *walk.Node, v *jobs.SubscriptionAttachmentDataAttachmentWidget) { if v == nil { - v = &jobs.SubscriptionAttachmentDataAttachmentWidget{} + w.missing(n, "page_id") + w.missing(n, "widget_id") + return } - if !(v.PageId != "") && !n.has("page_id") { + if !(v.PageId != "") { w.missing(n, "page_id") } - if !(v.WidgetId != "") && !n.has("widget_id") { + if !(v.WidgetId != "") { w.missing(n, "widget_id") } } -func requiredPipelinesGoogleAdsCustomReportOptions(w *walker, n *node, v *pipelines.GoogleAdsCustomReportOptions) { +func requiredPipelinesGoogleAdsCustomReportOptions(w *walker, n *walk.Node, v *pipelines.GoogleAdsCustomReportOptions) { if v == nil { - v = &pipelines.GoogleAdsCustomReportOptions{} + w.missing(n, "resource") + return } - if !(v.Resource != "") && !n.has("resource") { + if !(v.Resource != "") { w.missing(n, "resource") } } -func requiredPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w *walker, n *node, v *pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions) { +func requiredPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(w *walker, n *walk.Node, v *pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions) { if v == nil { - v = &pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions{} + w.missing(n, "finder") + return } - if !(v.Finder != "") && !n.has("finder") { + if !(v.Finder != "") { w.missing(n, "finder") } } diff --git a/bundle/config/validate/scripts.go b/bundle/config/validate/scripts.go index 323db769d23..0f9784c1b6b 100644 --- a/bundle/config/validate/scripts.go +++ b/bundle/config/validate/scripts.go @@ -9,8 +9,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type validateScripts struct{} @@ -38,13 +38,13 @@ func (f *validateScripts) Apply(ctx context.Context, b *bundle.Bundle) diag.Diag for _, k := range scriptKeys { script := b.Config.Scripts[k] - contentPath := dyn.NewPath(dyn.Key("scripts"), dyn.Key(k), dyn.Key("content")) + contentPath := structpath.NewPath(nil, "scripts", k, "content") if script.Content == "" { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("Script %s has no content", k), - Paths: dyn.ToStructPaths(contentPath), + Paths: []*structpath.PathNode{contentPath}, }) continue } @@ -59,14 +59,13 @@ func (f *validateScripts) Apply(ctx context.Context, b *bundle.Bundle) diag.Diag // validateScriptContent rejects any ${...} reference in a script's content. // Content is passed to the shell as-is, so ${...} would be ambiguous with a // bundle reference; reference a declared env entry with $NAME instead. -func validateScriptContent(b *bundle.Bundle, key, content string, p dyn.Path) diag.Diagnostics { - ref, ok := dynvar.NewRef(dyn.V(content)) +func validateScriptContent(b *bundle.Bundle, key, content string, p *structpath.PathNode) diag.Diagnostics { + ref, ok := structvar.NewRef(content) if !ok { return nil } first := ref.Matches[0][0] - v, _ := dyn.GetByPath(b.Config.Value(), p) return diag.Diagnostics{{ Severity: diag.Error, Summary: fmt.Sprintf("Found %s in script %s.content. Interpolation syntax ${...} is not supported in script content", first, key), @@ -79,8 +78,8 @@ from "content" with $NAME: env: MY_VAR: ${var.foo} content: echo "$MY_VAR"`, - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), + Locations: b.Config.GetLocationsOfExact(p), + Paths: []*structpath.PathNode{p}, }} } @@ -88,13 +87,12 @@ func validateScriptEnv(b *bundle.Bundle, key string, env map[string]string) diag var diags diag.Diagnostics for _, name := range slices.Sorted(maps.Keys(env)) { - ref, ok := dynvar.NewRef(dyn.V(env[name])) + ref, ok := structvar.NewRef(env[name]) if !ok { continue } - envValuePath := dyn.NewPath(dyn.Key("scripts"), dyn.Key(key), dyn.Key("env"), dyn.Key(name)) - v, _ := dyn.GetByPath(b.Config.Value(), envValuePath) + envValuePath := structpath.NewPath(nil, "scripts", key, "env", name) for _, refPath := range ref.References() { prefix, _, _ := strings.Cut(refPath, ".") @@ -104,8 +102,8 @@ func validateScriptEnv(b *bundle.Bundle, key string, env map[string]string) diag diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("${%s} cannot be used in scripts.%s.env.%s; only ${bundle.*}, ${workspace.*}, and ${var.*} are resolved before scripts execute", refPath, key, name), - Locations: v.Locations(), - Paths: dyn.ToStructPaths(envValuePath), + Locations: b.Config.GetLocationsOfExact(envValuePath), + Paths: []*structpath.PathNode{envValuePath}, }) } } diff --git a/bundle/config/validate/single_node_cluster.go b/bundle/config/validate/single_node_cluster.go index 88b7536f80b..cfdb924a8c3 100644 --- a/bundle/config/validate/single_node_cluster.go +++ b/bundle/config/validate/single_node_cluster.go @@ -2,13 +2,17 @@ package validate import ( "context" + "maps" + "slices" "strings" "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/cli/libs/log" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/pipelines" ) // Validates that any single node clusters defined in the bundle are correctly configured. @@ -36,43 +40,68 @@ are correctly set in the cluster specification: const singleNodeWarningSummary = `Single node cluster is not correctly configured` -func showSingleNodeClusterWarning(ctx context.Context, v dyn.Value) bool { +// clusterSpec holds the fields of compute.ClusterSpec and pipelines.PipelineCluster +// that the validation reads. +type clusterSpec struct { + numWorkers int + numWorkersSet bool + isSingleNode bool + sparkConf map[string]string + customTags map[string]string + policyId string +} + +func computeClusterSpec(c *compute.ClusterSpec) clusterSpec { + return clusterSpec{ + numWorkers: c.NumWorkers, + numWorkersSet: c.NumWorkers != 0 || slices.Contains(c.ForceSendFields, "NumWorkers"), + isSingleNode: c.IsSingleNode, + sparkConf: c.SparkConf, + customTags: c.CustomTags, + policyId: c.PolicyId, + } +} + +func pipelineClusterSpec(c *pipelines.PipelineCluster) clusterSpec { + return clusterSpec{ + numWorkers: c.NumWorkers, + numWorkersSet: c.NumWorkers != 0 || slices.Contains(c.ForceSendFields, "NumWorkers"), + sparkConf: c.SparkConf, + customTags: c.CustomTags, + policyId: c.PolicyId, + } +} + +// showSingleNodeClusterWarning reports whether the cluster c at p sets num_workers to +// 0 without configuring a single node cluster. +func showSingleNodeClusterWarning(ctx context.Context, b *bundle.Bundle, p *structpath.PathNode, c clusterSpec) bool { // Check if the user has explicitly set the num_workers to 0. Skip the warning // if that's not the case. - numWorkers, ok := v.Get("num_workers").AsInt() - if !ok || numWorkers > 0 { + if !c.numWorkersSet || c.numWorkers > 0 { return false } // If is_single_node is set to true, the cluster is correctly configured automatically. // No need to show the warning. - isSingleNode, ok := v.Get("is_single_node").AsBool() - if ok && isSingleNode { + if c.isSingleNode { return false } - // Convenient type that contains the common fields from compute.ClusterSpec and - // pipelines.PipelineCluster that we are interested in. - type ClusterConf struct { - SparkConf map[string]string `json:"spark_conf"` - CustomTags map[string]string `json:"custom_tags"` - PolicyId string `json:"policy_id"` - } - - conf := &ClusterConf{} - err := convert.ToTyped(conf, v) - if err != nil { - return false + // Values that are pure references are not resolved yet. + for _, k := range []string{"num_workers", "spark_conf", "custom_tags"} { + if b.Config.IsReferenceAt(structpath.NewStringKey(p, k)) { + return false + } } // If the policy id is set, we don't want to show the warning. This is because // the user might have configured `spark_conf` and `custom_tags` correctly // in their cluster policy. - if conf.PolicyId != "" { + if c.policyId != "" { return false } - profile, ok := conf.SparkConf["spark.databricks.cluster.profile"] + profile, ok := c.sparkConf["spark.databricks.cluster.profile"] if !ok { log.Debugf(ctx, "spark_conf spark.databricks.cluster.profile not found in single-node cluster spec") return true @@ -82,7 +111,7 @@ func showSingleNodeClusterWarning(ctx context.Context, v dyn.Value) bool { return true } - master, ok := conf.SparkConf["spark.master"] + master, ok := c.sparkConf["spark.master"] if !ok { log.Debugf(ctx, "spark_conf spark.master not found in single-node cluster spec") return true @@ -92,7 +121,7 @@ func showSingleNodeClusterWarning(ctx context.Context, v dyn.Value) bool { return true } - resourceClass, ok := conf.CustomTags["ResourceClass"] + resourceClass, ok := c.customTags["ResourceClass"] if !ok { log.Debugf(ctx, "custom_tag ResourceClass not found in single-node cluster spec") return true @@ -106,39 +135,59 @@ func showSingleNodeClusterWarning(ctx context.Context, v dyn.Value) bool { } func (m *singleNodeCluster) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - diags := diag.Diagnostics{} - - patterns := []dyn.Pattern{ - // Interactive clusters - dyn.NewPattern(dyn.Key("resources"), dyn.Key("clusters"), dyn.AnyKey()), - // Job clusters - dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey(), dyn.Key("job_clusters"), dyn.AnyIndex(), dyn.Key("new_cluster")), - // Job task clusters - dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey(), dyn.Key("tasks"), dyn.AnyIndex(), dyn.Key("new_cluster")), - // Job for each task clusters - dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey(), dyn.Key("tasks"), dyn.AnyIndex(), dyn.Key("for_each_task"), dyn.Key("task"), dyn.Key("new_cluster")), - // Pipeline clusters - dyn.NewPattern(dyn.Key("resources"), dyn.Key("pipelines"), dyn.AnyKey(), dyn.Key("clusters"), dyn.AnyIndex()), - } - - for _, p := range patterns { - _, err := dyn.MapByPattern(b.Config.Value(), p, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - warning := diag.Diagnostic{ - Severity: diag.Warning, - Summary: singleNodeWarningSummary, - Detail: singleNodeWarningDetail, - Locations: v.Locations(), - Paths: dyn.ToStructPaths(p), - } + c := clusterCheck{b: b, diags: diag.Diagnostics{}} + r := &b.Config.Resources - if showSingleNodeClusterWarning(ctx, v) { - diags = append(diags, warning) + // Interactive clusters + for _, name := range slices.Sorted(maps.Keys(r.Clusters)) { + if cl := r.Clusters[name]; cl != nil { + c.cluster(ctx, structpath.NewPath(nil, "resources", "clusters", name), computeClusterSpec(&cl.ClusterSpec)) + } + } + // Job clusters + for _, name := range slices.Sorted(maps.Keys(r.Jobs)) { + if job := r.Jobs[name]; job != nil { + for i := range job.JobClusters { + if nc := job.JobClusters[i].NewCluster; nc != nil { + c.cluster(ctx, structpath.NewPath(nil, "resources", "jobs", name, "job_clusters", i, "new_cluster"), computeClusterSpec(nc)) + } } - return v, nil + } + } + // Job task clusters, then the clusters of tasks in for_each_task + for _, nested := range []bool{false, true} { + r.IterateJobTasks(nested, func(p *structpath.PathNode, task *jobs.Task) bool { + if task.NewCluster != nil { + c.cluster(ctx, structpath.NewPath(p, "new_cluster"), computeClusterSpec(task.NewCluster)) + } + return true }) - if err != nil { - log.Debugf(ctx, "Error while applying single node cluster validation: %s", err) + } + // Pipeline clusters + for _, name := range slices.Sorted(maps.Keys(r.Pipelines)) { + if pl := r.Pipelines[name]; pl != nil { + for i := range pl.Clusters { + c.cluster(ctx, structpath.NewPath(nil, "resources", "pipelines", name, "clusters", i), pipelineClusterSpec(&pl.Clusters[i])) + } } } - return diags + return c.diags +} + +// clusterCheck collects the single node cluster warnings. +type clusterCheck struct { + b *bundle.Bundle + diags diag.Diagnostics +} + +func (c *clusterCheck) cluster(ctx context.Context, p *structpath.PathNode, spec clusterSpec) { + if showSingleNodeClusterWarning(ctx, c.b, p, spec) { + c.diags = append(c.diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: singleNodeWarningSummary, + Detail: singleNodeWarningDetail, + Locations: c.b.Config.GetLocationsOfExact(p), + Paths: []*structpath.PathNode{p}, + }) + } } diff --git a/bundle/config/validate/single_node_cluster_test.go b/bundle/config/validate/single_node_cluster_test.go index 997d39ea0ac..1c8d79af4f2 100644 --- a/bundle/config/validate/single_node_cluster_test.go +++ b/bundle/config/validate/single_node_cluster_test.go @@ -8,12 +8,12 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func failCases() []struct { @@ -112,10 +112,8 @@ func TestValidateSingleNodeClusterFailForInteractiveClusters(t *testing.T) { bundletest.SetLocation(b, "resources.clusters.foo", []diag.Location{{File: "a.yml", Line: 1, Column: 1}}) // We can't set num_workers to 0 explicitly 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.clusters.foo.num_workers", dyn.V(0)) - }) + // Set it explicitly, since zero values are otherwise treated as unset. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.clusters.foo.num_workers"), 0)) diags := SingleNodeCluster().Apply(ctx, b) assert.Equal(t, diag.Diagnostics{ { @@ -160,10 +158,8 @@ func TestValidateSingleNodeClusterFailForJobClusters(t *testing.T) { bundletest.SetLocation(b, "resources.jobs.foo.job_clusters[0].new_cluster", []diag.Location{{File: "b.yml", Line: 1, Column: 1}}) // We can't set num_workers to 0 explicitly 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.jobs.foo.job_clusters[0].new_cluster.num_workers", dyn.V(0)) - }) + // Set it explicitly, since zero values are otherwise treated as unset. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.job_clusters[0].new_cluster.num_workers"), 0)) diags := SingleNodeCluster().Apply(ctx, b) assert.Equal(t, diag.Diagnostics{ @@ -209,10 +205,8 @@ func TestValidateSingleNodeClusterFailForJobTaskClusters(t *testing.T) { bundletest.SetLocation(b, "resources.jobs.foo.tasks[0].new_cluster", []diag.Location{{File: "c.yml", Line: 1, Column: 1}}) // We can't set num_workers to 0 explicitly 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.jobs.foo.tasks[0].new_cluster.num_workers", dyn.V(0)) - }) + // Set it explicitly, since zero values are otherwise treated as unset. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.tasks[0].new_cluster.num_workers"), 0)) diags := bundle.Apply(ctx, b, SingleNodeCluster()) assert.Equal(t, diag.Diagnostics{ @@ -255,10 +249,8 @@ func TestValidateSingleNodeClusterFailForPipelineClusters(t *testing.T) { bundletest.SetLocation(b, "resources.pipelines.foo.clusters[0]", []diag.Location{{File: "d.yml", Line: 1, Column: 1}}) // We can't set num_workers to 0 explicitly 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.pipelines.foo.clusters[0].num_workers", dyn.V(0)) - }) + // Set it explicitly, since zero values are otherwise treated as unset. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.pipelines.foo.clusters[0].num_workers"), 0)) diags := bundle.Apply(ctx, b, SingleNodeCluster()) assert.Equal(t, diag.Diagnostics{ @@ -308,10 +300,8 @@ func TestValidateSingleNodeClusterFailForJobForEachTaskCluster(t *testing.T) { bundletest.SetLocation(b, "resources.jobs.foo.tasks[0].for_each_task.task.new_cluster", []diag.Location{{File: "e.yml", Line: 1, Column: 1}}) // We can't set num_workers to 0 explicitly 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.jobs.foo.tasks[0].for_each_task.task.new_cluster.num_workers", dyn.V(0)) - }) + // Set it explicitly, since zero values are otherwise treated as unset. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.tasks[0].for_each_task.task.new_cluster.num_workers"), 0)) diags := bundle.Apply(ctx, b, SingleNodeCluster()) assert.Equal(t, diag.Diagnostics{ @@ -392,9 +382,7 @@ func TestValidateSingleNodeClusterPassInteractiveClusters(t *testing.T) { } if tc.numWorkers != nil { - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources.clusters.foo.num_workers", dyn.V(*tc.numWorkers)) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.clusters.foo.num_workers"), *tc.numWorkers)) } diags := bundle.Apply(ctx, b, SingleNodeCluster()) @@ -432,9 +420,7 @@ func TestValidateSingleNodeClusterPassJobClusters(t *testing.T) { } if tc.numWorkers != nil { - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources.jobs.foo.job_clusters[0].new_cluster.num_workers", dyn.V(*tc.numWorkers)) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.job_clusters[0].new_cluster.num_workers"), *tc.numWorkers)) } diags := bundle.Apply(ctx, b, SingleNodeCluster()) @@ -472,9 +458,7 @@ func TestValidateSingleNodeClusterPassJobTaskClusters(t *testing.T) { } if tc.numWorkers != nil { - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources.jobs.foo.tasks[0].new_cluster.num_workers", dyn.V(*tc.numWorkers)) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.tasks[0].new_cluster.num_workers"), *tc.numWorkers)) } diags := bundle.Apply(ctx, b, SingleNodeCluster()) @@ -509,9 +493,7 @@ func TestValidateSingleNodeClusterPassPipelineClusters(t *testing.T) { } if tc.numWorkers != nil { - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources.pipelines.foo.clusters[0].num_workers", dyn.V(*tc.numWorkers)) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.pipelines.foo.clusters[0].num_workers"), *tc.numWorkers)) } diags := bundle.Apply(ctx, b, SingleNodeCluster()) @@ -553,9 +535,7 @@ func TestValidateSingleNodeClusterPassJobForEachTaskCluster(t *testing.T) { } if tc.numWorkers != nil { - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources.jobs.foo.tasks[0].for_each_task.task.new_cluster.num_workers", dyn.V(*tc.numWorkers)) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.tasks[0].for_each_task.task.new_cluster.num_workers"), *tc.numWorkers)) } diags := bundle.Apply(ctx, b, SingleNodeCluster()) @@ -590,13 +570,8 @@ func TestValidateSingleNodeClusterWithIsSingleNode(t *testing.T) { } // Set num_workers to 0 and is_single_node to true - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - v, err := dyn.Set(v, "resources.jobs.foo.job_clusters[0].new_cluster.num_workers", dyn.V(0)) - if err != nil { - return v, err - } - return dyn.Set(v, "resources.jobs.foo.job_clusters[0].new_cluster.is_single_node", dyn.V(true)) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.job_clusters[0].new_cluster.num_workers"), 0)) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.foo.job_clusters[0].new_cluster.is_single_node"), true)) diags := bundle.Apply(ctx, b, SingleNodeCluster()) assert.Empty(t, diags) diff --git a/bundle/config/validate/tf_only_references.go b/bundle/config/validate/tf_only_references.go index 3603b6f4a7f..a0407a74a30 100644 --- a/bundle/config/validate/tf_only_references.go +++ b/bundle/config/validate/tf_only_references.go @@ -6,11 +6,11 @@ import ( "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/bundle/terraform_dabs_map" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type tfOnlyReferences struct{} @@ -29,22 +29,15 @@ func (m *tfOnlyReferences) Name() string { func (m *tfOnlyReferences) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { var diags diag.Diagnostics - // Walk the entire config looking for ${resources.*} references. - _ = dyn.WalkReadOnly(b.Config.Value(), func(_ dyn.Path, v dyn.Value) error { - ref, ok := dynvar.NewRef(v) - if !ok { - return nil - } - for _, r := range ref.References() { - if !strings.HasPrefix(r, "resources.") { - continue - } - if d := checkTFOnlyReference(r, v.Location()); d != nil { - diags = append(diags, *d) - } - } - return nil + // Look for ${resources.*} references in strings and in fields that hold a pure + // reference. + walk.Strings(&b.Config, walk.WithReferences, func(n *walk.Node, s string) (string, bool) { + diags = append(diags, checkTFOnlyReferences(s, n.Location())...) + return "", false }) + for p, ref := range b.Config.References() { + diags = append(diags, checkTFOnlyReferences(ref, b.Config.GetLocationOfExact(p))...) + } if len(diags) > 0 { b.Metrics.AddBoolValue("has_tf_only_references", true) @@ -53,29 +46,42 @@ func (m *tfOnlyReferences) Apply(_ context.Context, b *bundle.Bundle) diag.Diagn return diags } +// checkTFOnlyReferences checks the ${resources.*} references in s. +func checkTFOnlyReferences(s string, loc diag.Location) diag.Diagnostics { + ref, ok := structvar.NewRef(s) + if !ok { + return nil + } + var diags diag.Diagnostics + for _, r := range ref.References() { + if !strings.HasPrefix(r, "resources.") { + continue + } + if d := checkTFOnlyReference(r, loc); d != nil { + diags = append(diags, *d) + } + } + return diags +} + // checkTFOnlyReference checks a single reference string like // "resources.jobs.src.always_running" and returns a diagnostic when it refers // to a TF-only field, or nil otherwise. func checkTFOnlyReference(ref string, loc diag.Location) *diag.Diagnostic { - p, err := dyn.NewPathFromString(ref) + p, err := structpath.ParsePath(ref) // Need at least resources... - if err != nil || len(p) < 4 || p[0].Key() != "resources" { + if err != nil || p.Len() < 4 || p.KeyAt(0) != "resources" { return nil } - group := p[1].Key() + group := p.KeyAt(1) tfOnlyFields, ok := terraform_dabs_map.TerraformOnlyFields[group] if !ok || len(tfOnlyFields) == 0 { return nil } // Field path is everything after resources... - fieldNode, err := structpath.ParsePath(p[3:].String()) - if err != nil { - return nil - } - - if !tfOnlyFields.Contains(fieldNode) { + if !tfOnlyFields.Contains(p.SkipPrefix(3)) { return nil } diff --git a/bundle/config/validate/tf_only_references_test.go b/bundle/config/validate/tf_only_references_test.go index 7b994cf864e..a4f5850f8c8 100644 --- a/bundle/config/validate/tf_only_references_test.go +++ b/bundle/config/validate/tf_only_references_test.go @@ -6,9 +6,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,9 +30,7 @@ func makeBundle(t *testing.T) *bundle.Bundle { func TestTFOnlyReferences_Error(t *testing.T) { b := makeBundle(t) - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources.jobs.dst.name", dyn.V("${resources.jobs.src.always_running}")) - }) + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.dst.name"), "${resources.jobs.src.always_running}")) diags := TFOnlyReferences().Apply(t.Context(), b) require.Len(t, diags, 1) @@ -44,10 +41,8 @@ func TestTFOnlyReferences_Error(t *testing.T) { func TestTFOnlyReferences_NormalReference(t *testing.T) { b := makeBundle(t) - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - // "name" is not a TF-only field; no diagnostic expected. - return dyn.Set(v, "resources.jobs.dst.name", dyn.V("${resources.jobs.src.name}")) - }) + // "name" is not a TF-only field; no diagnostic expected. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.dst.name"), "${resources.jobs.src.name}")) diags := TFOnlyReferences().Apply(t.Context(), b) assert.Empty(t, diags) @@ -55,10 +50,8 @@ func TestTFOnlyReferences_NormalReference(t *testing.T) { func TestTFOnlyReferences_RenamedField(t *testing.T) { b := makeBundle(t) - bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { - // "git_source[0].branch" is a TF rename (not TF-only), should not error. - return dyn.Set(v, "resources.jobs.dst.name", dyn.V("${resources.jobs.src.git_source[0].branch}")) - }) + // "git_source[0].branch" is a TF rename (not TF-only), should not error. + require.NoError(t, b.Config.Set(structpath.MustParsePath("resources.jobs.dst.name"), "${resources.jobs.src.git_source[0].branch}")) diags := TFOnlyReferences().Apply(t.Context(), b) assert.Empty(t, diags) diff --git a/bundle/config/validate/validate_artifact_path.go b/bundle/config/validate/validate_artifact_path.go index 36494cf7d77..4b91c3641a9 100644 --- a/bundle/config/validate/validate_artifact_path.go +++ b/bundle/config/validate/validate_artifact_path.go @@ -11,9 +11,8 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/libraries" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/apierr" ) @@ -52,7 +51,7 @@ func extractVolumeFromPath(artifactPath string) (string, string, string, error) return catalogName, schemaName, volumeName, nil } -func findVolumeInBundle(r config.Root, catalogName, schemaName, volumeName string) (dyn.Path, []diag.Location, bool) { +func findVolumeInBundle(r config.Root, catalogName, schemaName, volumeName string) (*structpath.PathNode, []diag.Location, bool) { volumes := r.Resources.Volumes for k, v := range volumes { if v.CatalogName != catalogName || v.Name != volumeName { @@ -64,12 +63,13 @@ func findVolumeInBundle(r config.Root, catalogName, schemaName, volumeName strin // schema name is interpolated. // We only have to check for ${resources.schemas...} references because any // other valid reference (like ${var.foo}) would have been interpolated by this point. - p, ok := dynvar.PureReferenceToPath(v.SchemaName) - isSchemaDefinedInBundle := ok && p.HasPrefix(dyn.Path{dyn.Key("resources"), dyn.Key("schemas")}) + p, ok := structvar.PureReferenceToPath(v.SchemaName) + isSchemaDefinedInBundle := ok && p.HasPrefix(structpath.NewPath(nil, "resources", "schemas")) if v.SchemaName != schemaName && !isSchemaDefinedInBundle { continue } - return dyn.Path{dyn.Key("resources"), dyn.Key("volumes"), dyn.Key(k)}, r.GetLocationsOf(structpath.NewPath(nil, "resources", "volumes", k)), true + volumePath := structpath.NewPath(nil, "resources", "volumes", k) + return volumePath, r.GetLocationsOf(volumePath), true } return nil, nil, false } @@ -118,7 +118,7 @@ this bundle but which has not been deployed yet. Please first deploy the volume using 'bundle deploy' and then switch over to using it in the artifact_path.`, Locations: slices.Concat(b.Config.GetLocations("workspace.artifact_path"), locations), - Paths: append(structpath.NewPathSlice("workspace", "artifact_path"), dyn.ToStructPath(path)), + Paths: []*structpath.PathNode{structpath.NewPath(nil, "workspace", "artifact_path"), path}, }} } diff --git a/bundle/config/validate/validate_artifact_path_test.go b/bundle/config/validate/validate_artifact_path_test.go index 97a08ab6e30..778326430e0 100644 --- a/bundle/config/validate/validate_artifact_path_test.go +++ b/bundle/config/validate/validate_artifact_path_test.go @@ -8,7 +8,6 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/experimental/mocks" @@ -209,7 +208,7 @@ func TestFindVolumeInBundle(t *testing.T) { Line: 1, Column: 2, }}, locations) - assert.Equal(t, dyn.MustPathFromString("resources.volumes.foo"), path) + assert.Equal(t, structpath.MustParsePath("resources.volumes.foo"), path) // wrong volume name _, _, ok = findVolumeInBundle(b.Config, "main", "my_schema", "doesnotexist") @@ -238,5 +237,5 @@ func TestFindVolumeInBundle(t *testing.T) { Line: 1, Column: 2, }}, locations) - assert.Equal(t, dyn.MustPathFromString("resources.volumes.foo"), path) + assert.Equal(t, structpath.MustParsePath("resources.volumes.foo"), path) } diff --git a/bundle/config/validate/validate_engine.go b/bundle/config/validate/validate_engine.go index f19c2270b26..55a99e7fb7a 100644 --- a/bundle/config/validate/validate_engine.go +++ b/bundle/config/validate/validate_engine.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/engine" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" ) @@ -26,11 +25,12 @@ func (v *validateEngine) Name() string { func (v *validateEngine) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { var diags diag.Diagnostics - if tf := dyn.GetValue(b.Config.Value(), "bundle.terraform"); tf.IsValid() { + // A reference to a variable also allocates the section. + if b.Config.Bundle.Terraform != nil { diags = diags.Append(diag.Diagnostic{ Severity: diag.Warning, Summary: "bundle.terraform is deprecated and has no effect: " + engine.TerraformRemovedSummary, - Locations: tf.Locations(), + Locations: b.Config.GetLocationsOfExact(structpath.NewPath(nil, "bundle", "terraform")), Paths: structpath.NewPathSlice("bundle", "terraform"), }) } @@ -40,7 +40,7 @@ func (v *validateEngine) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnos return diags } - loc := dyn.GetValue(b.Config.Value(), "bundle.engine").Location() + loc := b.Config.GetLocationOf(structpath.NewPath(nil, "bundle", "engine")) parsed, ok := engine.Parse(string(configEngine)) if !ok { diff --git a/bundle/config/validate/validate_volume_path.go b/bundle/config/validate/validate_volume_path.go index c440dfbf06f..802034bbbee 100644 --- a/bundle/config/validate/validate_volume_path.go +++ b/bundle/config/validate/validate_volume_path.go @@ -23,13 +23,12 @@ func (m *validateVolumePath) Apply(ctx context.Context, b *bundle.Bundle) diag.D // Define paths to check and their corresponding config field names pathChecks := []struct { path string - configName string configPath *structpath.PathNode }{ - {b.Config.Workspace.RootPath, "workspace.root_path", structpath.NewPath(nil, "workspace", "root_path")}, - {b.Config.Workspace.FilePath, "workspace.file_path", structpath.NewPath(nil, "workspace", "file_path")}, - {b.Config.Workspace.StatePath, "workspace.state_path", structpath.NewPath(nil, "workspace", "state_path")}, - {b.Config.Workspace.ResourcePath, "workspace.resource_path", structpath.NewPath(nil, "workspace", "resource_path")}, + {b.Config.Workspace.RootPath, structpath.NewPath(nil, "workspace", "root_path")}, + {b.Config.Workspace.FilePath, structpath.NewPath(nil, "workspace", "file_path")}, + {b.Config.Workspace.StatePath, structpath.NewPath(nil, "workspace", "state_path")}, + {b.Config.Workspace.ResourcePath, structpath.NewPath(nil, "workspace", "resource_path")}, } // Check each path @@ -37,14 +36,14 @@ func (m *validateVolumePath) Apply(ctx context.Context, b *bundle.Bundle) diag.D if check.path != "" && strings.HasPrefix(check.path, "/Volumes/") { diags = diags.Append(diag.Diagnostic{ Severity: diag.Error, - Summary: fmt.Sprintf("%s %s starts with /Volumes. /Volumes can only be used with workspace.artifact_path.", check.configName, check.path), + Summary: fmt.Sprintf("%s %s starts with /Volumes. /Volumes can only be used with workspace.artifact_path.", check.configPath, check.path), Detail: "For more information, see https://docs.databricks.com/aws/en/dev-tools/bundles/settings#workspace", Locations: b.Config.GetLocationsOf(check.configPath), Paths: []*structpath.PathNode{check.configPath}, }) // Return early for root path validation - if check.configName == "workspace.root_path" { + if check.configPath.String() == "workspace.root_path" { return diags } } diff --git a/bundle/config/validate/walk_node.go b/bundle/config/validate/walk_node.go index bcf8d75824a..41cff6ebd46 100644 --- a/bundle/config/validate/walk_node.go +++ b/bundle/config/validate/walk_node.go @@ -4,149 +4,51 @@ import ( "fmt" "slices" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) -// This file is the bridge between the generated typed walkers (required_walk.go, -// enum_walk.go) and the dynamic configuration. The typed configuration cannot tell an -// unset field from one that is null, set to its zero value or to a pure variable -// reference, and it has no locations, so a node follows the [dyn.Value] at its path -// for those. The typed configuration is derived from the dynamic one after every -// mutation, so the two agree. Replace the node once the configuration no longer uses -// dyn. - -// node is a position in the configuration visited by a generated walker. Nodes live -// on the stack and link to their parent; the path is only built for a diagnostic. -type node struct { - parent *node - key string - index int // -1 for a key, -2 for the root - v dyn.Value -} - -func rootNode(v dyn.Value) node { - return node{index: -2, v: v} -} - -// child returns the node of the key k of n. -func (n *node) child(k string) node { - return node{parent: n, key: k, index: -1, v: n.v.Get(k)} -} - -// elem returns the node of element i of n. -func (n *node) elem(i int) node { - return node{parent: n, index: i, v: n.v.Index(i)} -} - -// exists reports whether the configuration has a value (possibly null) at n. -func (n *node) exists() bool { - return n.v.IsValid() -} - -// has reports whether the key k of n is set to a value that is not null. -func (n *node) has(k string) bool { - kind := n.v.Get(k).Kind() - return kind != dyn.KindInvalid && kind != dyn.KindNil +// walker holds the state of a generated validation walk (required_walk.go, +// enum_walk.go). +type walker struct { + diags diag.Diagnostics } -func (n *node) path() *structpath.PathNode { - switch n.index { - case -2: - return nil - case -1: - return structpath.NewStringKey(n.parent.path(), n.key) - default: - return structpath.NewIndex(n.parent.path(), n.index) +// walkConfig runs the generated walker fn over the configuration of root; refs makes +// the nodes follow the pure references. +func walkConfig(root *config.Root, refs bool, fn func(*walker, *walk.Node, *config.Root)) diag.Diagnostics { + w := &walker{} + n := walk.Root(root) + if refs { + n = walk.RootWithReferences(root) } -} - -// walker holds the diagnostics of a generated walk. -type walker struct { - diags diag.Diagnostics + fn(w, &n, root) + return w.diags } // missing reports that the required field of n is not set. -func (w *walker) missing(n *node, field string) { +func (w *walker) missing(n *walk.Node, field string) { w.diags = append(w.diags, diag.Diagnostic{ Severity: diag.Warning, Summary: fmt.Sprintf("required field %q is not set", field), - Locations: n.v.Locations(), - Paths: []*structpath.PathNode{n.path()}, + Locations: n.Locations().Get(), + Paths: []*structpath.PathNode{n.Path()}, }) } -// enumField checks the enum field k of n, whose typed value is s. -func (w *walker) enumField(n *node, k, s string, valid []string) { - if s != "" && slices.Contains(valid, s) { - return - } - c := n.child(k) - w.enumValue(&c, valid) -} - -// enumElem checks the enum element n, whose typed value is s. -func (w *walker) enumElem(n *node, s string, valid []string) { - if s != "" && slices.Contains(valid, s) { - return - } - w.enumValue(n, valid) -} - -// enumValue reports the value of n if it is a string that is not one of valid. Values -// with variable references are not resolved yet and are skipped. -func (w *walker) enumValue(n *node, valid []string) { - s, ok := n.v.AsString() - if !ok || dynvar.ContainsVariableReference(s) || slices.Contains(valid, s) { +// enumValue reports s, the value of n, if it is not one of valid. Values with variable +// references are not resolved yet and are skipped. +func (w *walker) enumValue(n *walk.Node, s string, valid []string) { + if slices.Contains(valid, s) || structvar.ContainsVariableReference(s) { return } w.diags = append(w.diags, diag.Diagnostic{ Severity: diag.Warning, Summary: fmt.Sprintf("invalid value %q for enum field. Valid values are %v", s, valid), - Locations: n.v.Locations(), - Paths: []*structpath.PathNode{n.path()}, + Locations: n.Locations().Get(), + Paths: []*structpath.PathNode{n.Path()}, }) } - -// sortByWalkOrder orders diagnostics with one path each in the order [dyn.WalkReadOnly] -// visits their paths in root, which the walkers do not follow. -func sortByWalkOrder(root dyn.Value, diags diag.Diagnostics) { - type item struct { - pos []int - d diag.Diagnostic - } - items := make([]item, len(diags)) - for i, d := range diags { - items[i] = item{walkPosition(root, d.Paths[0]), d} - } - slices.SortStableFunc(items, func(a, b item) int { - return slices.Compare(a.pos, b.pos) - }) - for i := range items { - diags[i] = items[i].d - } -} - -// walkPosition returns the position of each component of path p in root: the index of -// a key among the keys of its map, or the index of an element. -func walkPosition(root dyn.Value, p *structpath.PathNode) []int { - v := root - var pos []int - for _, c := range p.AsSlice() { - if i, ok := c.Index(); ok { - pos = append(pos, i) - v = v.Index(i) - continue - } - k, _ := c.StringKey() - m, _ := v.AsMap() - i := slices.IndexFunc(m.Pairs(), func(pair dyn.Pair) bool { - return pair.Key.MustString() == k - }) - pos = append(pos, i) - v = v.Get(k) - } - return pos -} diff --git a/bundle/config/validate/walk_test.go b/bundle/config/validate/walk_test.go index cf0801b0da3..b8e21309ccb 100644 --- a/bundle/config/validate/walk_test.go +++ b/bundle/config/validate/walk_test.go @@ -7,7 +7,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -57,7 +56,9 @@ resources: } func TestRequiredWalkSetValues(t *testing.T) { - // A pure reference and an explicit zero value set a field; null does not. + // A pure reference sets a field; null does not. An explicit zero of a field that has no + // ForceSendFields (here value: 0) cannot be told from an unset field in the typed + // configuration, so it is reported (known difference from the dyn implementation). b := loadBundle(t, ` bundle: name: test-bundle @@ -86,6 +87,7 @@ resources: assert.Equal(t, []string{ `required field "commands" is not set at resources.jobs.job.tasks[1].dbt_task (23)`, `required field "job_id" is not set at resources.jobs.job.tasks[0].run_job_task (20)`, + `required field "value" is not set at resources.jobs.job.health.rules[1] (15)`, }, summarize(diags)) } @@ -111,9 +113,8 @@ resources: }, summarize(diags)) } -func TestEnumWalkKeepsWalkOrderForTies(t *testing.T) { - // Values without a location tie in the sort; they keep the order of the - // configuration, not that of the walk over the typed maps. +func TestEnumWalkOrdersTiesByPath(t *testing.T) { + // Diagnostics without a location tie in the sort; the path breaks the tie. b := loadBundle(t, ` bundle: name: test-bundle @@ -132,21 +133,15 @@ resources: - task_key: a run_if: SOMETIMES `) - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.MapByPattern(v, dyn.NewPattern(dyn.Key("resources"), dyn.Key("jobs"), dyn.AnyKey(), dyn.Key("tasks"), dyn.AnyIndex(), dyn.Key("run_if")), func(_ dyn.Path, v dyn.Value) (dyn.Value, error) { - return dyn.V(v.MustString()), nil - }) - }) - require.NoError(t, err) diags := bundle.Apply(t.Context(), b, Enum()) var paths []string for _, d := range diags { paths = append(paths, d.Paths[0].String()) } assert.Equal(t, []string{ - "resources.jobs.zeta.tasks[0].run_if", "resources.jobs.alpha.tasks[0].run_if", "resources.jobs.mid.tasks[0].run_if", + "resources.jobs.zeta.tasks[0].run_if", }, paths) } diff --git a/bundle/config/walk/.gitattributes b/bundle/config/walk/.gitattributes new file mode 100644 index 00000000000..aa33ca924e6 --- /dev/null +++ b/bundle/config/walk/.gitattributes @@ -0,0 +1 @@ +*_walk.go linguist-generated=true diff --git a/bundle/config/walk/node.go b/bundle/config/walk/node.go new file mode 100644 index 00000000000..f758c33c9e9 --- /dev/null +++ b/bundle/config/walk/node.go @@ -0,0 +1,162 @@ +// Package walk holds the node type of the generated typed walkers over the bundle +// configuration and the walkers that are not specific to one mutator. +package walk + +import ( + "reflect" + "slices" + + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" +) + +// Node is a position in the configuration visited by a generated walker. Nodes live +// on the stack and link to their parent; the path is only built when needed (e.g. for +// a diagnostic). A node also follows the locations tree and the tree of pure +// references, so neither needs a path lookup. +type Node struct { + parent *Node + key string + index int // -1 for a key, -2 for the root + locs *structvar.Locations + refs *refNode +} + +// Root returns the root node of the configuration r. Its nodes do not follow the pure +// references: use [RootWithReferences] for [Node.IsRef]. +func Root(r *config.Root) Node { + return Node{index: -2, locs: r.LocationTree()} +} + +// RootWithReferences is [Root] whose nodes also follow the pure references. +func RootWithReferences(r *config.Root) Node { + return rootWithReferencesUnder(r, nil) +} + +// rootWithReferencesUnder is [RootWithReferences] following only the references at +// path and below it, for a walk that stays there. +func rootWithReferencesUnder(r *config.Root, path *structpath.PathNode) Node { + n := Root(r) + n.refs = newRefTree(r, path) + return n +} + +// Child returns the node of the key k of n. +func (n *Node) Child(k string) Node { + return Node{parent: n, key: k, index: -1, locs: n.locs.Key(k), refs: n.refs.key(k)} +} + +// Elem returns the node of element i of n. +func (n *Node) Elem(i int) Node { + return Node{parent: n, index: i, locs: n.locs.Index(i), refs: n.refs.elem(i)} +} + +// Path returns the path of n. +func (n *Node) Path() *structpath.PathNode { + switch n.index { + case -2: + return nil + case -1: + return structpath.NewStringKey(n.parent.Path(), n.key) + default: + return structpath.NewIndex(n.parent.Path(), n.index) + } +} + +// Locations returns the locations of the value at n. +func (n *Node) Locations() *structvar.Locations { + return n.locs +} + +// Location returns the first location of the value at n. +func (n *Node) Location() diag.Location { + locs := n.locs.Get() + if len(locs) == 0 { + return diag.Location{} + } + return locs[0] +} + +// isRef reports whether n itself holds a pure variable reference. +func (n *Node) isRef() bool { + return n.refs != nil && n.refs.ref +} + +// IsRef reports whether the key k of n holds a pure variable reference. Only fields +// that cannot hold a string can. +func (n *Node) IsRef(k string) bool { + r := n.refs.key(k) + return r != nil && r.ref +} + +// HasRefs reports whether the key k of n holds a pure variable reference or has one +// below it (e.g. a struct whose only set field is a reference). +func (n *Node) HasRefs(k string) bool { + return n.refs.key(k) != nil +} + +// refNode is a tree of the paths that hold pure variable references. +type refNode struct { + ref bool + keys map[string]*refNode + elems map[int]*refNode +} + +func (r *refNode) key(k string) *refNode { + if r == nil { + return nil + } + return r.keys[k] +} + +func (r *refNode) elem(i int) *refNode { + if r == nil { + return nil + } + return r.elems[i] +} + +func newRefTree(root *config.Root, under *structpath.PathNode) *refNode { + var out *refNode + for p := range root.ReferencesUnder(under) { + if out == nil { + out = &refNode{} + } + n := out + for _, c := range p.AsSlice() { + if k, ok := c.StringKey(); ok { + if n.keys == nil { + n.keys = map[string]*refNode{} + } + if n.keys[k] == nil { + n.keys[k] = &refNode{} + } + n = n.keys[k] + } else if i, ok := c.Index(); ok { + if n.elems == nil { + n.elems = map[int]*refNode{} + } + if n.elems[i] == nil { + n.elems[i] = &refNode{} + } + n = n.elems[i] + } + } + n.ref = true + } + return out +} + +// Forced reports whether name is listed in the ForceSendFields fsf. Generated walkers +// use it to decide whether a zero value is set. +func Forced(fsf []string, name string) bool { + return slices.Contains(fsf, name) +} + +// IsZeroStruct reports whether the struct *v is the zero value. Generated walkers only +// need it for the few struct fields that are not pointers. +func IsZeroStruct[T any](v *T) bool { + return reflect.ValueOf(v).Elem().IsZero() +} diff --git a/bundle/config/walk/strings.go b/bundle/config/walk/strings.go new file mode 100644 index 00000000000..692c33953ff --- /dev/null +++ b/bundle/config/walk/strings.go @@ -0,0 +1,188 @@ +package walk + +import ( + "maps" + "reflect" + "slices" + "strings" + + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" +) + +// Mode selects the strings [Strings] visits. +type Mode int + +const ( + // WithReferences visits the strings that contain a variable reference ("${"). + WithReferences Mode = iota + // NonEmpty visits every non-empty string. + NonEmpty + // EmptyFields visits the struct fields with omitempty that hold an empty string + // and are still present: forced, or a pointer or interface holding "". + EmptyFields +) + +// StringFunc is called for a visited string s at n. It returns a replacement and +// whether to store it. +type StringFunc func(n *Node, s string) (string, bool) + +// Strings calls fn for the strings of the configuration selected by mode, with plain +// field access (strings_walk.go is generated by bundle/internal/validation). Pure +// references in fields that cannot hold a string are not strings: see +// [config.Root.References]. +// +// TODO: processors that run back to back (e.g. reference collection and the +// workspace-prefix rewrite) could share one walk: visit every string with its kind +// (empty, plain, containing "${", pure reference) and dispatch to each processor, +// paying for the traversal once. Phase ordering limits which ones can be combined. +func Strings(root *config.Root, mode Mode, fn StringFunc) { + c := &stringsWalk{mode: mode, fn: fn} + n := RootWithReferences(root) + stringsConfigRoot(c, &n, root) +} + +// StringsWithoutResources is [Strings] for the configuration outside "resources". +func StringsWithoutResources(root *config.Root, mode Mode, fn StringFunc) { + c := &stringsWalk{mode: mode, fn: fn, skipResources: true} + n := RootWithReferences(root) + stringsConfigRoot(c, &n, root) +} + +// ResourceStrings is [Strings] for "resources" only. +func ResourceStrings(root *config.Root, mode Mode, fn StringFunc) { + c := &stringsWalk{mode: mode, fn: fn} + n := rootWithReferencesUnder(root, structpath.NewStringKey(nil, "resources")) + ch := n.Child("resources") + stringsConfigResources(c, &ch, &root.Resources) +} + +// ResourceStringsOf is [Strings] for the resource name of type typ. +func ResourceStringsOf(root *config.Root, typ, name string, mode Mode, fn StringFunc) { + c := &stringsWalk{mode: mode, fn: fn} + n := rootWithReferencesUnder(root, structpath.NewPath(nil, "resources", typ, name)) + rn := n.Child("resources") + stringsResource(c, &rn, &root.Resources, typ, name) +} + +// VariableLookupStrings is [Strings] for the lookups of the variables +// ("variables.*.lookup"), in the order of the variable names. +func VariableLookupStrings(root *config.Root, mode Mode, fn StringFunc) { + c := &stringsWalk{mode: mode, fn: fn} + n := rootWithReferencesUnder(root, structpath.NewStringKey(nil, "variables")) + vn := n.Child("variables") + for _, k := range sortedKeys(root.Variables) { + v := root.Variables[k] + if v == nil || v.Lookup == nil { + continue + } + ch := vn.Child(k) + ln := ch.Child("lookup") + stringsVariableLookup(c, &ln, v.Lookup) + } +} + +type stringsWalk struct { + mode Mode + fn StringFunc + skipResources bool +} + +func (c *stringsWalk) match(s string) bool { + switch c.mode { + case WithReferences: + return strings.Contains(s, "${") + case NonEmpty: + return s != "" + default: + return false + } +} + +// any visits the strings of a dynamic value (e.g. a variable value): the maps, slices +// and strings that decoding produces. +func (c *stringsWalk) any(n *Node, v *any) { + switch x := (*v).(type) { + case string: + if c.match(x) { + if r, ok := c.fn(n, x); ok { + *v = r + } + } + case map[string]any: + for _, k := range sortedKeys(x) { + e := x[k] + ch := n.Child(k) + c.any(&ch, &e) + x[k] = e + } + case []any: + for i := range x { + ch := n.Elem(i) + c.any(&ch, &x[i]) + } + case nil, bool, int, int64, float64: + // Scalars decoded into a dynamic value hold no strings. + default: + // Other values set from code (typed maps, slices, structs, named strings). + rv := reflect.ValueOf(v).Elem() + c.value(n, rv) + } +} + +// value visits the strings of the dynamic value v, which is settable. +func (c *stringsWalk) value(n *Node, v reflect.Value) { + switch v.Kind() { + case reflect.Interface, reflect.Pointer: + if v.IsNil() { + return + } + e := v.Elem() + if v.Kind() == reflect.Interface { + // Interface values are not settable: visit a copy and store it back. + cp := reflect.New(e.Type()).Elem() + cp.Set(e) + c.value(n, cp) + v.Set(cp) + return + } + c.value(n, e) + case reflect.String: + if s := v.String(); c.match(s) { + if r, ok := c.fn(n, s); ok { + v.SetString(r) + } + } + case reflect.Slice, reflect.Array: + for i := range v.Len() { + ch := n.Elem(i) + c.value(&ch, v.Index(i)) + } + case reflect.Map: + if v.Type().Key().Kind() != reflect.String { + return + } + keys := v.MapKeys() + slices.SortFunc(keys, func(a, b reflect.Value) int { return strings.Compare(a.String(), b.String()) }) + for _, k := range keys { + e := reflect.New(v.Type().Elem()).Elem() + e.Set(v.MapIndex(k)) + ch := n.Child(k.String()) + c.value(&ch, e) + v.SetMapIndex(k, e) + } + case reflect.Struct: + info := structvar.GetStructInfo(v.Type()) + for _, f := range info.List { + ch := n.Child(f.Key) + c.value(&ch, structvar.FieldByIndex(v, f.Index)) + } + default: + } +} + +// sortedKeys returns the keys of m in the order maps are visited. +func sortedKeys[K ~string, V any](m map[K]V) []K { + return slices.Sorted(maps.Keys(m)) +} diff --git a/bundle/config/walk/strings_walk.go b/bundle/config/walk/strings_walk.go new file mode 100644 index 00000000000..0346d054fbc --- /dev/null +++ b/bundle/config/walk/strings_walk.go @@ -0,0 +1,18919 @@ +// Code generated by bundle/internal/validation. DO NOT EDIT. + +package walk + +import ( + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/engine" + "github.com/databricks/cli/bundle/config/loctable" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/config/variable" + "github.com/databricks/cli/libs/exec" + "github.com/databricks/cli/libs/snapshot" + "github.com/databricks/databricks-sdk-go/service/apps" + "github.com/databricks/databricks-sdk-go/service/catalog" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/databricks/databricks-sdk-go/service/dashboards" + "github.com/databricks/databricks-sdk-go/service/database" + "github.com/databricks/databricks-sdk-go/service/iam" + "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/ml" + "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/postgres" + "github.com/databricks/databricks-sdk-go/service/serving" + "github.com/databricks/databricks-sdk-go/service/sql" + "github.com/databricks/databricks-sdk-go/service/vectorsearch" + "github.com/databricks/databricks-sdk-go/service/workspace" +) + +func stringsConfigRoot(c *stringsWalk, n *Node, v *config.Root) { + if n.isRef() { + return + } + if !n.IsRef("variables") { + if len(v.Variables) > 0 { + cn := n.Child("variables") + for _, k := range sortedKeys(v.Variables) { + if x := v.Variables[k]; x != nil { + ch := cn.Child(string(k)) + stringsVariableVariable(c, &ch, x) + } + } + } + } + if !n.IsRef("bundle") { + { + ch := n.Child("bundle") + stringsConfigBundle(c, &ch, &v.Bundle) + } + } + if !n.IsRef("include") { + if len(v.Include) > 0 { + cn := n.Child("include") + for i := range v.Include { + if s := string(v.Include[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Include[i] = (r) + } + } + } + } + } + if !n.IsRef("workspace") { + { + ch := n.Child("workspace") + stringsConfigWorkspace(c, &ch, &v.Workspace) + } + } + if !n.IsRef("artifacts") { + if len(v.Artifacts) > 0 { + cn := n.Child("artifacts") + for _, k := range sortedKeys(v.Artifacts) { + if x := v.Artifacts[k]; x != nil { + ch := cn.Child(string(k)) + stringsConfigArtifact(c, &ch, x) + } + } + } + } + if !c.skipResources && !n.IsRef("resources") { + { + ch := n.Child("resources") + stringsConfigResources(c, &ch, &v.Resources) + } + } + if !n.IsRef("targets") { + if len(v.Targets) > 0 { + cn := n.Child("targets") + for _, k := range sortedKeys(v.Targets) { + if x := v.Targets[k]; x != nil { + ch := cn.Child(string(k)) + stringsConfigTarget(c, &ch, x) + } + } + } + } + if !n.IsRef("environments") { + if len(v.Environments) > 0 { + cn := n.Child("environments") + for _, k := range sortedKeys(v.Environments) { + if x := v.Environments[k]; x != nil { + ch := cn.Child(string(k)) + stringsConfigTarget(c, &ch, x) + } + } + } + } + if !n.IsRef("sync") { + { + ch := n.Child("sync") + stringsConfigSync(c, &ch, &v.Sync) + } + } + if !n.IsRef("run_as") { + if x := v.RunAs; x != nil { + ch := n.Child("run_as") + stringsJobsJobRunAs(c, &ch, x) + } + } + if !n.IsRef("presets") { + { + ch := n.Child("presets") + stringsConfigPresets(c, &ch, &v.Presets) + } + } + if !n.IsRef("experimental") { + if x := v.Experimental; x != nil { + ch := n.Child("experimental") + stringsConfigExperimental(c, &ch, x) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if !n.IsRef("__locations") { + if x := v.Locations; x != nil { + ch := n.Child("__locations") + stringsLoctableLocations(c, &ch, x) + } + } + if !n.IsRef("scripts") { + if len(v.Scripts) > 0 { + cn := n.Child("scripts") + for _, k := range sortedKeys(v.Scripts) { + x := v.Scripts[k] + { + ch := cn.Child(string(k)) + stringsConfigScript(c, &ch, &x) + } + v.Scripts[k] = x + } + } + } + if !n.IsRef("python") { + { + ch := n.Child("python") + stringsConfigPython(c, &ch, &v.Python) + } + } + if c.mode == EmptyFields && v.Definitions == "" { + ch := n.Child("definitions") + c.fn(&ch, "") + } + if v.Definitions != nil { + ch := n.Child("definitions") + c.any(&ch, &v.Definitions) + } +} + +func stringsVariableVariable(c *stringsWalk, n *Node, v *variable.Variable) { + if n.isRef() { + return + } + if s := string(v.Type); c.match(s) { + ch := n.Child("type") + if r, ok := c.fn(&ch, s); ok { + v.Type = variable.VariableType(r) + } + } + if c.mode == EmptyFields && v.Default == "" { + ch := n.Child("default") + c.fn(&ch, "") + } + if v.Default != nil { + ch := n.Child("default") + c.any(&ch, &v.Default) + } + if s := string(v.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.Description = (r) + } + } + if c.mode == EmptyFields && v.Value == "" { + ch := n.Child("value") + c.fn(&ch, "") + } + if v.Value != nil { + ch := n.Child("value") + c.any(&ch, &v.Value) + } + if !n.IsRef("lookup") { + if x := v.Lookup; x != nil { + ch := n.Child("lookup") + stringsVariableLookup(c, &ch, x) + } + } +} + +func stringsConfigBundle(c *stringsWalk, n *Node, v *config.Bundle) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if s := string(v.Target); c.match(s) { + ch := n.Child("target") + if r, ok := c.fn(&ch, s); ok { + v.Target = (r) + } + } + if s := string(v.Environment); c.match(s) { + ch := n.Child("environment") + if r, ok := c.fn(&ch, s); ok { + v.Environment = (r) + } + } + if !n.IsRef("terraform") { + if x := v.Terraform; x != nil { + ch := n.Child("terraform") + stringsConfigTerraform(c, &ch, x) + } + } + if !n.IsRef("git") { + { + ch := n.Child("git") + stringsConfigGit(c, &ch, &v.Git) + } + } + if s := string(v.Mode); c.match(s) { + ch := n.Child("mode") + if r, ok := c.fn(&ch, s); ok { + v.Mode = config.Mode(r) + } + } + if s := string(v.ComputeId); c.match(s) { + ch := n.Child("compute_id") + if r, ok := c.fn(&ch, s); ok { + v.ComputeId = (r) + } + } + if s := string(v.ClusterId); c.match(s) { + ch := n.Child("cluster_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterId = (r) + } + } + if s := string(v.Engine); c.match(s) { + ch := n.Child("engine") + if r, ok := c.fn(&ch, s); ok { + v.Engine = engine.EngineType(r) + } + } + if !n.IsRef("deployment") { + { + ch := n.Child("deployment") + stringsConfigDeployment(c, &ch, &v.Deployment) + } + } + if s := string(v.DatabricksCliVersion); c.match(s) { + ch := n.Child("databricks_cli_version") + if r, ok := c.fn(&ch, s); ok { + v.DatabricksCliVersion = (r) + } + } + if s := string(v.Uuid); c.match(s) { + ch := n.Child("uuid") + if r, ok := c.fn(&ch, s); ok { + v.Uuid = (r) + } + } +} + +func stringsConfigWorkspace(c *stringsWalk, n *Node, v *config.Workspace) { + if n.isRef() { + return + } + if s := string(v.Host); c.match(s) { + ch := n.Child("host") + if r, ok := c.fn(&ch, s); ok { + v.Host = (r) + } + } + if s := string(v.Profile); c.match(s) { + ch := n.Child("profile") + if r, ok := c.fn(&ch, s); ok { + v.Profile = (r) + } + } + if s := string(v.AuthType); c.match(s) { + ch := n.Child("auth_type") + if r, ok := c.fn(&ch, s); ok { + v.AuthType = (r) + } + } + if s := string(v.MetadataServiceURL); c.match(s) { + ch := n.Child("metadata_service_url") + if r, ok := c.fn(&ch, s); ok { + v.MetadataServiceURL = (r) + } + } + if s := string(v.ClientID); c.match(s) { + ch := n.Child("client_id") + if r, ok := c.fn(&ch, s); ok { + v.ClientID = (r) + } + } + if s := string(v.GoogleServiceAccount); c.match(s) { + ch := n.Child("google_service_account") + if r, ok := c.fn(&ch, s); ok { + v.GoogleServiceAccount = (r) + } + } + if s := string(v.AzureResourceID); c.match(s) { + ch := n.Child("azure_workspace_resource_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureResourceID = (r) + } + } + if s := string(v.AzureClientID); c.match(s) { + ch := n.Child("azure_client_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureClientID = (r) + } + } + if s := string(v.AzureTenantID); c.match(s) { + ch := n.Child("azure_tenant_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureTenantID = (r) + } + } + if s := string(v.AzureEnvironment); c.match(s) { + ch := n.Child("azure_environment") + if r, ok := c.fn(&ch, s); ok { + v.AzureEnvironment = (r) + } + } + if s := string(v.AzureLoginAppID); c.match(s) { + ch := n.Child("azure_login_app_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureLoginAppID = (r) + } + } + if s := string(v.AccountID); c.match(s) { + ch := n.Child("account_id") + if r, ok := c.fn(&ch, s); ok { + v.AccountID = (r) + } + } + if s := string(v.WorkspaceID); c.match(s) { + ch := n.Child("workspace_id") + if r, ok := c.fn(&ch, s); ok { + v.WorkspaceID = (r) + } + } + if !n.IsRef("current_user") { + if x := v.CurrentUser; x != nil { + ch := n.Child("current_user") + stringsConfigUser(c, &ch, x) + } + } + if s := string(v.RootPath); c.match(s) { + ch := n.Child("root_path") + if r, ok := c.fn(&ch, s); ok { + v.RootPath = (r) + } + } + if s := string(v.FilePath); c.match(s) { + ch := n.Child("file_path") + if r, ok := c.fn(&ch, s); ok { + v.FilePath = (r) + } + } + if s := string(v.ResourcePath); c.match(s) { + ch := n.Child("resource_path") + if r, ok := c.fn(&ch, s); ok { + v.ResourcePath = (r) + } + } + if s := string(v.ArtifactPath); c.match(s) { + ch := n.Child("artifact_path") + if r, ok := c.fn(&ch, s); ok { + v.ArtifactPath = (r) + } + } + if s := string(v.StatePath); c.match(s) { + ch := n.Child("state_path") + if r, ok := c.fn(&ch, s); ok { + v.StatePath = (r) + } + } + if s := string(v.SyncMode); c.match(s) { + ch := n.Child("sync_mode") + if r, ok := c.fn(&ch, s); ok { + v.SyncMode = config.SyncMode(r) + } + } +} + +func stringsConfigArtifact(c *stringsWalk, n *Node, v *config.Artifact) { + if n.isRef() { + return + } + if s := string(v.Type); c.match(s) { + ch := n.Child("type") + if r, ok := c.fn(&ch, s); ok { + v.Type = config.ArtifactType(r) + } + } + if s := string(v.Path); c.match(s) { + ch := n.Child("path") + if r, ok := c.fn(&ch, s); ok { + v.Path = (r) + } + } + if !n.IsRef("files") { + if len(v.Files) > 0 { + cn := n.Child("files") + for i := range v.Files { + { + ch := cn.Elem(i) + stringsConfigArtifactFile(c, &ch, &v.Files[i]) + } + } + } + } + if s := string(v.BuildCommand); c.match(s) { + ch := n.Child("build") + if r, ok := c.fn(&ch, s); ok { + v.BuildCommand = (r) + } + } + if s := string(v.Executable); c.match(s) { + ch := n.Child("executable") + if r, ok := c.fn(&ch, s); ok { + v.Executable = exec.ExecutableType(r) + } + } + if !n.IsRef("include") { + if len(v.Include) > 0 { + cn := n.Child("include") + for i := range v.Include { + if s := string(v.Include[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Include[i] = (r) + } + } + } + } + } + if !n.IsRef("git") { + if x := v.Git; x != nil { + ch := n.Child("git") + stringsConfigArtifactGit(c, &ch, x) + } + } +} + +func stringsConfigResources(c *stringsWalk, n *Node, v *config.Resources) { + if n.isRef() { + return + } + if !n.IsRef("jobs") { + if len(v.Jobs) > 0 { + cn := n.Child("jobs") + for _, k := range sortedKeys(v.Jobs) { + if x := v.Jobs[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesJob(c, &ch, x) + } + } + } + } + if !n.IsRef("job_runs") { + if len(v.JobRuns) > 0 { + cn := n.Child("job_runs") + for _, k := range sortedKeys(v.JobRuns) { + if x := v.JobRuns[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesJobRun(c, &ch, x) + } + } + } + } + if !n.IsRef("pipelines") { + if len(v.Pipelines) > 0 { + cn := n.Child("pipelines") + for _, k := range sortedKeys(v.Pipelines) { + if x := v.Pipelines[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPipeline(c, &ch, x) + } + } + } + } + if !n.IsRef("features") { + if len(v.Features) > 0 { + cn := n.Child("features") + for _, k := range sortedKeys(v.Features) { + if x := v.Features[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesFeature(c, &ch, x) + } + } + } + } + if !n.IsRef("models") { + if len(v.Models) > 0 { + cn := n.Child("models") + for _, k := range sortedKeys(v.Models) { + if x := v.Models[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesMlflowModel(c, &ch, x) + } + } + } + } + if !n.IsRef("experiments") { + if len(v.Experiments) > 0 { + cn := n.Child("experiments") + for _, k := range sortedKeys(v.Experiments) { + if x := v.Experiments[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesMlflowExperiment(c, &ch, x) + } + } + } + } + if !n.IsRef("model_serving_endpoints") { + if len(v.ModelServingEndpoints) > 0 { + cn := n.Child("model_serving_endpoints") + for _, k := range sortedKeys(v.ModelServingEndpoints) { + if x := v.ModelServingEndpoints[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesModelServingEndpoint(c, &ch, x) + } + } + } + } + if !n.IsRef("model_services") { + if len(v.ModelServices) > 0 { + cn := n.Child("model_services") + for _, k := range sortedKeys(v.ModelServices) { + if x := v.ModelServices[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesModelService(c, &ch, x) + } + } + } + } + if !n.IsRef("mcp_services") { + if len(v.McpServices) > 0 { + cn := n.Child("mcp_services") + for _, k := range sortedKeys(v.McpServices) { + if x := v.McpServices[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesMcpService(c, &ch, x) + } + } + } + } + if !n.IsRef("model_provider_services") { + if len(v.ModelProviderServices) > 0 { + cn := n.Child("model_provider_services") + for _, k := range sortedKeys(v.ModelProviderServices) { + if x := v.ModelProviderServices[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesModelProviderService(c, &ch, x) + } + } + } + } + if !n.IsRef("registered_models") { + if len(v.RegisteredModels) > 0 { + cn := n.Child("registered_models") + for _, k := range sortedKeys(v.RegisteredModels) { + if x := v.RegisteredModels[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesRegisteredModel(c, &ch, x) + } + } + } + } + if !n.IsRef("quality_monitors") { + if len(v.QualityMonitors) > 0 { + cn := n.Child("quality_monitors") + for _, k := range sortedKeys(v.QualityMonitors) { + if x := v.QualityMonitors[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesQualityMonitor(c, &ch, x) + } + } + } + } + if !n.IsRef("catalogs") { + if len(v.Catalogs) > 0 { + cn := n.Child("catalogs") + for _, k := range sortedKeys(v.Catalogs) { + if x := v.Catalogs[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesCatalog(c, &ch, x) + } + } + } + } + if !n.IsRef("schemas") { + if len(v.Schemas) > 0 { + cn := n.Child("schemas") + for _, k := range sortedKeys(v.Schemas) { + if x := v.Schemas[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesSchema(c, &ch, x) + } + } + } + } + if !n.IsRef("volumes") { + if len(v.Volumes) > 0 { + cn := n.Child("volumes") + for _, k := range sortedKeys(v.Volumes) { + if x := v.Volumes[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesVolume(c, &ch, x) + } + } + } + } + if !n.IsRef("external_locations") { + if len(v.ExternalLocations) > 0 { + cn := n.Child("external_locations") + for _, k := range sortedKeys(v.ExternalLocations) { + if x := v.ExternalLocations[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesExternalLocation(c, &ch, x) + } + } + } + } + if !n.IsRef("clusters") { + if len(v.Clusters) > 0 { + cn := n.Child("clusters") + for _, k := range sortedKeys(v.Clusters) { + if x := v.Clusters[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesCluster(c, &ch, x) + } + } + } + } + if !n.IsRef("dashboards") { + if len(v.Dashboards) > 0 { + cn := n.Child("dashboards") + for _, k := range sortedKeys(v.Dashboards) { + if x := v.Dashboards[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesDashboard(c, &ch, x) + } + } + } + } + if !n.IsRef("genie_spaces") { + if len(v.GenieSpaces) > 0 { + cn := n.Child("genie_spaces") + for _, k := range sortedKeys(v.GenieSpaces) { + if x := v.GenieSpaces[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesGenieSpace(c, &ch, x) + } + } + } + } + if !n.IsRef("apps") { + if len(v.Apps) > 0 { + cn := n.Child("apps") + for _, k := range sortedKeys(v.Apps) { + if x := v.Apps[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesApp(c, &ch, x) + } + } + } + } + if !n.IsRef("secret_scopes") { + if len(v.SecretScopes) > 0 { + cn := n.Child("secret_scopes") + for _, k := range sortedKeys(v.SecretScopes) { + if x := v.SecretScopes[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesSecretScope(c, &ch, x) + } + } + } + } + if !n.IsRef("alerts") { + if len(v.Alerts) > 0 { + cn := n.Child("alerts") + for _, k := range sortedKeys(v.Alerts) { + if x := v.Alerts[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesAlert(c, &ch, x) + } + } + } + } + if !n.IsRef("sql_warehouses") { + if len(v.SqlWarehouses) > 0 { + cn := n.Child("sql_warehouses") + for _, k := range sortedKeys(v.SqlWarehouses) { + if x := v.SqlWarehouses[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesSqlWarehouse(c, &ch, x) + } + } + } + } + if !n.IsRef("database_instances") { + if len(v.DatabaseInstances) > 0 { + cn := n.Child("database_instances") + for _, k := range sortedKeys(v.DatabaseInstances) { + if x := v.DatabaseInstances[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesDatabaseInstance(c, &ch, x) + } + } + } + } + if !n.IsRef("database_catalogs") { + if len(v.DatabaseCatalogs) > 0 { + cn := n.Child("database_catalogs") + for _, k := range sortedKeys(v.DatabaseCatalogs) { + if x := v.DatabaseCatalogs[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesDatabaseCatalog(c, &ch, x) + } + } + } + } + if !n.IsRef("synced_database_tables") { + if len(v.SyncedDatabaseTables) > 0 { + cn := n.Child("synced_database_tables") + for _, k := range sortedKeys(v.SyncedDatabaseTables) { + if x := v.SyncedDatabaseTables[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesSyncedDatabaseTable(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_projects") { + if len(v.PostgresProjects) > 0 { + cn := n.Child("postgres_projects") + for _, k := range sortedKeys(v.PostgresProjects) { + if x := v.PostgresProjects[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresProject(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_branches") { + if len(v.PostgresBranches) > 0 { + cn := n.Child("postgres_branches") + for _, k := range sortedKeys(v.PostgresBranches) { + if x := v.PostgresBranches[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresBranch(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_endpoints") { + if len(v.PostgresEndpoints) > 0 { + cn := n.Child("postgres_endpoints") + for _, k := range sortedKeys(v.PostgresEndpoints) { + if x := v.PostgresEndpoints[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresEndpoint(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_catalogs") { + if len(v.PostgresCatalogs) > 0 { + cn := n.Child("postgres_catalogs") + for _, k := range sortedKeys(v.PostgresCatalogs) { + if x := v.PostgresCatalogs[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresCatalog(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_databases") { + if len(v.PostgresDatabases) > 0 { + cn := n.Child("postgres_databases") + for _, k := range sortedKeys(v.PostgresDatabases) { + if x := v.PostgresDatabases[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresDatabase(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_roles") { + if len(v.PostgresRoles) > 0 { + cn := n.Child("postgres_roles") + for _, k := range sortedKeys(v.PostgresRoles) { + if x := v.PostgresRoles[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresRole(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_synced_tables") { + if len(v.PostgresSyncedTables) > 0 { + cn := n.Child("postgres_synced_tables") + for _, k := range sortedKeys(v.PostgresSyncedTables) { + if x := v.PostgresSyncedTables[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresSyncedTable(c, &ch, x) + } + } + } + } + if !n.IsRef("postgres_snapshot_schedules") { + if len(v.PostgresSnapshotSchedules) > 0 { + cn := n.Child("postgres_snapshot_schedules") + for _, k := range sortedKeys(v.PostgresSnapshotSchedules) { + if x := v.PostgresSnapshotSchedules[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesPostgresSnapshotSchedule(c, &ch, x) + } + } + } + } + if !n.IsRef("vector_search_endpoints") { + if len(v.VectorSearchEndpoints) > 0 { + cn := n.Child("vector_search_endpoints") + for _, k := range sortedKeys(v.VectorSearchEndpoints) { + if x := v.VectorSearchEndpoints[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesVectorSearchEndpoint(c, &ch, x) + } + } + } + } + if !n.IsRef("vector_search_indexes") { + if len(v.VectorSearchIndexes) > 0 { + cn := n.Child("vector_search_indexes") + for _, k := range sortedKeys(v.VectorSearchIndexes) { + if x := v.VectorSearchIndexes[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesVectorSearchIndex(c, &ch, x) + } + } + } + } + if !n.IsRef("instance_pools") { + if len(v.InstancePools) > 0 { + cn := n.Child("instance_pools") + for _, k := range sortedKeys(v.InstancePools) { + if x := v.InstancePools[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesInstancePool(c, &ch, x) + } + } + } + } + if !n.IsRef("secrets") { + if len(v.Secrets) > 0 { + cn := n.Child("secrets") + for _, k := range sortedKeys(v.Secrets) { + if x := v.Secrets[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesSecret(c, &ch, x) + } + } + } + } + if !n.IsRef("cluster_policies") { + if len(v.ClusterPolicies) > 0 { + cn := n.Child("cluster_policies") + for _, k := range sortedKeys(v.ClusterPolicies) { + if x := v.ClusterPolicies[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesClusterPolicy(c, &ch, x) + } + } + } + } + if !n.IsRef("internal_immutable_snapshots") { + if len(v.Snapshots) > 0 { + cn := n.Child("internal_immutable_snapshots") + for _, k := range sortedKeys(v.Snapshots) { + if x := v.Snapshots[k]; x != nil { + ch := cn.Child(string(k)) + stringsResourcesSnapshot(c, &ch, x) + } + } + } + } +} + +func stringsConfigTarget(c *stringsWalk, n *Node, v *config.Target) { + if n.isRef() { + return + } + if s := string(v.Mode); c.match(s) { + ch := n.Child("mode") + if r, ok := c.fn(&ch, s); ok { + v.Mode = config.Mode(r) + } + } + if !n.IsRef("presets") { + { + ch := n.Child("presets") + stringsConfigPresets(c, &ch, &v.Presets) + } + } + if s := string(v.ComputeId); c.match(s) { + ch := n.Child("compute_id") + if r, ok := c.fn(&ch, s); ok { + v.ComputeId = (r) + } + } + if s := string(v.ClusterId); c.match(s) { + ch := n.Child("cluster_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterId = (r) + } + } + if !n.IsRef("bundle") { + if x := v.Bundle; x != nil { + ch := n.Child("bundle") + stringsConfigBundle(c, &ch, x) + } + } + if !n.IsRef("workspace") { + if x := v.Workspace; x != nil { + ch := n.Child("workspace") + stringsConfigWorkspace(c, &ch, x) + } + } + if !n.IsRef("artifacts") { + if len(v.Artifacts) > 0 { + cn := n.Child("artifacts") + for _, k := range sortedKeys(v.Artifacts) { + if x := v.Artifacts[k]; x != nil { + ch := cn.Child(string(k)) + stringsConfigArtifact(c, &ch, x) + } + } + } + } + if !n.IsRef("resources") { + if x := v.Resources; x != nil { + ch := n.Child("resources") + stringsConfigResources(c, &ch, x) + } + } + if !n.IsRef("variables") { + if len(v.Variables) > 0 { + cn := n.Child("variables") + for _, k := range sortedKeys(v.Variables) { + if x := v.Variables[k]; x != nil { + ch := cn.Child(string(k)) + stringsVariableTargetVariable(c, &ch, x) + } + } + } + } + if !n.IsRef("git") { + { + ch := n.Child("git") + stringsConfigGit(c, &ch, &v.Git) + } + } + if !n.IsRef("run_as") { + if x := v.RunAs; x != nil { + ch := n.Child("run_as") + stringsJobsJobRunAs(c, &ch, x) + } + } + if !n.IsRef("sync") { + if x := v.Sync; x != nil { + ch := n.Child("sync") + stringsConfigSync(c, &ch, x) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } +} + +func stringsConfigSync(c *stringsWalk, n *Node, v *config.Sync) { + if n.isRef() { + return + } + if !n.IsRef("paths") { + if len(v.Paths) > 0 { + cn := n.Child("paths") + for i := range v.Paths { + if s := string(v.Paths[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Paths[i] = (r) + } + } + } + } + } + if !n.IsRef("include") { + if len(v.Include) > 0 { + cn := n.Child("include") + for i := range v.Include { + if s := string(v.Include[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Include[i] = (r) + } + } + } + } + } + if !n.IsRef("exclude") { + if len(v.Exclude) > 0 { + cn := n.Child("exclude") + for i := range v.Exclude { + if s := string(v.Exclude[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Exclude[i] = (r) + } + } + } + } + } +} + +func stringsJobsJobRunAs(c *stringsWalk, n *Node, v *jobs.JobRunAs) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.GroupName == "" && Forced(v.ForceSendFields, "GroupName") { + ch := n.Child("group_name") + c.fn(&ch, "") + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } + if c.mode == EmptyFields && v.ServicePrincipalName == "" && Forced(v.ForceSendFields, "ServicePrincipalName") { + ch := n.Child("service_principal_name") + c.fn(&ch, "") + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if c.mode == EmptyFields && v.UserName == "" && Forced(v.ForceSendFields, "UserName") { + ch := n.Child("user_name") + c.fn(&ch, "") + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } +} + +func stringsConfigPresets(c *stringsWalk, n *Node, v *config.Presets) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.NamePrefix == "" && Forced(v.ForceSendFields, "NamePrefix") { + ch := n.Child("name_prefix") + c.fn(&ch, "") + } + if s := string(v.NamePrefix); c.match(s) { + ch := n.Child("name_prefix") + if r, ok := c.fn(&ch, s); ok { + v.NamePrefix = (r) + } + } + if c.mode == EmptyFields && v.TriggerPauseStatus == "" && Forced(v.ForceSendFields, "TriggerPauseStatus") { + ch := n.Child("trigger_pause_status") + c.fn(&ch, "") + } + if s := string(v.TriggerPauseStatus); c.match(s) { + ch := n.Child("trigger_pause_status") + if r, ok := c.fn(&ch, s); ok { + v.TriggerPauseStatus = (r) + } + } + if !n.IsRef("tags") { + if len(v.Tags) > 0 { + cn := n.Child("tags") + for _, k := range sortedKeys(v.Tags) { + if s := string(v.Tags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Tags[k] = (r) + } + } + } + } + } +} + +func stringsConfigExperimental(c *stringsWalk, n *Node, v *config.Experimental) { + if n.isRef() { + return + } + if !n.IsRef("scripts") { + if len(v.Scripts) > 0 { + cn := n.Child("scripts") + for _, k := range sortedKeys(v.Scripts) { + if s := string(v.Scripts[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Scripts[k] = config.Command(r) + } + } + } + } + } + if !n.IsRef("python") { + { + ch := n.Child("python") + stringsConfigPython(c, &ch, &v.Python) + } + } +} + +func stringsResourcesPermission(c *stringsWalk, n *Node, v *resources.Permission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = iam.PermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsLoctableLocations(c *stringsWalk, n *Node, v *loctable.Locations) { + if n.isRef() { + return + } + if !n.IsRef("files") { + if len(v.Files) > 0 { + cn := n.Child("files") + for i := range v.Files { + if s := string(v.Files[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Files[i] = (r) + } + } + } + } + } +} + +func stringsConfigScript(c *stringsWalk, n *Node, v *config.Script) { + if n.isRef() { + return + } + if s := string(v.Content); c.match(s) { + ch := n.Child("content") + if r, ok := c.fn(&ch, s); ok { + v.Content = (r) + } + } + if !n.IsRef("env") { + if len(v.Env) > 0 { + cn := n.Child("env") + for _, k := range sortedKeys(v.Env) { + if s := string(v.Env[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Env[k] = (r) + } + } + } + } + } +} + +func stringsConfigPython(c *stringsWalk, n *Node, v *config.Python) { + if n.isRef() { + return + } + if !n.IsRef("resources") { + if len(v.Resources) > 0 { + cn := n.Child("resources") + for i := range v.Resources { + if s := string(v.Resources[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Resources[i] = (r) + } + } + } + } + } + if !n.IsRef("mutators") { + if len(v.Mutators) > 0 { + cn := n.Child("mutators") + for i := range v.Mutators { + if s := string(v.Mutators[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Mutators[i] = (r) + } + } + } + } + } + if s := string(v.VEnvPath); c.match(s) { + ch := n.Child("venv_path") + if r, ok := c.fn(&ch, s); ok { + v.VEnvPath = (r) + } + } +} + +func stringsVariableLookup(c *stringsWalk, n *Node, v *variable.Lookup) { + if n.isRef() { + return + } + if s := string(v.Alert); c.match(s) { + ch := n.Child("alert") + if r, ok := c.fn(&ch, s); ok { + v.Alert = (r) + } + } + if s := string(v.ClusterPolicy); c.match(s) { + ch := n.Child("cluster_policy") + if r, ok := c.fn(&ch, s); ok { + v.ClusterPolicy = (r) + } + } + if s := string(v.Cluster); c.match(s) { + ch := n.Child("cluster") + if r, ok := c.fn(&ch, s); ok { + v.Cluster = (r) + } + } + if s := string(v.Dashboard); c.match(s) { + ch := n.Child("dashboard") + if r, ok := c.fn(&ch, s); ok { + v.Dashboard = (r) + } + } + if s := string(v.InstancePool); c.match(s) { + ch := n.Child("instance_pool") + if r, ok := c.fn(&ch, s); ok { + v.InstancePool = (r) + } + } + if s := string(v.Job); c.match(s) { + ch := n.Child("job") + if r, ok := c.fn(&ch, s); ok { + v.Job = (r) + } + } + if s := string(v.Metastore); c.match(s) { + ch := n.Child("metastore") + if r, ok := c.fn(&ch, s); ok { + v.Metastore = (r) + } + } + if s := string(v.NotificationDestination); c.match(s) { + ch := n.Child("notification_destination") + if r, ok := c.fn(&ch, s); ok { + v.NotificationDestination = (r) + } + } + if s := string(v.Pipeline); c.match(s) { + ch := n.Child("pipeline") + if r, ok := c.fn(&ch, s); ok { + v.Pipeline = (r) + } + } + if s := string(v.Query); c.match(s) { + ch := n.Child("query") + if r, ok := c.fn(&ch, s); ok { + v.Query = (r) + } + } + if s := string(v.ServicePrincipal); c.match(s) { + ch := n.Child("service_principal") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipal = (r) + } + } + if s := string(v.Warehouse); c.match(s) { + ch := n.Child("warehouse") + if r, ok := c.fn(&ch, s); ok { + v.Warehouse = (r) + } + } +} + +func stringsConfigTerraform(c *stringsWalk, n *Node, v *config.Terraform) { + if n.isRef() { + return + } + if s := string(v.ExecPath); c.match(s) { + ch := n.Child("exec_path") + if r, ok := c.fn(&ch, s); ok { + v.ExecPath = (r) + } + } +} + +func stringsConfigGit(c *stringsWalk, n *Node, v *config.Git) { + if n.isRef() { + return + } + if s := string(v.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.Branch = (r) + } + } + if s := string(v.OriginURL); c.match(s) { + ch := n.Child("origin_url") + if r, ok := c.fn(&ch, s); ok { + v.OriginURL = (r) + } + } + if s := string(v.Commit); c.match(s) { + ch := n.Child("commit") + if r, ok := c.fn(&ch, s); ok { + v.Commit = (r) + } + } + if s := string(v.BundleRootPath); c.match(s) { + ch := n.Child("bundle_root_path") + if r, ok := c.fn(&ch, s); ok { + v.BundleRootPath = (r) + } + } + if s := string(v.ActualBranch); c.match(s) { + ch := n.Child("actual_branch") + if r, ok := c.fn(&ch, s); ok { + v.ActualBranch = (r) + } + } +} + +func stringsConfigDeployment(c *stringsWalk, n *Node, v *config.Deployment) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DeploymentID == "" && Forced(v.ForceSendFields, "DeploymentID") { + ch := n.Child("deployment_id") + c.fn(&ch, "") + } + if s := string(v.DeploymentID); c.match(s) { + ch := n.Child("deployment_id") + if r, ok := c.fn(&ch, s); ok { + v.DeploymentID = (r) + } + } +} + +func stringsConfigUser(c *stringsWalk, n *Node, v *config.User) { + if n.isRef() { + return + } + if s := string(v.ShortName); c.match(s) { + ch := n.Child("short_name") + if r, ok := c.fn(&ch, s); ok { + v.ShortName = (r) + } + } + if s := string(v.DomainFriendlyName); c.match(s) { + ch := n.Child("domain_friendly_name") + if r, ok := c.fn(&ch, s); ok { + v.DomainFriendlyName = (r) + } + } + if v.User != nil { + if c.mode == EmptyFields && v.User.DisplayName == "" && Forced(v.User.ForceSendFields, "DisplayName") { + ch := n.Child("displayName") + c.fn(&ch, "") + } + if s := string(v.User.DisplayName); c.match(s) { + ch := n.Child("displayName") + if r, ok := c.fn(&ch, s); ok { + v.User.DisplayName = (r) + } + } + } + if v.User != nil && !n.IsRef("emails") { + if len(v.User.Emails) > 0 { + cn := n.Child("emails") + for i := range v.User.Emails { + { + ch := cn.Elem(i) + stringsIamComplexValue(c, &ch, &v.User.Emails[i]) + } + } + } + } + if v.User != nil && !n.IsRef("entitlements") { + if len(v.User.Entitlements) > 0 { + cn := n.Child("entitlements") + for i := range v.User.Entitlements { + { + ch := cn.Elem(i) + stringsIamComplexValue(c, &ch, &v.User.Entitlements[i]) + } + } + } + } + if v.User != nil { + if c.mode == EmptyFields && v.User.ExternalId == "" && Forced(v.User.ForceSendFields, "ExternalId") { + ch := n.Child("externalId") + c.fn(&ch, "") + } + if s := string(v.User.ExternalId); c.match(s) { + ch := n.Child("externalId") + if r, ok := c.fn(&ch, s); ok { + v.User.ExternalId = (r) + } + } + } + if v.User != nil && !n.IsRef("groups") { + if len(v.User.Groups) > 0 { + cn := n.Child("groups") + for i := range v.User.Groups { + { + ch := cn.Elem(i) + stringsIamComplexValue(c, &ch, &v.User.Groups[i]) + } + } + } + } + if v.User != nil { + if c.mode == EmptyFields && v.User.Id == "" && Forced(v.User.ForceSendFields, "Id") { + ch := n.Child("id") + c.fn(&ch, "") + } + if s := string(v.User.Id); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.User.Id = (r) + } + } + } + if v.User != nil && !n.IsRef("name") { + if x := v.User.Name; x != nil { + ch := n.Child("name") + stringsIamName(c, &ch, x) + } + } + if v.User != nil && !n.IsRef("roles") { + if len(v.User.Roles) > 0 { + cn := n.Child("roles") + for i := range v.User.Roles { + { + ch := cn.Elem(i) + stringsIamComplexValue(c, &ch, &v.User.Roles[i]) + } + } + } + } + if v.User != nil && !n.IsRef("schemas") { + if len(v.User.Schemas) > 0 { + cn := n.Child("schemas") + for i := range v.User.Schemas { + if s := string(v.User.Schemas[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.User.Schemas[i] = iam.UserSchema(r) + } + } + } + } + } + if v.User != nil { + if c.mode == EmptyFields && v.User.UserName == "" && Forced(v.User.ForceSendFields, "UserName") { + ch := n.Child("userName") + c.fn(&ch, "") + } + if s := string(v.User.UserName); c.match(s) { + ch := n.Child("userName") + if r, ok := c.fn(&ch, s); ok { + v.User.UserName = (r) + } + } + } +} + +func stringsConfigArtifactFile(c *stringsWalk, n *Node, v *config.ArtifactFile) { + if n.isRef() { + return + } + if s := string(v.Source); c.match(s) { + ch := n.Child("source") + if r, ok := c.fn(&ch, s); ok { + v.Source = (r) + } + } + if s := string(v.Patched); c.match(s) { + ch := n.Child("patched") + if r, ok := c.fn(&ch, s); ok { + v.Patched = (r) + } + } + if s := string(v.RemotePath); c.match(s) { + ch := n.Child("remote_path") + if r, ok := c.fn(&ch, s); ok { + v.RemotePath = (r) + } + } +} + +func stringsConfigArtifactGit(c *stringsWalk, n *Node, v *config.ArtifactGit) { + if n.isRef() { + return + } + if s := string(v.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.Branch = (r) + } + } + if s := string(v.Commit); c.match(s) { + ch := n.Child("commit") + if r, ok := c.fn(&ch, s); ok { + v.Commit = (r) + } + } +} + +func stringsResourcesJob(c *stringsWalk, n *Node, v *resources.Job) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesJobPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.JobSettings.BudgetPolicyId == "" && Forced(v.JobSettings.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.JobSettings.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.BudgetPolicyId = (r) + } + } + if !n.IsRef("continuous") { + if x := v.JobSettings.Continuous; x != nil { + ch := n.Child("continuous") + stringsJobsContinuous(c, &ch, x) + } + } + if !n.IsRef("deployment") { + if x := v.JobSettings.Deployment; x != nil { + ch := n.Child("deployment") + stringsJobsJobDeployment(c, &ch, x) + } + } + if c.mode == EmptyFields && v.JobSettings.Description == "" && Forced(v.JobSettings.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.JobSettings.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.Description = (r) + } + } + if c.mode == EmptyFields && v.JobSettings.DurationTarget == "" && Forced(v.JobSettings.ForceSendFields, "DurationTarget") { + ch := n.Child("duration_target") + c.fn(&ch, "") + } + if s := string(v.JobSettings.DurationTarget); c.match(s) { + ch := n.Child("duration_target") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.DurationTarget = (r) + } + } + if c.mode == EmptyFields && v.JobSettings.EditMode == "" && Forced(v.JobSettings.ForceSendFields, "EditMode") { + ch := n.Child("edit_mode") + c.fn(&ch, "") + } + if s := string(v.JobSettings.EditMode); c.match(s) { + ch := n.Child("edit_mode") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.EditMode = jobs.JobEditMode(r) + } + } + if !n.IsRef("email_notifications") { + if x := v.JobSettings.EmailNotifications; x != nil { + ch := n.Child("email_notifications") + stringsJobsJobEmailNotifications(c, &ch, x) + } + } + if !n.IsRef("environment_variables") { + if len(v.JobSettings.EnvironmentVariables) > 0 { + cn := n.Child("environment_variables") + for i := range v.JobSettings.EnvironmentVariables { + { + ch := cn.Elem(i) + stringsJobsJobEnvironmentVariables(c, &ch, &v.JobSettings.EnvironmentVariables[i]) + } + } + } + } + if !n.IsRef("environments") { + if len(v.JobSettings.Environments) > 0 { + cn := n.Child("environments") + for i := range v.JobSettings.Environments { + { + ch := cn.Elem(i) + stringsJobsJobEnvironment(c, &ch, &v.JobSettings.Environments[i]) + } + } + } + } + if c.mode == EmptyFields && v.JobSettings.Format == "" && Forced(v.JobSettings.ForceSendFields, "Format") { + ch := n.Child("format") + c.fn(&ch, "") + } + if s := string(v.JobSettings.Format); c.match(s) { + ch := n.Child("format") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.Format = jobs.Format(r) + } + } + if !n.IsRef("git_source") { + if x := v.JobSettings.GitSource; x != nil { + ch := n.Child("git_source") + stringsJobsGitSource(c, &ch, x) + } + } + if !n.IsRef("health") { + if x := v.JobSettings.Health; x != nil { + ch := n.Child("health") + stringsJobsJobsHealthRules(c, &ch, x) + } + } + if !n.IsRef("job_clusters") { + if len(v.JobSettings.JobClusters) > 0 { + cn := n.Child("job_clusters") + for i := range v.JobSettings.JobClusters { + { + ch := cn.Elem(i) + stringsJobsJobCluster(c, &ch, &v.JobSettings.JobClusters[i]) + } + } + } + } + if c.mode == EmptyFields && v.JobSettings.Name == "" && Forced(v.JobSettings.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.JobSettings.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.Name = (r) + } + } + if !n.IsRef("parameters") { + if len(v.JobSettings.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.JobSettings.Parameters { + { + ch := cn.Elem(i) + stringsJobsJobParameterDefinition(c, &ch, &v.JobSettings.Parameters[i]) + } + } + } + } + if c.mode == EmptyFields && v.JobSettings.ParentPath == "" && Forced(v.JobSettings.ForceSendFields, "ParentPath") { + ch := n.Child("parent_path") + c.fn(&ch, "") + } + if s := string(v.JobSettings.ParentPath); c.match(s) { + ch := n.Child("parent_path") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.ParentPath = (r) + } + } + if c.mode == EmptyFields && v.JobSettings.PerformanceTarget == "" && Forced(v.JobSettings.ForceSendFields, "PerformanceTarget") { + ch := n.Child("performance_target") + c.fn(&ch, "") + } + if s := string(v.JobSettings.PerformanceTarget); c.match(s) { + ch := n.Child("performance_target") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.PerformanceTarget = jobs.PerformanceTarget(r) + } + } + if !n.IsRef("run_as") { + if x := v.JobSettings.RunAs; x != nil { + ch := n.Child("run_as") + stringsJobsJobRunAs(c, &ch, x) + } + } + if !n.IsRef("schedule") { + if x := v.JobSettings.Schedule; x != nil { + ch := n.Child("schedule") + stringsJobsCronSchedule(c, &ch, x) + } + } + if !n.IsRef("tags") { + if len(v.JobSettings.Tags) > 0 { + cn := n.Child("tags") + for _, k := range sortedKeys(v.JobSettings.Tags) { + if s := string(v.JobSettings.Tags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.Tags[k] = (r) + } + } + } + } + } + if !n.IsRef("tasks") { + if len(v.JobSettings.Tasks) > 0 { + cn := n.Child("tasks") + for i := range v.JobSettings.Tasks { + { + ch := cn.Elem(i) + stringsJobsTask(c, &ch, &v.JobSettings.Tasks[i]) + } + } + } + } + if !n.IsRef("trigger") { + if x := v.JobSettings.Trigger; x != nil { + ch := n.Child("trigger") + stringsJobsTriggerSettings(c, &ch, x) + } + } + if !n.IsRef("triggers") { + if len(v.JobSettings.Triggers) > 0 { + cn := n.Child("triggers") + for i := range v.JobSettings.Triggers { + { + ch := cn.Elem(i) + stringsJobsTriggerConfiguration(c, &ch, &v.JobSettings.Triggers[i]) + } + } + } + } + if c.mode == EmptyFields && v.JobSettings.UsagePolicyId == "" && Forced(v.JobSettings.ForceSendFields, "UsagePolicyId") { + ch := n.Child("usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.JobSettings.UsagePolicyId); c.match(s) { + ch := n.Child("usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.JobSettings.UsagePolicyId = (r) + } + } + if !n.IsRef("webhook_notifications") { + if x := v.JobSettings.WebhookNotifications; x != nil { + ch := n.Child("webhook_notifications") + stringsJobsWebhookNotifications(c, &ch, x) + } + } +} + +func stringsResourcesJobRun(c *stringsWalk, n *Node, v *resources.JobRun) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("lifecycle") { + if x := v.Lifecycle; x != nil { + ch := n.Child("lifecycle") + stringsResourcesJobRunLifecycle(c, &ch, x) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("dbt_commands") { + if len(v.RunNow.DbtCommands) > 0 { + cn := n.Child("dbt_commands") + for i := range v.RunNow.DbtCommands { + if s := string(v.RunNow.DbtCommands[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.DbtCommands[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.RunNow.DurationTarget == "" && Forced(v.RunNow.ForceSendFields, "DurationTarget") { + ch := n.Child("duration_target") + c.fn(&ch, "") + } + if s := string(v.RunNow.DurationTarget); c.match(s) { + ch := n.Child("duration_target") + if r, ok := c.fn(&ch, s); ok { + v.RunNow.DurationTarget = (r) + } + } + if c.mode == EmptyFields && v.RunNow.IdempotencyToken == "" && Forced(v.RunNow.ForceSendFields, "IdempotencyToken") { + ch := n.Child("idempotency_token") + c.fn(&ch, "") + } + if s := string(v.RunNow.IdempotencyToken); c.match(s) { + ch := n.Child("idempotency_token") + if r, ok := c.fn(&ch, s); ok { + v.RunNow.IdempotencyToken = (r) + } + } + if !n.IsRef("jar_params") { + if len(v.RunNow.JarParams) > 0 { + cn := n.Child("jar_params") + for i := range v.RunNow.JarParams { + if s := string(v.RunNow.JarParams[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.JarParams[i] = (r) + } + } + } + } + } + if !n.IsRef("job_parameters") { + if len(v.RunNow.JobParameters) > 0 { + cn := n.Child("job_parameters") + for _, k := range sortedKeys(v.RunNow.JobParameters) { + if s := string(v.RunNow.JobParameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.JobParameters[k] = (r) + } + } + } + } + } + if !n.IsRef("notebook_params") { + if len(v.RunNow.NotebookParams) > 0 { + cn := n.Child("notebook_params") + for _, k := range sortedKeys(v.RunNow.NotebookParams) { + if s := string(v.RunNow.NotebookParams[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.NotebookParams[k] = (r) + } + } + } + } + } + if !n.IsRef("only") { + if len(v.RunNow.Only) > 0 { + cn := n.Child("only") + for i := range v.RunNow.Only { + if s := string(v.RunNow.Only[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.Only[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.RunNow.PerformanceTarget == "" && Forced(v.RunNow.ForceSendFields, "PerformanceTarget") { + ch := n.Child("performance_target") + c.fn(&ch, "") + } + if s := string(v.RunNow.PerformanceTarget); c.match(s) { + ch := n.Child("performance_target") + if r, ok := c.fn(&ch, s); ok { + v.RunNow.PerformanceTarget = jobs.PerformanceTarget(r) + } + } + if !n.IsRef("pipeline_params") { + if x := v.RunNow.PipelineParams; x != nil { + ch := n.Child("pipeline_params") + stringsJobsPipelineParams(c, &ch, x) + } + } + if !n.IsRef("python_named_params") { + if len(v.RunNow.PythonNamedParams) > 0 { + cn := n.Child("python_named_params") + for _, k := range sortedKeys(v.RunNow.PythonNamedParams) { + if s := string(v.RunNow.PythonNamedParams[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.PythonNamedParams[k] = (r) + } + } + } + } + } + if !n.IsRef("python_params") { + if len(v.RunNow.PythonParams) > 0 { + cn := n.Child("python_params") + for i := range v.RunNow.PythonParams { + if s := string(v.RunNow.PythonParams[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.PythonParams[i] = (r) + } + } + } + } + } + if !n.IsRef("spark_submit_params") { + if len(v.RunNow.SparkSubmitParams) > 0 { + cn := n.Child("spark_submit_params") + for i := range v.RunNow.SparkSubmitParams { + if s := string(v.RunNow.SparkSubmitParams[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.SparkSubmitParams[i] = (r) + } + } + } + } + } + if !n.IsRef("sql_params") { + if len(v.RunNow.SqlParams) > 0 { + cn := n.Child("sql_params") + for _, k := range sortedKeys(v.RunNow.SqlParams) { + if s := string(v.RunNow.SqlParams[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.RunNow.SqlParams[k] = (r) + } + } + } + } + } +} + +func stringsResourcesPipeline(c *stringsWalk, n *Node, v *resources.Pipeline) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPipelinePermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.CreatePipeline.BudgetPolicyId == "" && Forced(v.CreatePipeline.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.BudgetPolicyId = (r) + } + } + if c.mode == EmptyFields && v.CreatePipeline.Catalog == "" && Forced(v.CreatePipeline.ForceSendFields, "Catalog") { + ch := n.Child("catalog") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Catalog); c.match(s) { + ch := n.Child("catalog") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Catalog = (r) + } + } + if c.mode == EmptyFields && v.CreatePipeline.Channel == "" && Forced(v.CreatePipeline.ForceSendFields, "Channel") { + ch := n.Child("channel") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Channel); c.match(s) { + ch := n.Child("channel") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Channel = (r) + } + } + if !n.IsRef("clusters") { + if len(v.CreatePipeline.Clusters) > 0 { + cn := n.Child("clusters") + for i := range v.CreatePipeline.Clusters { + { + ch := cn.Elem(i) + stringsPipelinesPipelineCluster(c, &ch, &v.CreatePipeline.Clusters[i]) + } + } + } + } + if !n.IsRef("configuration") { + if len(v.CreatePipeline.Configuration) > 0 { + cn := n.Child("configuration") + for _, k := range sortedKeys(v.CreatePipeline.Configuration) { + if s := string(v.CreatePipeline.Configuration[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Configuration[k] = (r) + } + } + } + } + } + if !n.IsRef("deployment") { + if x := v.CreatePipeline.Deployment; x != nil { + ch := n.Child("deployment") + stringsPipelinesPipelineDeployment(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreatePipeline.Edition == "" && Forced(v.CreatePipeline.ForceSendFields, "Edition") { + ch := n.Child("edition") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Edition); c.match(s) { + ch := n.Child("edition") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Edition = (r) + } + } + if !n.IsRef("environment") { + if x := v.CreatePipeline.Environment; x != nil { + ch := n.Child("environment") + stringsPipelinesPipelinesEnvironment(c, &ch, x) + } + } + if !n.IsRef("event_log") { + if x := v.CreatePipeline.EventLog; x != nil { + ch := n.Child("event_log") + stringsPipelinesEventLogSpec(c, &ch, x) + } + } + if !n.IsRef("filters") { + if x := v.CreatePipeline.Filters; x != nil { + ch := n.Child("filters") + stringsPipelinesFilters(c, &ch, x) + } + } + if !n.IsRef("gateway_definition") { + if x := v.CreatePipeline.GatewayDefinition; x != nil { + ch := n.Child("gateway_definition") + stringsPipelinesIngestionGatewayPipelineDefinition(c, &ch, x) + } + } + if !n.IsRef("ingestion_definition") { + if x := v.CreatePipeline.IngestionDefinition; x != nil { + ch := n.Child("ingestion_definition") + stringsPipelinesIngestionPipelineDefinition(c, &ch, x) + } + } + if !n.IsRef("libraries") { + if len(v.CreatePipeline.Libraries) > 0 { + cn := n.Child("libraries") + for i := range v.CreatePipeline.Libraries { + { + ch := cn.Elem(i) + stringsPipelinesPipelineLibrary(c, &ch, &v.CreatePipeline.Libraries[i]) + } + } + } + } + if c.mode == EmptyFields && v.CreatePipeline.Name == "" && Forced(v.CreatePipeline.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Name = (r) + } + } + if !n.IsRef("notifications") { + if len(v.CreatePipeline.Notifications) > 0 { + cn := n.Child("notifications") + for i := range v.CreatePipeline.Notifications { + { + ch := cn.Elem(i) + stringsPipelinesNotifications(c, &ch, &v.CreatePipeline.Notifications[i]) + } + } + } + } + if !n.IsRef("parameters") { + if len(v.CreatePipeline.Parameters) > 0 { + cn := n.Child("parameters") + for _, k := range sortedKeys(v.CreatePipeline.Parameters) { + if s := string(v.CreatePipeline.Parameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Parameters[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.CreatePipeline.ParentPath == "" && Forced(v.CreatePipeline.ForceSendFields, "ParentPath") { + ch := n.Child("parent_path") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.ParentPath); c.match(s) { + ch := n.Child("parent_path") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.ParentPath = (r) + } + } + if !n.IsRef("restart_window") { + if x := v.CreatePipeline.RestartWindow; x != nil { + ch := n.Child("restart_window") + stringsPipelinesRestartWindow(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreatePipeline.RootPath == "" && Forced(v.CreatePipeline.ForceSendFields, "RootPath") { + ch := n.Child("root_path") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.RootPath); c.match(s) { + ch := n.Child("root_path") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.RootPath = (r) + } + } + if !n.IsRef("run_as") { + if x := v.CreatePipeline.RunAs; x != nil { + ch := n.Child("run_as") + stringsPipelinesRunAs(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreatePipeline.Schema == "" && Forced(v.CreatePipeline.ForceSendFields, "Schema") { + ch := n.Child("schema") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Schema = (r) + } + } + if c.mode == EmptyFields && v.CreatePipeline.ServerlessComputeId == "" && Forced(v.CreatePipeline.ForceSendFields, "ServerlessComputeId") { + ch := n.Child("serverless_compute_id") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.ServerlessComputeId); c.match(s) { + ch := n.Child("serverless_compute_id") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.ServerlessComputeId = (r) + } + } + if c.mode == EmptyFields && v.CreatePipeline.Storage == "" && Forced(v.CreatePipeline.ForceSendFields, "Storage") { + ch := n.Child("storage") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Storage); c.match(s) { + ch := n.Child("storage") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Storage = (r) + } + } + if !n.IsRef("tags") { + if len(v.CreatePipeline.Tags) > 0 { + cn := n.Child("tags") + for _, k := range sortedKeys(v.CreatePipeline.Tags) { + if s := string(v.CreatePipeline.Tags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Tags[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.CreatePipeline.Target == "" && Forced(v.CreatePipeline.ForceSendFields, "Target") { + ch := n.Child("target") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.Target); c.match(s) { + ch := n.Child("target") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.Target = (r) + } + } + if !n.IsRef("trigger") { + if x := v.CreatePipeline.Trigger; x != nil { + ch := n.Child("trigger") + stringsPipelinesPipelineTrigger(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreatePipeline.UsagePolicyId == "" && Forced(v.CreatePipeline.ForceSendFields, "UsagePolicyId") { + ch := n.Child("usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.CreatePipeline.UsagePolicyId); c.match(s) { + ch := n.Child("usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.CreatePipeline.UsagePolicyId = (r) + } + } +} + +func stringsResourcesFeature(c *stringsWalk, n *Node, v *resources.Feature) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.Feature.CatalogName == "" && Forced(v.Feature.ForceSendFields, "CatalogName") { + ch := n.Child("catalog_name") + c.fn(&ch, "") + } + if s := string(v.Feature.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.Feature.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.Feature.CreatedBy == "" && Forced(v.Feature.ForceSendFields, "CreatedBy") { + ch := n.Child("created_by") + c.fn(&ch, "") + } + if s := string(v.Feature.CreatedBy); c.match(s) { + ch := n.Child("created_by") + if r, ok := c.fn(&ch, s); ok { + v.Feature.CreatedBy = (r) + } + } + if c.mode == EmptyFields && v.Feature.Description == "" && Forced(v.Feature.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.Feature.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.Feature.Description = (r) + } + } + if !n.IsRef("entities") { + if len(v.Feature.Entities) > 0 { + cn := n.Child("entities") + for i := range v.Feature.Entities { + { + ch := cn.Elem(i) + stringsMlEntityColumn(c, &ch, &v.Feature.Entities[i]) + } + } + } + } + if c.mode == EmptyFields && v.Feature.FilterCondition == "" && Forced(v.Feature.ForceSendFields, "FilterCondition") { + ch := n.Child("filter_condition") + c.fn(&ch, "") + } + if s := string(v.Feature.FilterCondition); c.match(s) { + ch := n.Child("filter_condition") + if r, ok := c.fn(&ch, s); ok { + v.Feature.FilterCondition = (r) + } + } + if s := string(v.Feature.FullName); c.match(s) { + ch := n.Child("full_name") + if r, ok := c.fn(&ch, s); ok { + v.Feature.FullName = (r) + } + } + if !n.IsRef("function") { + { + ch := n.Child("function") + stringsMlFunction(c, &ch, &v.Feature.Function) + } + } + if !n.IsRef("inputs") { + if len(v.Feature.Inputs) > 0 { + cn := n.Child("inputs") + for i := range v.Feature.Inputs { + if s := string(v.Feature.Inputs[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Feature.Inputs[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.Feature.Name == "" && Forced(v.Feature.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Feature.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Feature.Name = (r) + } + } + if c.mode == EmptyFields && v.Feature.SchemaName == "" && Forced(v.Feature.ForceSendFields, "SchemaName") { + ch := n.Child("schema_name") + c.fn(&ch, "") + } + if s := string(v.Feature.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.Feature.SchemaName = (r) + } + } + if !n.IsRef("source") { + { + ch := n.Child("source") + stringsMlDataSource(c, &ch, &v.Feature.Source) + } + } + if !n.IsRef("time_window") { + if x := v.Feature.TimeWindow; x != nil { + ch := n.Child("time_window") + stringsMlTimeWindow(c, &ch, x) + } + } + if !n.IsRef("timeseries_column") { + if x := v.Feature.TimeseriesColumn; x != nil { + ch := n.Child("timeseries_column") + stringsMlTimeseriesColumn(c, &ch, x) + } + } +} + +func stringsResourcesMlflowModel(c *stringsWalk, n *Node, v *resources.MlflowModel) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesMlflowModelPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.CreateModelRequest.Description == "" && Forced(v.CreateModelRequest.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.CreateModelRequest.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.CreateModelRequest.Description = (r) + } + } + if s := string(v.CreateModelRequest.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateModelRequest.Name = (r) + } + } + if !n.IsRef("tags") { + if len(v.CreateModelRequest.Tags) > 0 { + cn := n.Child("tags") + for i := range v.CreateModelRequest.Tags { + { + ch := cn.Elem(i) + stringsMlModelTag(c, &ch, &v.CreateModelRequest.Tags[i]) + } + } + } + } +} + +func stringsResourcesMlflowExperiment(c *stringsWalk, n *Node, v *resources.MlflowExperiment) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesMlflowExperimentPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.CreateExperiment.ArtifactLocation == "" && Forced(v.CreateExperiment.ForceSendFields, "ArtifactLocation") { + ch := n.Child("artifact_location") + c.fn(&ch, "") + } + if s := string(v.CreateExperiment.ArtifactLocation); c.match(s) { + ch := n.Child("artifact_location") + if r, ok := c.fn(&ch, s); ok { + v.CreateExperiment.ArtifactLocation = (r) + } + } + if s := string(v.CreateExperiment.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateExperiment.Name = (r) + } + } + if !n.IsRef("tags") { + if len(v.CreateExperiment.Tags) > 0 { + cn := n.Child("tags") + for i := range v.CreateExperiment.Tags { + { + ch := cn.Elem(i) + stringsMlExperimentTag(c, &ch, &v.CreateExperiment.Tags[i]) + } + } + } + } + if !n.IsRef("trace_location") { + if x := v.CreateExperiment.TraceLocation; x != nil { + ch := n.Child("trace_location") + stringsMlExperimentTraceLocation(c, &ch, x) + } + } +} + +func stringsResourcesModelServingEndpoint(c *stringsWalk, n *Node, v *resources.ModelServingEndpoint) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesModelServingEndpointPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if !n.IsRef("ai_gateway") { + if x := v.CreateServingEndpoint.AiGateway; x != nil { + ch := n.Child("ai_gateway") + stringsServingAiGatewayConfig(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreateServingEndpoint.BudgetPolicyId == "" && Forced(v.CreateServingEndpoint.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.CreateServingEndpoint.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.CreateServingEndpoint.BudgetPolicyId = (r) + } + } + if !n.IsRef("config") { + if x := v.CreateServingEndpoint.Config; x != nil { + ch := n.Child("config") + stringsServingEndpointCoreConfigInput(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreateServingEndpoint.Description == "" && Forced(v.CreateServingEndpoint.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.CreateServingEndpoint.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.CreateServingEndpoint.Description = (r) + } + } + if !n.IsRef("email_notifications") { + if x := v.CreateServingEndpoint.EmailNotifications; x != nil { + ch := n.Child("email_notifications") + stringsServingEmailNotifications(c, &ch, x) + } + } + if s := string(v.CreateServingEndpoint.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateServingEndpoint.Name = (r) + } + } + if !n.IsRef("rate_limits") { + if len(v.CreateServingEndpoint.RateLimits) > 0 { + cn := n.Child("rate_limits") + for i := range v.CreateServingEndpoint.RateLimits { + { + ch := cn.Elem(i) + stringsServingRateLimit(c, &ch, &v.CreateServingEndpoint.RateLimits[i]) + } + } + } + } + if !n.IsRef("tags") { + if len(v.CreateServingEndpoint.Tags) > 0 { + cn := n.Child("tags") + for i := range v.CreateServingEndpoint.Tags { + { + ch := cn.Elem(i) + stringsServingEndpointTag(c, &ch, &v.CreateServingEndpoint.Tags[i]) + } + } + } + } + if !n.IsRef("telemetry_config") { + if x := v.CreateServingEndpoint.TelemetryConfig; x != nil { + ch := n.Child("telemetry_config") + stringsServingTelemetryConfig(c, &ch, x) + } + } +} + +func stringsResourcesModelService(c *stringsWalk, n *Node, v *resources.ModelService) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.ModelServiceConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.ModelServiceConfig.Parent = (r) + } + } + if s := string(v.ModelServiceConfig.ModelServiceId); c.match(s) { + ch := n.Child("model_service_id") + if r, ok := c.fn(&ch, s); ok { + v.ModelServiceConfig.ModelServiceId = (r) + } + } + if c.mode == EmptyFields && v.ModelServiceConfig.Comment == "" && Forced(v.ModelServiceConfig.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.ModelServiceConfig.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.ModelServiceConfig.Comment = (r) + } + } + if !n.IsRef("config") { + if x := v.ModelServiceConfig.Config; x != nil { + ch := n.Child("config") + stringsCatalogModelServiceConfig(c, &ch, x) + } + } +} + +func stringsResourcesMcpService(c *stringsWalk, n *Node, v *resources.McpService) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.McpServiceConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.McpServiceConfig.Parent = (r) + } + } + if s := string(v.McpServiceConfig.McpServiceId); c.match(s) { + ch := n.Child("mcp_service_id") + if r, ok := c.fn(&ch, s); ok { + v.McpServiceConfig.McpServiceId = (r) + } + } + if c.mode == EmptyFields && v.McpServiceConfig.Comment == "" && Forced(v.McpServiceConfig.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.McpServiceConfig.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.McpServiceConfig.Comment = (r) + } + } + if !n.IsRef("config") { + if x := v.McpServiceConfig.Config; x != nil { + ch := n.Child("config") + stringsCatalogMcpServiceConfig(c, &ch, x) + } + } +} + +func stringsResourcesModelProviderService(c *stringsWalk, n *Node, v *resources.ModelProviderService) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.ModelProviderServiceConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.ModelProviderServiceConfig.Parent = (r) + } + } + if s := string(v.ModelProviderServiceConfig.ModelProviderServiceId); c.match(s) { + ch := n.Child("model_provider_service_id") + if r, ok := c.fn(&ch, s); ok { + v.ModelProviderServiceConfig.ModelProviderServiceId = (r) + } + } + if c.mode == EmptyFields && v.ModelProviderServiceConfig.Comment == "" && Forced(v.ModelProviderServiceConfig.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.ModelProviderServiceConfig.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.ModelProviderServiceConfig.Comment = (r) + } + } + if !n.IsRef("config") { + if x := v.ModelProviderServiceConfig.Config; x != nil { + ch := n.Child("config") + stringsCatalogModelProviderServiceConfig(c, &ch, x) + } + } +} + +func stringsResourcesRegisteredModel(c *stringsWalk, n *Node, v *resources.RegisteredModel) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if !n.IsRef("aliases") { + if len(v.CreateRegisteredModelRequest.Aliases) > 0 { + cn := n.Child("aliases") + for i := range v.CreateRegisteredModelRequest.Aliases { + { + ch := cn.Elem(i) + stringsCatalogRegisteredModelAlias(c, &ch, &v.CreateRegisteredModelRequest.Aliases[i]) + } + } + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.CatalogName == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "CatalogName") { + ch := n.Child("catalog_name") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.Comment == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.Comment = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.CreatedBy == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "CreatedBy") { + ch := n.Child("created_by") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.CreatedBy); c.match(s) { + ch := n.Child("created_by") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.CreatedBy = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.FullName == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "FullName") { + ch := n.Child("full_name") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.FullName); c.match(s) { + ch := n.Child("full_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.FullName = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.MetastoreId == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "MetastoreId") { + ch := n.Child("metastore_id") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.MetastoreId); c.match(s) { + ch := n.Child("metastore_id") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.MetastoreId = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.Name == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.Name = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.Owner == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "Owner") { + ch := n.Child("owner") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.Owner); c.match(s) { + ch := n.Child("owner") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.Owner = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.SchemaName == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "SchemaName") { + ch := n.Child("schema_name") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.SchemaName = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.StorageLocation == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "StorageLocation") { + ch := n.Child("storage_location") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.StorageLocation); c.match(s) { + ch := n.Child("storage_location") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.StorageLocation = (r) + } + } + if c.mode == EmptyFields && v.CreateRegisteredModelRequest.UpdatedBy == "" && Forced(v.CreateRegisteredModelRequest.ForceSendFields, "UpdatedBy") { + ch := n.Child("updated_by") + c.fn(&ch, "") + } + if s := string(v.CreateRegisteredModelRequest.UpdatedBy); c.match(s) { + ch := n.Child("updated_by") + if r, ok := c.fn(&ch, s); ok { + v.CreateRegisteredModelRequest.UpdatedBy = (r) + } + } +} + +func stringsResourcesQualityMonitor(c *stringsWalk, n *Node, v *resources.QualityMonitor) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.TableName); c.match(s) { + ch := n.Child("table_name") + if r, ok := c.fn(&ch, s); ok { + v.TableName = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.CreateMonitor.AssetsDir); c.match(s) { + ch := n.Child("assets_dir") + if r, ok := c.fn(&ch, s); ok { + v.CreateMonitor.AssetsDir = (r) + } + } + if c.mode == EmptyFields && v.CreateMonitor.BaselineTableName == "" && Forced(v.CreateMonitor.ForceSendFields, "BaselineTableName") { + ch := n.Child("baseline_table_name") + c.fn(&ch, "") + } + if s := string(v.CreateMonitor.BaselineTableName); c.match(s) { + ch := n.Child("baseline_table_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateMonitor.BaselineTableName = (r) + } + } + if !n.IsRef("custom_metrics") { + if len(v.CreateMonitor.CustomMetrics) > 0 { + cn := n.Child("custom_metrics") + for i := range v.CreateMonitor.CustomMetrics { + { + ch := cn.Elem(i) + stringsCatalogMonitorMetric(c, &ch, &v.CreateMonitor.CustomMetrics[i]) + } + } + } + } + if !n.IsRef("inference_log") { + if x := v.CreateMonitor.InferenceLog; x != nil { + ch := n.Child("inference_log") + stringsCatalogMonitorInferenceLog(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreateMonitor.LatestMonitorFailureMsg == "" && Forced(v.CreateMonitor.ForceSendFields, "LatestMonitorFailureMsg") { + ch := n.Child("latest_monitor_failure_msg") + c.fn(&ch, "") + } + if s := string(v.CreateMonitor.LatestMonitorFailureMsg); c.match(s) { + ch := n.Child("latest_monitor_failure_msg") + if r, ok := c.fn(&ch, s); ok { + v.CreateMonitor.LatestMonitorFailureMsg = (r) + } + } + if !n.IsRef("notifications") { + if x := v.CreateMonitor.Notifications; x != nil { + ch := n.Child("notifications") + stringsCatalogMonitorNotifications(c, &ch, x) + } + } + if s := string(v.CreateMonitor.OutputSchemaName); c.match(s) { + ch := n.Child("output_schema_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateMonitor.OutputSchemaName = (r) + } + } + if !n.IsRef("schedule") { + if x := v.CreateMonitor.Schedule; x != nil { + ch := n.Child("schedule") + stringsCatalogMonitorCronSchedule(c, &ch, x) + } + } + if !n.IsRef("slicing_exprs") { + if len(v.CreateMonitor.SlicingExprs) > 0 { + cn := n.Child("slicing_exprs") + for i := range v.CreateMonitor.SlicingExprs { + if s := string(v.CreateMonitor.SlicingExprs[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.CreateMonitor.SlicingExprs[i] = (r) + } + } + } + } + } + if !n.IsRef("time_series") { + if x := v.CreateMonitor.TimeSeries; x != nil { + ch := n.Child("time_series") + stringsCatalogMonitorTimeSeries(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreateMonitor.WarehouseId == "" && Forced(v.CreateMonitor.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.CreateMonitor.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.CreateMonitor.WarehouseId = (r) + } + } +} + +func stringsResourcesCatalog(c *stringsWalk, n *Node, v *resources.Catalog) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.CreateCatalog.Comment == "" && Forced(v.CreateCatalog.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.CreateCatalog.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.Comment = (r) + } + } + if c.mode == EmptyFields && v.CreateCatalog.ConnectionName == "" && Forced(v.CreateCatalog.ForceSendFields, "ConnectionName") { + ch := n.Child("connection_name") + c.fn(&ch, "") + } + if s := string(v.CreateCatalog.ConnectionName); c.match(s) { + ch := n.Child("connection_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.ConnectionName = (r) + } + } + if !n.IsRef("managed_encryption_settings") { + if x := v.CreateCatalog.ManagedEncryptionSettings; x != nil { + ch := n.Child("managed_encryption_settings") + stringsCatalogEncryptionSettings(c, &ch, x) + } + } + if s := string(v.CreateCatalog.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.Name = (r) + } + } + if !n.IsRef("options") { + if len(v.CreateCatalog.Options) > 0 { + cn := n.Child("options") + for _, k := range sortedKeys(v.CreateCatalog.Options) { + if s := string(v.CreateCatalog.Options[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.Options[k] = (r) + } + } + } + } + } + if !n.IsRef("properties") { + if len(v.CreateCatalog.Properties) > 0 { + cn := n.Child("properties") + for _, k := range sortedKeys(v.CreateCatalog.Properties) { + if s := string(v.CreateCatalog.Properties[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.Properties[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.CreateCatalog.ProviderName == "" && Forced(v.CreateCatalog.ForceSendFields, "ProviderName") { + ch := n.Child("provider_name") + c.fn(&ch, "") + } + if s := string(v.CreateCatalog.ProviderName); c.match(s) { + ch := n.Child("provider_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.ProviderName = (r) + } + } + if c.mode == EmptyFields && v.CreateCatalog.ShareName == "" && Forced(v.CreateCatalog.ForceSendFields, "ShareName") { + ch := n.Child("share_name") + c.fn(&ch, "") + } + if s := string(v.CreateCatalog.ShareName); c.match(s) { + ch := n.Child("share_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.ShareName = (r) + } + } + if c.mode == EmptyFields && v.CreateCatalog.StorageRoot == "" && Forced(v.CreateCatalog.ForceSendFields, "StorageRoot") { + ch := n.Child("storage_root") + c.fn(&ch, "") + } + if s := string(v.CreateCatalog.StorageRoot); c.match(s) { + ch := n.Child("storage_root") + if r, ok := c.fn(&ch, s); ok { + v.CreateCatalog.StorageRoot = (r) + } + } +} + +func stringsResourcesSchema(c *stringsWalk, n *Node, v *resources.Schema) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.CreateSchema.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateSchema.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.CreateSchema.Comment == "" && Forced(v.CreateSchema.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.CreateSchema.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.CreateSchema.Comment = (r) + } + } + if s := string(v.CreateSchema.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateSchema.Name = (r) + } + } + if !n.IsRef("properties") { + if len(v.CreateSchema.Properties) > 0 { + cn := n.Child("properties") + for _, k := range sortedKeys(v.CreateSchema.Properties) { + if s := string(v.CreateSchema.Properties[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreateSchema.Properties[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.CreateSchema.StorageRoot == "" && Forced(v.CreateSchema.ForceSendFields, "StorageRoot") { + ch := n.Child("storage_root") + c.fn(&ch, "") + } + if s := string(v.CreateSchema.StorageRoot); c.match(s) { + ch := n.Child("storage_root") + if r, ok := c.fn(&ch, s); ok { + v.CreateSchema.StorageRoot = (r) + } + } +} + +func stringsResourcesVolume(c *stringsWalk, n *Node, v *resources.Volume) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.VolumePath); c.match(s) { + ch := n.Child("volume_path") + if r, ok := c.fn(&ch, s); ok { + v.VolumePath = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.CreateVolumeRequestContent.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateVolumeRequestContent.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.CreateVolumeRequestContent.Comment == "" && Forced(v.CreateVolumeRequestContent.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.CreateVolumeRequestContent.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.CreateVolumeRequestContent.Comment = (r) + } + } + if s := string(v.CreateVolumeRequestContent.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateVolumeRequestContent.Name = (r) + } + } + if s := string(v.CreateVolumeRequestContent.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateVolumeRequestContent.SchemaName = (r) + } + } + if c.mode == EmptyFields && v.CreateVolumeRequestContent.StorageLocation == "" && Forced(v.CreateVolumeRequestContent.ForceSendFields, "StorageLocation") { + ch := n.Child("storage_location") + c.fn(&ch, "") + } + if s := string(v.CreateVolumeRequestContent.StorageLocation); c.match(s) { + ch := n.Child("storage_location") + if r, ok := c.fn(&ch, s); ok { + v.CreateVolumeRequestContent.StorageLocation = (r) + } + } + if s := string(v.CreateVolumeRequestContent.VolumeType); c.match(s) { + ch := n.Child("volume_type") + if r, ok := c.fn(&ch, s); ok { + v.CreateVolumeRequestContent.VolumeType = catalog.VolumeType(r) + } + } +} + +func stringsResourcesExternalLocation(c *stringsWalk, n *Node, v *resources.ExternalLocation) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.Lifecycle) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if c.mode == EmptyFields && v.CreateExternalLocation.Comment == "" && Forced(v.CreateExternalLocation.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.CreateExternalLocation.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.CreateExternalLocation.Comment = (r) + } + } + if s := string(v.CreateExternalLocation.CredentialName); c.match(s) { + ch := n.Child("credential_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateExternalLocation.CredentialName = (r) + } + } + if !n.IsRef("effective_file_event_queue") { + if x := v.CreateExternalLocation.EffectiveFileEventQueue; x != nil { + ch := n.Child("effective_file_event_queue") + stringsCatalogFileEventQueue(c, &ch, x) + } + } + if !n.IsRef("encryption_details") { + if x := v.CreateExternalLocation.EncryptionDetails; x != nil { + ch := n.Child("encryption_details") + stringsCatalogEncryptionDetails(c, &ch, x) + } + } + if !n.IsRef("file_event_queue") { + if x := v.CreateExternalLocation.FileEventQueue; x != nil { + ch := n.Child("file_event_queue") + stringsCatalogFileEventQueue(c, &ch, x) + } + } + if s := string(v.CreateExternalLocation.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateExternalLocation.Name = (r) + } + } + if s := string(v.CreateExternalLocation.Url); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.CreateExternalLocation.Url = (r) + } + } +} + +func stringsResourcesCluster(c *stringsWalk, n *Node, v *resources.Cluster) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("lifecycle") { + if x := v.Lifecycle; x != nil { + ch := n.Child("lifecycle") + stringsResourcesLifecycleWithStarted(c, &ch, x) + } + } + if !n.IsRef("libraries") { + if len(v.Libraries) > 0 { + cn := n.Child("libraries") + for i := range v.Libraries { + { + ch := cn.Elem(i) + stringsComputeLibrary(c, &ch, &v.Libraries[i]) + } + } + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesClusterPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("aws_attributes") { + if x := v.ClusterSpec.AwsAttributes; x != nil { + ch := n.Child("aws_attributes") + stringsComputeAwsAttributes(c, &ch, x) + } + } + if !n.IsRef("azure_attributes") { + if x := v.ClusterSpec.AzureAttributes; x != nil { + ch := n.Child("azure_attributes") + stringsComputeAzureAttributes(c, &ch, x) + } + } + if !n.IsRef("cluster_log_conf") { + if x := v.ClusterSpec.ClusterLogConf; x != nil { + ch := n.Child("cluster_log_conf") + stringsComputeClusterLogConf(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ClusterSpec.ClusterName == "" && Forced(v.ClusterSpec.ForceSendFields, "ClusterName") { + ch := n.Child("cluster_name") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.ClusterName); c.match(s) { + ch := n.Child("cluster_name") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.ClusterName = (r) + } + } + if !n.IsRef("custom_tags") { + if len(v.ClusterSpec.CustomTags) > 0 { + cn := n.Child("custom_tags") + for _, k := range sortedKeys(v.ClusterSpec.CustomTags) { + if s := string(v.ClusterSpec.CustomTags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.CustomTags[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ClusterSpec.DataSecurityMode == "" && Forced(v.ClusterSpec.ForceSendFields, "DataSecurityMode") { + ch := n.Child("data_security_mode") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.DataSecurityMode); c.match(s) { + ch := n.Child("data_security_mode") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.DataSecurityMode = compute.DataSecurityMode(r) + } + } + if c.mode == EmptyFields && v.ClusterSpec.DependencyMode == "" && Forced(v.ClusterSpec.ForceSendFields, "DependencyMode") { + ch := n.Child("dependency_mode") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.DependencyMode); c.match(s) { + ch := n.Child("dependency_mode") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.DependencyMode = compute.DependencyMode(r) + } + } + if !n.IsRef("docker_image") { + if x := v.ClusterSpec.DockerImage; x != nil { + ch := n.Child("docker_image") + stringsComputeDockerImage(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ClusterSpec.DriverInstancePoolId == "" && Forced(v.ClusterSpec.ForceSendFields, "DriverInstancePoolId") { + ch := n.Child("driver_instance_pool_id") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.DriverInstancePoolId); c.match(s) { + ch := n.Child("driver_instance_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.DriverInstancePoolId = (r) + } + } + if !n.IsRef("driver_node_type_flexibility") { + if x := v.ClusterSpec.DriverNodeTypeFlexibility; x != nil { + ch := n.Child("driver_node_type_flexibility") + stringsComputeNodeTypeFlexibility(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ClusterSpec.DriverNodeTypeId == "" && Forced(v.ClusterSpec.ForceSendFields, "DriverNodeTypeId") { + ch := n.Child("driver_node_type_id") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.DriverNodeTypeId); c.match(s) { + ch := n.Child("driver_node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.DriverNodeTypeId = (r) + } + } + if !n.IsRef("gcp_attributes") { + if x := v.ClusterSpec.GcpAttributes; x != nil { + ch := n.Child("gcp_attributes") + stringsComputeGcpAttributes(c, &ch, x) + } + } + if !n.IsRef("init_scripts") { + if len(v.ClusterSpec.InitScripts) > 0 { + cn := n.Child("init_scripts") + for i := range v.ClusterSpec.InitScripts { + { + ch := cn.Elem(i) + stringsComputeInitScriptInfo(c, &ch, &v.ClusterSpec.InitScripts[i]) + } + } + } + } + if c.mode == EmptyFields && v.ClusterSpec.InstancePoolId == "" && Forced(v.ClusterSpec.ForceSendFields, "InstancePoolId") { + ch := n.Child("instance_pool_id") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.InstancePoolId); c.match(s) { + ch := n.Child("instance_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.InstancePoolId = (r) + } + } + if c.mode == EmptyFields && v.ClusterSpec.Kind == "" && Forced(v.ClusterSpec.ForceSendFields, "Kind") { + ch := n.Child("kind") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.Kind); c.match(s) { + ch := n.Child("kind") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.Kind = compute.Kind(r) + } + } + if c.mode == EmptyFields && v.ClusterSpec.NodeTypeId == "" && Forced(v.ClusterSpec.ForceSendFields, "NodeTypeId") { + ch := n.Child("node_type_id") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.NodeTypeId); c.match(s) { + ch := n.Child("node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.NodeTypeId = (r) + } + } + if c.mode == EmptyFields && v.ClusterSpec.PolicyId == "" && Forced(v.ClusterSpec.ForceSendFields, "PolicyId") { + ch := n.Child("policy_id") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.PolicyId); c.match(s) { + ch := n.Child("policy_id") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.PolicyId = (r) + } + } + if c.mode == EmptyFields && v.ClusterSpec.RuntimeEngine == "" && Forced(v.ClusterSpec.ForceSendFields, "RuntimeEngine") { + ch := n.Child("runtime_engine") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.RuntimeEngine); c.match(s) { + ch := n.Child("runtime_engine") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.RuntimeEngine = compute.RuntimeEngine(r) + } + } + if c.mode == EmptyFields && v.ClusterSpec.SingleUserName == "" && Forced(v.ClusterSpec.ForceSendFields, "SingleUserName") { + ch := n.Child("single_user_name") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.SingleUserName); c.match(s) { + ch := n.Child("single_user_name") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.SingleUserName = (r) + } + } + if !n.IsRef("spark_conf") { + if len(v.ClusterSpec.SparkConf) > 0 { + cn := n.Child("spark_conf") + for _, k := range sortedKeys(v.ClusterSpec.SparkConf) { + if s := string(v.ClusterSpec.SparkConf[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.SparkConf[k] = (r) + } + } + } + } + } + if !n.IsRef("spark_env_vars") { + if len(v.ClusterSpec.SparkEnvVars) > 0 { + cn := n.Child("spark_env_vars") + for _, k := range sortedKeys(v.ClusterSpec.SparkEnvVars) { + if s := string(v.ClusterSpec.SparkEnvVars[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.SparkEnvVars[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ClusterSpec.SparkVersion == "" && Forced(v.ClusterSpec.ForceSendFields, "SparkVersion") { + ch := n.Child("spark_version") + c.fn(&ch, "") + } + if s := string(v.ClusterSpec.SparkVersion); c.match(s) { + ch := n.Child("spark_version") + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.SparkVersion = (r) + } + } + if !n.IsRef("ssh_public_keys") { + if len(v.ClusterSpec.SshPublicKeys) > 0 { + cn := n.Child("ssh_public_keys") + for i := range v.ClusterSpec.SshPublicKeys { + if s := string(v.ClusterSpec.SshPublicKeys[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ClusterSpec.SshPublicKeys[i] = (r) + } + } + } + } + } + if !n.IsRef("worker_node_type_flexibility") { + if x := v.ClusterSpec.WorkerNodeTypeFlexibility; x != nil { + ch := n.Child("worker_node_type_flexibility") + stringsComputeNodeTypeFlexibility(c, &ch, x) + } + } +} + +func stringsResourcesDashboard(c *stringsWalk, n *Node, v *resources.Dashboard) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.FilePath); c.match(s) { + ch := n.Child("file_path") + if r, ok := c.fn(&ch, s); ok { + v.FilePath = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.DashboardConfig.CreateTime == "" && Forced(v.DashboardConfig.ForceSendFields, "CreateTime") { + ch := n.Child("create_time") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.CreateTime); c.match(s) { + ch := n.Child("create_time") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.CreateTime = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.DashboardId == "" && Forced(v.DashboardConfig.ForceSendFields, "DashboardId") { + ch := n.Child("dashboard_id") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.DashboardId); c.match(s) { + ch := n.Child("dashboard_id") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.DashboardId = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.DisplayName == "" && Forced(v.DashboardConfig.ForceSendFields, "DisplayName") { + ch := n.Child("display_name") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.DisplayName); c.match(s) { + ch := n.Child("display_name") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.DisplayName = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.Etag == "" && Forced(v.DashboardConfig.ForceSendFields, "Etag") { + ch := n.Child("etag") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.Etag); c.match(s) { + ch := n.Child("etag") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.Etag = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.LifecycleState == "" && Forced(v.DashboardConfig.ForceSendFields, "LifecycleState") { + ch := n.Child("lifecycle_state") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.LifecycleState); c.match(s) { + ch := n.Child("lifecycle_state") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.LifecycleState = dashboards.LifecycleState(r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.ParentPath == "" && Forced(v.DashboardConfig.ForceSendFields, "ParentPath") { + ch := n.Child("parent_path") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.ParentPath); c.match(s) { + ch := n.Child("parent_path") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.ParentPath = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.Path == "" && Forced(v.DashboardConfig.ForceSendFields, "Path") { + ch := n.Child("path") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.Path); c.match(s) { + ch := n.Child("path") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.Path = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.UpdateTime == "" && Forced(v.DashboardConfig.ForceSendFields, "UpdateTime") { + ch := n.Child("update_time") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.UpdateTime); c.match(s) { + ch := n.Child("update_time") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.UpdateTime = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.WarehouseId == "" && Forced(v.DashboardConfig.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.WarehouseId = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.SerializedDashboard == "" { + ch := n.Child("serialized_dashboard") + c.fn(&ch, "") + } + if v.DashboardConfig.SerializedDashboard != nil { + ch := n.Child("serialized_dashboard") + c.any(&ch, &v.DashboardConfig.SerializedDashboard) + } + if c.mode == EmptyFields && v.DashboardConfig.DatasetCatalog == "" && Forced(v.DashboardConfig.ForceSendFields, "DatasetCatalog") { + ch := n.Child("dataset_catalog") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.DatasetCatalog); c.match(s) { + ch := n.Child("dataset_catalog") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.DatasetCatalog = (r) + } + } + if c.mode == EmptyFields && v.DashboardConfig.DatasetSchema == "" && Forced(v.DashboardConfig.ForceSendFields, "DatasetSchema") { + ch := n.Child("dataset_schema") + c.fn(&ch, "") + } + if s := string(v.DashboardConfig.DatasetSchema); c.match(s) { + ch := n.Child("dataset_schema") + if r, ok := c.fn(&ch, s); ok { + v.DashboardConfig.DatasetSchema = (r) + } + } +} + +func stringsResourcesGenieSpace(c *stringsWalk, n *Node, v *resources.GenieSpace) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.FilePath); c.match(s) { + ch := n.Child("file_path") + if r, ok := c.fn(&ch, s); ok { + v.FilePath = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.GenieSpaceConfig.Description == "" && Forced(v.GenieSpaceConfig.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.GenieSpaceConfig.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.GenieSpaceConfig.Description = (r) + } + } + if c.mode == EmptyFields && v.GenieSpaceConfig.Etag == "" && Forced(v.GenieSpaceConfig.ForceSendFields, "Etag") { + ch := n.Child("etag") + c.fn(&ch, "") + } + if s := string(v.GenieSpaceConfig.Etag); c.match(s) { + ch := n.Child("etag") + if r, ok := c.fn(&ch, s); ok { + v.GenieSpaceConfig.Etag = (r) + } + } + if c.mode == EmptyFields && v.GenieSpaceConfig.Title == "" && Forced(v.GenieSpaceConfig.ForceSendFields, "Title") { + ch := n.Child("title") + c.fn(&ch, "") + } + if s := string(v.GenieSpaceConfig.Title); c.match(s) { + ch := n.Child("title") + if r, ok := c.fn(&ch, s); ok { + v.GenieSpaceConfig.Title = (r) + } + } + if c.mode == EmptyFields && v.GenieSpaceConfig.WarehouseId == "" && Forced(v.GenieSpaceConfig.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.GenieSpaceConfig.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.GenieSpaceConfig.WarehouseId = (r) + } + } + if c.mode == EmptyFields && v.GenieSpaceConfig.ParentPath == "" && Forced(v.GenieSpaceConfig.ForceSendFields, "ParentPath") { + ch := n.Child("parent_path") + c.fn(&ch, "") + } + if s := string(v.GenieSpaceConfig.ParentPath); c.match(s) { + ch := n.Child("parent_path") + if r, ok := c.fn(&ch, s); ok { + v.GenieSpaceConfig.ParentPath = (r) + } + } + if c.mode == EmptyFields && v.GenieSpaceConfig.SerializedSpace == "" { + ch := n.Child("serialized_space") + c.fn(&ch, "") + } + if v.GenieSpaceConfig.SerializedSpace != nil { + ch := n.Child("serialized_space") + c.any(&ch, &v.GenieSpaceConfig.SerializedSpace) + } +} + +func stringsResourcesApp(c *stringsWalk, n *Node, v *resources.App) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.URL = (r) + } + } + if !n.IsRef("lifecycle") { + if x := v.Lifecycle; x != nil { + ch := n.Child("lifecycle") + stringsResourcesLifecycleWithStarted(c, &ch, x) + } + } + if !n.IsRef("config") { + if x := v.Config; x != nil { + ch := n.Child("config") + stringsResourcesAppConfig(c, &ch, x) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesAppPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("active_deployment") { + if x := v.App.ActiveDeployment; x != nil { + ch := n.Child("active_deployment") + stringsAppsAppDeployment(c, &ch, x) + } + } + if !n.IsRef("app_status") { + if x := v.App.AppStatus; x != nil { + ch := n.Child("app_status") + stringsAppsApplicationStatus(c, &ch, x) + } + } + if c.mode == EmptyFields && v.App.BudgetPolicyId == "" && Forced(v.App.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.App.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.App.BudgetPolicyId = (r) + } + } + if c.mode == EmptyFields && v.App.ComputeSize == "" && Forced(v.App.ForceSendFields, "ComputeSize") { + ch := n.Child("compute_size") + c.fn(&ch, "") + } + if s := string(v.App.ComputeSize); c.match(s) { + ch := n.Child("compute_size") + if r, ok := c.fn(&ch, s); ok { + v.App.ComputeSize = apps.ComputeSize(r) + } + } + if !n.IsRef("compute_status") { + if x := v.App.ComputeStatus; x != nil { + ch := n.Child("compute_status") + stringsAppsComputeStatus(c, &ch, x) + } + } + if c.mode == EmptyFields && v.App.CreateTime == "" && Forced(v.App.ForceSendFields, "CreateTime") { + ch := n.Child("create_time") + c.fn(&ch, "") + } + if s := string(v.App.CreateTime); c.match(s) { + ch := n.Child("create_time") + if r, ok := c.fn(&ch, s); ok { + v.App.CreateTime = (r) + } + } + if c.mode == EmptyFields && v.App.Creator == "" && Forced(v.App.ForceSendFields, "Creator") { + ch := n.Child("creator") + c.fn(&ch, "") + } + if s := string(v.App.Creator); c.match(s) { + ch := n.Child("creator") + if r, ok := c.fn(&ch, s); ok { + v.App.Creator = (r) + } + } + if !n.IsRef("default_git_source") { + if x := v.App.DefaultGitSource; x != nil { + ch := n.Child("default_git_source") + stringsAppsGitSource(c, &ch, x) + } + } + if c.mode == EmptyFields && v.App.DefaultSourceCodePath == "" && Forced(v.App.ForceSendFields, "DefaultSourceCodePath") { + ch := n.Child("default_source_code_path") + c.fn(&ch, "") + } + if s := string(v.App.DefaultSourceCodePath); c.match(s) { + ch := n.Child("default_source_code_path") + if r, ok := c.fn(&ch, s); ok { + v.App.DefaultSourceCodePath = (r) + } + } + if c.mode == EmptyFields && v.App.Description == "" && Forced(v.App.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.App.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.App.Description = (r) + } + } + if c.mode == EmptyFields && v.App.EffectiveBudgetPolicyId == "" && Forced(v.App.ForceSendFields, "EffectiveBudgetPolicyId") { + ch := n.Child("effective_budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.App.EffectiveBudgetPolicyId); c.match(s) { + ch := n.Child("effective_budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.App.EffectiveBudgetPolicyId = (r) + } + } + if c.mode == EmptyFields && v.App.EffectiveUsagePolicyId == "" && Forced(v.App.ForceSendFields, "EffectiveUsagePolicyId") { + ch := n.Child("effective_usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.App.EffectiveUsagePolicyId); c.match(s) { + ch := n.Child("effective_usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.App.EffectiveUsagePolicyId = (r) + } + } + if !n.IsRef("effective_user_api_scopes") { + if len(v.App.EffectiveUserApiScopes) > 0 { + cn := n.Child("effective_user_api_scopes") + for i := range v.App.EffectiveUserApiScopes { + if s := string(v.App.EffectiveUserApiScopes[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.App.EffectiveUserApiScopes[i] = (r) + } + } + } + } + } + if !n.IsRef("git_repository") { + if x := v.App.GitRepository; x != nil { + ch := n.Child("git_repository") + stringsAppsGitRepository(c, &ch, x) + } + } + if !n.IsRef("git_source") { + if x := v.App.GitSource; x != nil { + ch := n.Child("git_source") + stringsAppsGitSource(c, &ch, x) + } + } + if s := string(v.App.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.App.Name = (r) + } + } + if c.mode == EmptyFields && v.App.Oauth2AppClientId == "" && Forced(v.App.ForceSendFields, "Oauth2AppClientId") { + ch := n.Child("oauth2_app_client_id") + c.fn(&ch, "") + } + if s := string(v.App.Oauth2AppClientId); c.match(s) { + ch := n.Child("oauth2_app_client_id") + if r, ok := c.fn(&ch, s); ok { + v.App.Oauth2AppClientId = (r) + } + } + if c.mode == EmptyFields && v.App.Oauth2AppIntegrationId == "" && Forced(v.App.ForceSendFields, "Oauth2AppIntegrationId") { + ch := n.Child("oauth2_app_integration_id") + c.fn(&ch, "") + } + if s := string(v.App.Oauth2AppIntegrationId); c.match(s) { + ch := n.Child("oauth2_app_integration_id") + if r, ok := c.fn(&ch, s); ok { + v.App.Oauth2AppIntegrationId = (r) + } + } + if !n.IsRef("pending_deployment") { + if x := v.App.PendingDeployment; x != nil { + ch := n.Child("pending_deployment") + stringsAppsAppDeployment(c, &ch, x) + } + } + if !n.IsRef("resources") { + if len(v.App.Resources) > 0 { + cn := n.Child("resources") + for i := range v.App.Resources { + { + ch := cn.Elem(i) + stringsAppsAppResource(c, &ch, &v.App.Resources[i]) + } + } + } + } + if c.mode == EmptyFields && v.App.ServicePrincipalClientId == "" && Forced(v.App.ForceSendFields, "ServicePrincipalClientId") { + ch := n.Child("service_principal_client_id") + c.fn(&ch, "") + } + if s := string(v.App.ServicePrincipalClientId); c.match(s) { + ch := n.Child("service_principal_client_id") + if r, ok := c.fn(&ch, s); ok { + v.App.ServicePrincipalClientId = (r) + } + } + if c.mode == EmptyFields && v.App.ServicePrincipalName == "" && Forced(v.App.ForceSendFields, "ServicePrincipalName") { + ch := n.Child("service_principal_name") + c.fn(&ch, "") + } + if s := string(v.App.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.App.ServicePrincipalName = (r) + } + } + if c.mode == EmptyFields && v.App.SourceCodePath == "" && Forced(v.App.ForceSendFields, "SourceCodePath") { + ch := n.Child("source_code_path") + c.fn(&ch, "") + } + if s := string(v.App.SourceCodePath); c.match(s) { + ch := n.Child("source_code_path") + if r, ok := c.fn(&ch, s); ok { + v.App.SourceCodePath = (r) + } + } + if c.mode == EmptyFields && v.App.Space == "" && Forced(v.App.ForceSendFields, "Space") { + ch := n.Child("space") + c.fn(&ch, "") + } + if s := string(v.App.Space); c.match(s) { + ch := n.Child("space") + if r, ok := c.fn(&ch, s); ok { + v.App.Space = (r) + } + } + if !n.IsRef("telemetry_export_destinations") { + if len(v.App.TelemetryExportDestinations) > 0 { + cn := n.Child("telemetry_export_destinations") + for i := range v.App.TelemetryExportDestinations { + { + ch := cn.Elem(i) + stringsAppsTelemetryExportDestination(c, &ch, &v.App.TelemetryExportDestinations[i]) + } + } + } + } + if c.mode == EmptyFields && v.App.ThumbnailUrl == "" && Forced(v.App.ForceSendFields, "ThumbnailUrl") { + ch := n.Child("thumbnail_url") + c.fn(&ch, "") + } + if s := string(v.App.ThumbnailUrl); c.match(s) { + ch := n.Child("thumbnail_url") + if r, ok := c.fn(&ch, s); ok { + v.App.ThumbnailUrl = (r) + } + } + if c.mode == EmptyFields && v.App.UpdateTime == "" && Forced(v.App.ForceSendFields, "UpdateTime") { + ch := n.Child("update_time") + c.fn(&ch, "") + } + if s := string(v.App.UpdateTime); c.match(s) { + ch := n.Child("update_time") + if r, ok := c.fn(&ch, s); ok { + v.App.UpdateTime = (r) + } + } + if c.mode == EmptyFields && v.App.Updater == "" && Forced(v.App.ForceSendFields, "Updater") { + ch := n.Child("updater") + c.fn(&ch, "") + } + if s := string(v.App.Updater); c.match(s) { + ch := n.Child("updater") + if r, ok := c.fn(&ch, s); ok { + v.App.Updater = (r) + } + } + if c.mode == EmptyFields && v.App.UsagePolicyId == "" && Forced(v.App.ForceSendFields, "UsagePolicyId") { + ch := n.Child("usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.App.UsagePolicyId); c.match(s) { + ch := n.Child("usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.App.UsagePolicyId = (r) + } + } + if !n.IsRef("user_api_scopes") { + if len(v.App.UserApiScopes) > 0 { + cn := n.Child("user_api_scopes") + for i := range v.App.UserApiScopes { + if s := string(v.App.UserApiScopes[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.App.UserApiScopes[i] = (r) + } + } + } + } + } +} + +func stringsResourcesSecretScope(c *stringsWalk, n *Node, v *resources.SecretScope) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesSecretScopePermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BackendType); c.match(s) { + ch := n.Child("backend_type") + if r, ok := c.fn(&ch, s); ok { + v.BackendType = workspace.ScopeBackendType(r) + } + } + if !n.IsRef("keyvault_metadata") { + if x := v.KeyvaultMetadata; x != nil { + ch := n.Child("keyvault_metadata") + stringsWorkspaceAzureKeyVaultSecretScopeMetadata(c, &ch, x) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } +} + +func stringsResourcesAlert(c *stringsWalk, n *Node, v *resources.Alert) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.FilePath); c.match(s) { + ch := n.Child("file_path") + if r, ok := c.fn(&ch, s); ok { + v.FilePath = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.AlertV2.CreateTime == "" && Forced(v.AlertV2.ForceSendFields, "CreateTime") { + ch := n.Child("create_time") + c.fn(&ch, "") + } + if s := string(v.AlertV2.CreateTime); c.match(s) { + ch := n.Child("create_time") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.CreateTime = (r) + } + } + if c.mode == EmptyFields && v.AlertV2.CustomDescription == "" && Forced(v.AlertV2.ForceSendFields, "CustomDescription") { + ch := n.Child("custom_description") + c.fn(&ch, "") + } + if s := string(v.AlertV2.CustomDescription); c.match(s) { + ch := n.Child("custom_description") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.CustomDescription = (r) + } + } + if c.mode == EmptyFields && v.AlertV2.CustomSummary == "" && Forced(v.AlertV2.ForceSendFields, "CustomSummary") { + ch := n.Child("custom_summary") + c.fn(&ch, "") + } + if s := string(v.AlertV2.CustomSummary); c.match(s) { + ch := n.Child("custom_summary") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.CustomSummary = (r) + } + } + if c.mode == EmptyFields && v.AlertV2.CustomTemplateFormat == "" && Forced(v.AlertV2.ForceSendFields, "CustomTemplateFormat") { + ch := n.Child("custom_template_format") + c.fn(&ch, "") + } + if s := string(v.AlertV2.CustomTemplateFormat); c.match(s) { + ch := n.Child("custom_template_format") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.CustomTemplateFormat = sql.CustomTemplateFormat(r) + } + } + if s := string(v.AlertV2.DisplayName); c.match(s) { + ch := n.Child("display_name") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.DisplayName = (r) + } + } + if !n.IsRef("effective_run_as") { + if x := v.AlertV2.EffectiveRunAs; x != nil { + ch := n.Child("effective_run_as") + stringsSqlAlertV2RunAs(c, &ch, x) + } + } + if !n.IsRef("evaluation") { + { + ch := n.Child("evaluation") + stringsSqlAlertV2Evaluation(c, &ch, &v.AlertV2.Evaluation) + } + } + if c.mode == EmptyFields && v.AlertV2.LifecycleState == "" && Forced(v.AlertV2.ForceSendFields, "LifecycleState") { + ch := n.Child("lifecycle_state") + c.fn(&ch, "") + } + if s := string(v.AlertV2.LifecycleState); c.match(s) { + ch := n.Child("lifecycle_state") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.LifecycleState = sql.AlertLifecycleState(r) + } + } + if c.mode == EmptyFields && v.AlertV2.OwnerUserName == "" && Forced(v.AlertV2.ForceSendFields, "OwnerUserName") { + ch := n.Child("owner_user_name") + c.fn(&ch, "") + } + if s := string(v.AlertV2.OwnerUserName); c.match(s) { + ch := n.Child("owner_user_name") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.OwnerUserName = (r) + } + } + if !n.IsRef("parameters") { + if len(v.AlertV2.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.AlertV2.Parameters { + { + ch := cn.Elem(i) + stringsSqlAlertStatementParameter(c, &ch, &v.AlertV2.Parameters[i]) + } + } + } + } + if c.mode == EmptyFields && v.AlertV2.ParentPath == "" && Forced(v.AlertV2.ForceSendFields, "ParentPath") { + ch := n.Child("parent_path") + c.fn(&ch, "") + } + if s := string(v.AlertV2.ParentPath); c.match(s) { + ch := n.Child("parent_path") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.ParentPath = (r) + } + } + if s := string(v.AlertV2.QueryText); c.match(s) { + ch := n.Child("query_text") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.QueryText = (r) + } + } + if !n.IsRef("run_as") { + if x := v.AlertV2.RunAs; x != nil { + ch := n.Child("run_as") + stringsSqlAlertV2RunAs(c, &ch, x) + } + } + if c.mode == EmptyFields && v.AlertV2.RunAsUserName == "" && Forced(v.AlertV2.ForceSendFields, "RunAsUserName") { + ch := n.Child("run_as_user_name") + c.fn(&ch, "") + } + if s := string(v.AlertV2.RunAsUserName); c.match(s) { + ch := n.Child("run_as_user_name") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.RunAsUserName = (r) + } + } + if !n.IsRef("schedule") { + { + ch := n.Child("schedule") + stringsSqlCronSchedule(c, &ch, &v.AlertV2.Schedule) + } + } + if c.mode == EmptyFields && v.AlertV2.UpdateTime == "" && Forced(v.AlertV2.ForceSendFields, "UpdateTime") { + ch := n.Child("update_time") + c.fn(&ch, "") + } + if s := string(v.AlertV2.UpdateTime); c.match(s) { + ch := n.Child("update_time") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.UpdateTime = (r) + } + } + if s := string(v.AlertV2.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.AlertV2.WarehouseId = (r) + } + } +} + +func stringsResourcesSqlWarehouse(c *stringsWalk, n *Node, v *resources.SqlWarehouse) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("lifecycle") { + if x := v.Lifecycle; x != nil { + ch := n.Child("lifecycle") + stringsResourcesLifecycleWithStarted(c, &ch, x) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesSqlWarehousePermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("channel") { + if x := v.CreateWarehouseRequest.Channel; x != nil { + ch := n.Child("channel") + stringsSqlChannel(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.ClusterSize == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "ClusterSize") { + ch := n.Child("cluster_size") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.ClusterSize); c.match(s) { + ch := n.Child("cluster_size") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.ClusterSize = (r) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.CreatorName == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "CreatorName") { + ch := n.Child("creator_name") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.CreatorName); c.match(s) { + ch := n.Child("creator_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.CreatorName = (r) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.CustomerVersionLabel == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "CustomerVersionLabel") { + ch := n.Child("customer_version_label") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.CustomerVersionLabel); c.match(s) { + ch := n.Child("customer_version_label") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.CustomerVersionLabel = (r) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.InstanceProfileArn == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "InstanceProfileArn") { + ch := n.Child("instance_profile_arn") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.InstanceProfileArn); c.match(s) { + ch := n.Child("instance_profile_arn") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.InstanceProfileArn = (r) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.Name == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.Name = (r) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.SpotInstancePolicy == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "SpotInstancePolicy") { + ch := n.Child("spot_instance_policy") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.SpotInstancePolicy); c.match(s) { + ch := n.Child("spot_instance_policy") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.SpotInstancePolicy = sql.SpotInstancePolicy(r) + } + } + if !n.IsRef("tags") { + if x := v.CreateWarehouseRequest.Tags; x != nil { + ch := n.Child("tags") + stringsSqlEndpointTags(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CreateWarehouseRequest.WarehouseType == "" && Forced(v.CreateWarehouseRequest.ForceSendFields, "WarehouseType") { + ch := n.Child("warehouse_type") + c.fn(&ch, "") + } + if s := string(v.CreateWarehouseRequest.WarehouseType); c.match(s) { + ch := n.Child("warehouse_type") + if r, ok := c.fn(&ch, s); ok { + v.CreateWarehouseRequest.WarehouseType = sql.CreateWarehouseRequestWarehouseType(r) + } + } +} + +func stringsResourcesDatabaseInstance(c *stringsWalk, n *Node, v *resources.DatabaseInstance) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.Capacity == "" && Forced(v.DatabaseInstance.ForceSendFields, "Capacity") { + ch := n.Child("capacity") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.Capacity); c.match(s) { + ch := n.Child("capacity") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.Capacity = (r) + } + } + if !n.IsRef("child_instance_refs") { + if len(v.DatabaseInstance.ChildInstanceRefs) > 0 { + cn := n.Child("child_instance_refs") + for i := range v.DatabaseInstance.ChildInstanceRefs { + { + ch := cn.Elem(i) + stringsDatabaseDatabaseInstanceRef(c, &ch, &v.DatabaseInstance.ChildInstanceRefs[i]) + } + } + } + } + if c.mode == EmptyFields && v.DatabaseInstance.CreationTime == "" && Forced(v.DatabaseInstance.ForceSendFields, "CreationTime") { + ch := n.Child("creation_time") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.CreationTime); c.match(s) { + ch := n.Child("creation_time") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.CreationTime = (r) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.Creator == "" && Forced(v.DatabaseInstance.ForceSendFields, "Creator") { + ch := n.Child("creator") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.Creator); c.match(s) { + ch := n.Child("creator") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.Creator = (r) + } + } + if !n.IsRef("custom_tags") { + if len(v.DatabaseInstance.CustomTags) > 0 { + cn := n.Child("custom_tags") + for i := range v.DatabaseInstance.CustomTags { + { + ch := cn.Elem(i) + stringsDatabaseCustomTag(c, &ch, &v.DatabaseInstance.CustomTags[i]) + } + } + } + } + if c.mode == EmptyFields && v.DatabaseInstance.EffectiveCapacity == "" && Forced(v.DatabaseInstance.ForceSendFields, "EffectiveCapacity") { + ch := n.Child("effective_capacity") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.EffectiveCapacity); c.match(s) { + ch := n.Child("effective_capacity") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.EffectiveCapacity = (r) + } + } + if !n.IsRef("effective_custom_tags") { + if len(v.DatabaseInstance.EffectiveCustomTags) > 0 { + cn := n.Child("effective_custom_tags") + for i := range v.DatabaseInstance.EffectiveCustomTags { + { + ch := cn.Elem(i) + stringsDatabaseCustomTag(c, &ch, &v.DatabaseInstance.EffectiveCustomTags[i]) + } + } + } + } + if c.mode == EmptyFields && v.DatabaseInstance.EffectiveUsagePolicyId == "" && Forced(v.DatabaseInstance.ForceSendFields, "EffectiveUsagePolicyId") { + ch := n.Child("effective_usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.EffectiveUsagePolicyId); c.match(s) { + ch := n.Child("effective_usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.EffectiveUsagePolicyId = (r) + } + } + if s := string(v.DatabaseInstance.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.Name = (r) + } + } + if !n.IsRef("parent_instance_ref") { + if x := v.DatabaseInstance.ParentInstanceRef; x != nil { + ch := n.Child("parent_instance_ref") + stringsDatabaseDatabaseInstanceRef(c, &ch, x) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.PgVersion == "" && Forced(v.DatabaseInstance.ForceSendFields, "PgVersion") { + ch := n.Child("pg_version") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.PgVersion); c.match(s) { + ch := n.Child("pg_version") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.PgVersion = (r) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.ReadOnlyDns == "" && Forced(v.DatabaseInstance.ForceSendFields, "ReadOnlyDns") { + ch := n.Child("read_only_dns") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.ReadOnlyDns); c.match(s) { + ch := n.Child("read_only_dns") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.ReadOnlyDns = (r) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.ReadWriteDns == "" && Forced(v.DatabaseInstance.ForceSendFields, "ReadWriteDns") { + ch := n.Child("read_write_dns") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.ReadWriteDns); c.match(s) { + ch := n.Child("read_write_dns") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.ReadWriteDns = (r) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.State == "" && Forced(v.DatabaseInstance.ForceSendFields, "State") { + ch := n.Child("state") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.State); c.match(s) { + ch := n.Child("state") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.State = database.DatabaseInstanceState(r) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.Uid == "" && Forced(v.DatabaseInstance.ForceSendFields, "Uid") { + ch := n.Child("uid") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.Uid); c.match(s) { + ch := n.Child("uid") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.Uid = (r) + } + } + if c.mode == EmptyFields && v.DatabaseInstance.UsagePolicyId == "" && Forced(v.DatabaseInstance.ForceSendFields, "UsagePolicyId") { + ch := n.Child("usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.DatabaseInstance.UsagePolicyId); c.match(s) { + ch := n.Child("usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseInstance.UsagePolicyId = (r) + } + } +} + +func stringsResourcesDatabaseCatalog(c *stringsWalk, n *Node, v *resources.DatabaseCatalog) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.DatabaseCatalog.DatabaseInstanceName); c.match(s) { + ch := n.Child("database_instance_name") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseCatalog.DatabaseInstanceName = (r) + } + } + if s := string(v.DatabaseCatalog.DatabaseName); c.match(s) { + ch := n.Child("database_name") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseCatalog.DatabaseName = (r) + } + } + if s := string(v.DatabaseCatalog.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseCatalog.Name = (r) + } + } + if c.mode == EmptyFields && v.DatabaseCatalog.Uid == "" && Forced(v.DatabaseCatalog.ForceSendFields, "Uid") { + ch := n.Child("uid") + c.fn(&ch, "") + } + if s := string(v.DatabaseCatalog.Uid); c.match(s) { + ch := n.Child("uid") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseCatalog.Uid = (r) + } + } +} + +func stringsResourcesSyncedDatabaseTable(c *stringsWalk, n *Node, v *resources.SyncedDatabaseTable) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if !n.IsRef("data_synchronization_status") { + if x := v.SyncedDatabaseTable.DataSynchronizationStatus; x != nil { + ch := n.Child("data_synchronization_status") + stringsDatabaseSyncedTableStatus(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SyncedDatabaseTable.DatabaseInstanceName == "" && Forced(v.SyncedDatabaseTable.ForceSendFields, "DatabaseInstanceName") { + ch := n.Child("database_instance_name") + c.fn(&ch, "") + } + if s := string(v.SyncedDatabaseTable.DatabaseInstanceName); c.match(s) { + ch := n.Child("database_instance_name") + if r, ok := c.fn(&ch, s); ok { + v.SyncedDatabaseTable.DatabaseInstanceName = (r) + } + } + if c.mode == EmptyFields && v.SyncedDatabaseTable.EffectiveDatabaseInstanceName == "" && Forced(v.SyncedDatabaseTable.ForceSendFields, "EffectiveDatabaseInstanceName") { + ch := n.Child("effective_database_instance_name") + c.fn(&ch, "") + } + if s := string(v.SyncedDatabaseTable.EffectiveDatabaseInstanceName); c.match(s) { + ch := n.Child("effective_database_instance_name") + if r, ok := c.fn(&ch, s); ok { + v.SyncedDatabaseTable.EffectiveDatabaseInstanceName = (r) + } + } + if c.mode == EmptyFields && v.SyncedDatabaseTable.EffectiveLogicalDatabaseName == "" && Forced(v.SyncedDatabaseTable.ForceSendFields, "EffectiveLogicalDatabaseName") { + ch := n.Child("effective_logical_database_name") + c.fn(&ch, "") + } + if s := string(v.SyncedDatabaseTable.EffectiveLogicalDatabaseName); c.match(s) { + ch := n.Child("effective_logical_database_name") + if r, ok := c.fn(&ch, s); ok { + v.SyncedDatabaseTable.EffectiveLogicalDatabaseName = (r) + } + } + if c.mode == EmptyFields && v.SyncedDatabaseTable.LogicalDatabaseName == "" && Forced(v.SyncedDatabaseTable.ForceSendFields, "LogicalDatabaseName") { + ch := n.Child("logical_database_name") + c.fn(&ch, "") + } + if s := string(v.SyncedDatabaseTable.LogicalDatabaseName); c.match(s) { + ch := n.Child("logical_database_name") + if r, ok := c.fn(&ch, s); ok { + v.SyncedDatabaseTable.LogicalDatabaseName = (r) + } + } + if s := string(v.SyncedDatabaseTable.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.SyncedDatabaseTable.Name = (r) + } + } + if !n.IsRef("spec") { + if x := v.SyncedDatabaseTable.Spec; x != nil { + ch := n.Child("spec") + stringsDatabaseSyncedTableSpec(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SyncedDatabaseTable.UnityCatalogProvisioningState == "" && Forced(v.SyncedDatabaseTable.ForceSendFields, "UnityCatalogProvisioningState") { + ch := n.Child("unity_catalog_provisioning_state") + c.fn(&ch, "") + } + if s := string(v.SyncedDatabaseTable.UnityCatalogProvisioningState); c.match(s) { + ch := n.Child("unity_catalog_provisioning_state") + if r, ok := c.fn(&ch, s); ok { + v.SyncedDatabaseTable.UnityCatalogProvisioningState = database.ProvisioningInfoState(r) + } + } +} + +func stringsResourcesPostgresProject(c *stringsWalk, n *Node, v *resources.PostgresProject) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresProjectConfig.ProjectId); c.match(s) { + ch := n.Child("project_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresProjectConfig.ProjectId = (r) + } + } + if c.mode == EmptyFields && v.PostgresProjectConfig.ProjectSpec.BudgetPolicyId == "" && Forced(v.PostgresProjectConfig.ProjectSpec.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.PostgresProjectConfig.ProjectSpec.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresProjectConfig.ProjectSpec.BudgetPolicyId = (r) + } + } + if !n.IsRef("custom_tags") { + if len(v.PostgresProjectConfig.ProjectSpec.CustomTags) > 0 { + cn := n.Child("custom_tags") + for i := range v.PostgresProjectConfig.ProjectSpec.CustomTags { + { + ch := cn.Elem(i) + stringsPostgresProjectCustomTag(c, &ch, &v.PostgresProjectConfig.ProjectSpec.CustomTags[i]) + } + } + } + } + if c.mode == EmptyFields && v.PostgresProjectConfig.ProjectSpec.DefaultBranch == "" && Forced(v.PostgresProjectConfig.ProjectSpec.ForceSendFields, "DefaultBranch") { + ch := n.Child("default_branch") + c.fn(&ch, "") + } + if s := string(v.PostgresProjectConfig.ProjectSpec.DefaultBranch); c.match(s) { + ch := n.Child("default_branch") + if r, ok := c.fn(&ch, s); ok { + v.PostgresProjectConfig.ProjectSpec.DefaultBranch = (r) + } + } + if !n.IsRef("default_endpoint_settings") { + if x := v.PostgresProjectConfig.ProjectSpec.DefaultEndpointSettings; x != nil { + ch := n.Child("default_endpoint_settings") + stringsPostgresProjectDefaultEndpointSettings(c, &ch, x) + } + } + if c.mode == EmptyFields && v.PostgresProjectConfig.ProjectSpec.DisplayName == "" && Forced(v.PostgresProjectConfig.ProjectSpec.ForceSendFields, "DisplayName") { + ch := n.Child("display_name") + c.fn(&ch, "") + } + if s := string(v.PostgresProjectConfig.ProjectSpec.DisplayName); c.match(s) { + ch := n.Child("display_name") + if r, ok := c.fn(&ch, s); ok { + v.PostgresProjectConfig.ProjectSpec.DisplayName = (r) + } + } +} + +func stringsResourcesPostgresBranch(c *stringsWalk, n *Node, v *resources.PostgresBranch) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresBranchConfig.BranchId); c.match(s) { + ch := n.Child("branch_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresBranchConfig.BranchId = (r) + } + } + if s := string(v.PostgresBranchConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.PostgresBranchConfig.Parent = (r) + } + } + if c.mode == EmptyFields && v.PostgresBranchConfig.BranchSpec.SourceBranch == "" && Forced(v.PostgresBranchConfig.BranchSpec.ForceSendFields, "SourceBranch") { + ch := n.Child("source_branch") + c.fn(&ch, "") + } + if s := string(v.PostgresBranchConfig.BranchSpec.SourceBranch); c.match(s) { + ch := n.Child("source_branch") + if r, ok := c.fn(&ch, s); ok { + v.PostgresBranchConfig.BranchSpec.SourceBranch = (r) + } + } + if c.mode == EmptyFields && v.PostgresBranchConfig.BranchSpec.SourceBranchLsn == "" && Forced(v.PostgresBranchConfig.BranchSpec.ForceSendFields, "SourceBranchLsn") { + ch := n.Child("source_branch_lsn") + c.fn(&ch, "") + } + if s := string(v.PostgresBranchConfig.BranchSpec.SourceBranchLsn); c.match(s) { + ch := n.Child("source_branch_lsn") + if r, ok := c.fn(&ch, s); ok { + v.PostgresBranchConfig.BranchSpec.SourceBranchLsn = (r) + } + } + if c.mode == EmptyFields && v.PostgresBranchConfig.BranchSpec.SourceSnapshot == "" && Forced(v.PostgresBranchConfig.BranchSpec.ForceSendFields, "SourceSnapshot") { + ch := n.Child("source_snapshot") + c.fn(&ch, "") + } + if s := string(v.PostgresBranchConfig.BranchSpec.SourceSnapshot); c.match(s) { + ch := n.Child("source_snapshot") + if r, ok := c.fn(&ch, s); ok { + v.PostgresBranchConfig.BranchSpec.SourceSnapshot = (r) + } + } +} + +func stringsResourcesPostgresEndpoint(c *stringsWalk, n *Node, v *resources.PostgresEndpoint) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresEndpointConfig.EndpointId); c.match(s) { + ch := n.Child("endpoint_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresEndpointConfig.EndpointId = (r) + } + } + if s := string(v.PostgresEndpointConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.PostgresEndpointConfig.Parent = (r) + } + } + if s := string(v.PostgresEndpointConfig.EndpointSpec.EndpointType); c.match(s) { + ch := n.Child("endpoint_type") + if r, ok := c.fn(&ch, s); ok { + v.PostgresEndpointConfig.EndpointSpec.EndpointType = postgres.EndpointType(r) + } + } + if !n.IsRef("settings") { + if x := v.PostgresEndpointConfig.EndpointSpec.Settings; x != nil { + ch := n.Child("settings") + stringsPostgresEndpointSettings(c, &ch, x) + } + } +} + +func stringsResourcesPostgresCatalog(c *stringsWalk, n *Node, v *resources.PostgresCatalog) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresCatalogConfig.CatalogId); c.match(s) { + ch := n.Child("catalog_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresCatalogConfig.CatalogId = (r) + } + } + if c.mode == EmptyFields && v.PostgresCatalogConfig.CatalogCatalogSpec.Branch == "" && Forced(v.PostgresCatalogConfig.CatalogCatalogSpec.ForceSendFields, "Branch") { + ch := n.Child("branch") + c.fn(&ch, "") + } + if s := string(v.PostgresCatalogConfig.CatalogCatalogSpec.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.PostgresCatalogConfig.CatalogCatalogSpec.Branch = (r) + } + } + if s := string(v.PostgresCatalogConfig.CatalogCatalogSpec.PostgresDatabase); c.match(s) { + ch := n.Child("postgres_database") + if r, ok := c.fn(&ch, s); ok { + v.PostgresCatalogConfig.CatalogCatalogSpec.PostgresDatabase = (r) + } + } +} + +func stringsResourcesPostgresDatabase(c *stringsWalk, n *Node, v *resources.PostgresDatabase) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresDatabaseConfig.DatabaseId); c.match(s) { + ch := n.Child("database_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresDatabaseConfig.DatabaseId = (r) + } + } + if s := string(v.PostgresDatabaseConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.PostgresDatabaseConfig.Parent = (r) + } + } + if c.mode == EmptyFields && v.PostgresDatabaseConfig.DatabaseDatabaseSpec.PostgresDatabase == "" && Forced(v.PostgresDatabaseConfig.DatabaseDatabaseSpec.ForceSendFields, "PostgresDatabase") { + ch := n.Child("postgres_database") + c.fn(&ch, "") + } + if s := string(v.PostgresDatabaseConfig.DatabaseDatabaseSpec.PostgresDatabase); c.match(s) { + ch := n.Child("postgres_database") + if r, ok := c.fn(&ch, s); ok { + v.PostgresDatabaseConfig.DatabaseDatabaseSpec.PostgresDatabase = (r) + } + } + if s := string(v.PostgresDatabaseConfig.DatabaseDatabaseSpec.Role); c.match(s) { + ch := n.Child("role") + if r, ok := c.fn(&ch, s); ok { + v.PostgresDatabaseConfig.DatabaseDatabaseSpec.Role = (r) + } + } +} + +func stringsResourcesPostgresRole(c *stringsWalk, n *Node, v *resources.PostgresRole) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresRoleConfig.RoleId); c.match(s) { + ch := n.Child("role_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresRoleConfig.RoleId = (r) + } + } + if s := string(v.PostgresRoleConfig.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.PostgresRoleConfig.Parent = (r) + } + } + if c.mode == EmptyFields && v.PostgresRoleConfig.RoleRoleSpec.AuthMethod == "" && Forced(v.PostgresRoleConfig.RoleRoleSpec.ForceSendFields, "AuthMethod") { + ch := n.Child("auth_method") + c.fn(&ch, "") + } + if s := string(v.PostgresRoleConfig.RoleRoleSpec.AuthMethod); c.match(s) { + ch := n.Child("auth_method") + if r, ok := c.fn(&ch, s); ok { + v.PostgresRoleConfig.RoleRoleSpec.AuthMethod = postgres.RoleAuthMethod(r) + } + } + if c.mode == EmptyFields && v.PostgresRoleConfig.RoleRoleSpec.IdentityType == "" && Forced(v.PostgresRoleConfig.RoleRoleSpec.ForceSendFields, "IdentityType") { + ch := n.Child("identity_type") + c.fn(&ch, "") + } + if s := string(v.PostgresRoleConfig.RoleRoleSpec.IdentityType); c.match(s) { + ch := n.Child("identity_type") + if r, ok := c.fn(&ch, s); ok { + v.PostgresRoleConfig.RoleRoleSpec.IdentityType = postgres.RoleIdentityType(r) + } + } + if !n.IsRef("membership_roles") { + if len(v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles) > 0 { + cn := n.Child("membership_roles") + for i := range v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles { + if s := string(v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.PostgresRoleConfig.RoleRoleSpec.MembershipRoles[i] = postgres.RoleMembershipRole(r) + } + } + } + } + } + if c.mode == EmptyFields && v.PostgresRoleConfig.RoleRoleSpec.PostgresRole == "" && Forced(v.PostgresRoleConfig.RoleRoleSpec.ForceSendFields, "PostgresRole") { + ch := n.Child("postgres_role") + c.fn(&ch, "") + } + if s := string(v.PostgresRoleConfig.RoleRoleSpec.PostgresRole); c.match(s) { + ch := n.Child("postgres_role") + if r, ok := c.fn(&ch, s); ok { + v.PostgresRoleConfig.RoleRoleSpec.PostgresRole = (r) + } + } +} + +func stringsResourcesPostgresSyncedTable(c *stringsWalk, n *Node, v *resources.PostgresSyncedTable) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableId); c.match(s) { + ch := n.Child("synced_table_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableId = (r) + } + } + if c.mode == EmptyFields && v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.Branch == "" && Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "Branch") { + ch := n.Child("branch") + c.fn(&ch, "") + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.Branch = (r) + } + } + if c.mode == EmptyFields && v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExistingPipelineId == "" && Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "ExistingPipelineId") { + ch := n.Child("existing_pipeline_id") + c.fn(&ch, "") + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExistingPipelineId); c.match(s) { + ch := n.Child("existing_pipeline_id") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExistingPipelineId = (r) + } + } + if !n.IsRef("extra_columns") { + if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns) > 0 { + cn := n.Child("extra_columns") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns { + { + ch := cn.Elem(i) + stringsPostgresSyncedTableSyncedTableSpecExtraColumn(c, &ch, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ExtraColumns[i]) + } + } + } + } + if !n.IsRef("new_pipeline_spec") { + if x := v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.NewPipelineSpec; x != nil { + ch := n.Child("new_pipeline_spec") + stringsPostgresNewPipelineSpec(c, &ch, x) + } + } + if c.mode == EmptyFields && v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PostgresDatabase == "" && Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "PostgresDatabase") { + ch := n.Child("postgres_database") + c.fn(&ch, "") + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PostgresDatabase); c.match(s) { + ch := n.Child("postgres_database") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PostgresDatabase = (r) + } + } + if !n.IsRef("primary_key_columns") { + if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PrimaryKeyColumns) > 0 { + cn := n.Child("primary_key_columns") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PrimaryKeyColumns { + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PrimaryKeyColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.PrimaryKeyColumns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SchedulingPolicy == "" && Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "SchedulingPolicy") { + ch := n.Child("scheduling_policy") + c.fn(&ch, "") + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SchedulingPolicy); c.match(s) { + ch := n.Child("scheduling_policy") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SchedulingPolicy = postgres.SyncedTableSyncedTableSpecSyncedTableSchedulingPolicy(r) + } + } + if c.mode == EmptyFields && v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SourceTableFullName == "" && Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "SourceTableFullName") { + ch := n.Child("source_table_full_name") + c.fn(&ch, "") + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SourceTableFullName); c.match(s) { + ch := n.Child("source_table_full_name") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.SourceTableFullName = (r) + } + } + if c.mode == EmptyFields && v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TimeseriesKey == "" && Forced(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.ForceSendFields, "TimeseriesKey") { + ch := n.Child("timeseries_key") + c.fn(&ch, "") + } + if s := string(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TimeseriesKey); c.match(s) { + ch := n.Child("timeseries_key") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TimeseriesKey = (r) + } + } + if !n.IsRef("type_overrides") { + if len(v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides) > 0 { + cn := n.Child("type_overrides") + for i := range v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides { + { + ch := cn.Elem(i) + stringsPostgresSyncedTableSyncedTableSpecTypeOverride(c, &ch, &v.PostgresSyncedTableConfig.SyncedTableSyncedTableSpec.TypeOverrides[i]) + } + } + } + } +} + +func stringsResourcesPostgresSnapshotSchedule(c *stringsWalk, n *Node, v *resources.PostgresSnapshotSchedule) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.PostgresSnapshotScheduleConfig.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.PostgresSnapshotScheduleConfig.Branch = (r) + } + } + if !n.IsRef("schedule") { + if len(v.PostgresSnapshotScheduleConfig.Schedule) > 0 { + cn := n.Child("schedule") + for i := range v.PostgresSnapshotScheduleConfig.Schedule { + { + ch := cn.Elem(i) + stringsPostgresScheduleCadence(c, &ch, &v.PostgresSnapshotScheduleConfig.Schedule[i]) + } + } + } + } +} + +func stringsResourcesVectorSearchEndpoint(c *stringsWalk, n *Node, v *resources.VectorSearchEndpoint) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesVectorSearchEndpointPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.CreateEndpoint.BudgetPolicyId == "" && Forced(v.CreateEndpoint.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.CreateEndpoint.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.CreateEndpoint.BudgetPolicyId = (r) + } + } + if s := string(v.CreateEndpoint.EndpointType); c.match(s) { + ch := n.Child("endpoint_type") + if r, ok := c.fn(&ch, s); ok { + v.CreateEndpoint.EndpointType = vectorsearch.EndpointType(r) + } + } + if s := string(v.CreateEndpoint.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateEndpoint.Name = (r) + } + } + if c.mode == EmptyFields && v.CreateEndpoint.UsagePolicyId == "" && Forced(v.CreateEndpoint.ForceSendFields, "UsagePolicyId") { + ch := n.Child("usage_policy_id") + c.fn(&ch, "") + } + if s := string(v.CreateEndpoint.UsagePolicyId); c.match(s) { + ch := n.Child("usage_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.CreateEndpoint.UsagePolicyId = (r) + } + } +} + +func stringsResourcesVectorSearchIndex(c *stringsWalk, n *Node, v *resources.VectorSearchIndex) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if !n.IsRef("delta_sync_index_spec") { + if x := v.CreateVectorIndexRequest.DeltaSyncIndexSpec; x != nil { + ch := n.Child("delta_sync_index_spec") + stringsVectorsearchDeltaSyncVectorIndexSpecRequest(c, &ch, x) + } + } + if !n.IsRef("direct_access_index_spec") { + if x := v.CreateVectorIndexRequest.DirectAccessIndexSpec; x != nil { + ch := n.Child("direct_access_index_spec") + stringsVectorsearchDirectAccessVectorIndexSpec(c, &ch, x) + } + } + if s := string(v.CreateVectorIndexRequest.EndpointName); c.match(s) { + ch := n.Child("endpoint_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateVectorIndexRequest.EndpointName = (r) + } + } + if s := string(v.CreateVectorIndexRequest.IndexSubtype); c.match(s) { + ch := n.Child("index_subtype") + if r, ok := c.fn(&ch, s); ok { + v.CreateVectorIndexRequest.IndexSubtype = vectorsearch.IndexSubtype(r) + } + } + if s := string(v.CreateVectorIndexRequest.IndexType); c.match(s) { + ch := n.Child("index_type") + if r, ok := c.fn(&ch, s); ok { + v.CreateVectorIndexRequest.IndexType = vectorsearch.VectorIndexType(r) + } + } + if s := string(v.CreateVectorIndexRequest.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreateVectorIndexRequest.Name = (r) + } + } + if s := string(v.CreateVectorIndexRequest.PrimaryKey); c.match(s) { + ch := n.Child("primary_key") + if r, ok := c.fn(&ch, s); ok { + v.CreateVectorIndexRequest.PrimaryKey = (r) + } + } +} + +func stringsResourcesInstancePool(c *stringsWalk, n *Node, v *resources.InstancePool) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesInstancePoolPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if !n.IsRef("aws_attributes") { + if x := v.CreateInstancePool.AwsAttributes; x != nil { + ch := n.Child("aws_attributes") + stringsComputeInstancePoolAwsAttributes(c, &ch, x) + } + } + if !n.IsRef("azure_attributes") { + if x := v.CreateInstancePool.AzureAttributes; x != nil { + ch := n.Child("azure_attributes") + stringsComputeInstancePoolAzureAttributes(c, &ch, x) + } + } + if !n.IsRef("custom_tags") { + if len(v.CreateInstancePool.CustomTags) > 0 { + cn := n.Child("custom_tags") + for _, k := range sortedKeys(v.CreateInstancePool.CustomTags) { + if s := string(v.CreateInstancePool.CustomTags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CreateInstancePool.CustomTags[k] = (r) + } + } + } + } + } + if !n.IsRef("disk_spec") { + if x := v.CreateInstancePool.DiskSpec; x != nil { + ch := n.Child("disk_spec") + stringsComputeDiskSpec(c, &ch, x) + } + } + if !n.IsRef("gcp_attributes") { + if x := v.CreateInstancePool.GcpAttributes; x != nil { + ch := n.Child("gcp_attributes") + stringsComputeInstancePoolGcpAttributes(c, &ch, x) + } + } + if s := string(v.CreateInstancePool.InstancePoolName); c.match(s) { + ch := n.Child("instance_pool_name") + if r, ok := c.fn(&ch, s); ok { + v.CreateInstancePool.InstancePoolName = (r) + } + } + if !n.IsRef("node_type_flexibility") { + if x := v.CreateInstancePool.NodeTypeFlexibility; x != nil { + ch := n.Child("node_type_flexibility") + stringsComputeNodeTypeFlexibility(c, &ch, x) + } + } + if s := string(v.CreateInstancePool.NodeTypeId); c.match(s) { + ch := n.Child("node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.CreateInstancePool.NodeTypeId = (r) + } + } + if !n.IsRef("preloaded_docker_images") { + if len(v.CreateInstancePool.PreloadedDockerImages) > 0 { + cn := n.Child("preloaded_docker_images") + for i := range v.CreateInstancePool.PreloadedDockerImages { + { + ch := cn.Elem(i) + stringsComputeDockerImage(c, &ch, &v.CreateInstancePool.PreloadedDockerImages[i]) + } + } + } + } + if !n.IsRef("preloaded_spark_versions") { + if len(v.CreateInstancePool.PreloadedSparkVersions) > 0 { + cn := n.Child("preloaded_spark_versions") + for i := range v.CreateInstancePool.PreloadedSparkVersions { + if s := string(v.CreateInstancePool.PreloadedSparkVersions[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.CreateInstancePool.PreloadedSparkVersions[i] = (r) + } + } + } + } + } +} + +func stringsResourcesSecret(c *stringsWalk, n *Node, v *resources.Secret) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if !n.IsRef("grants") { + if len(v.Grants) > 0 { + cn := n.Child("grants") + for i := range v.Grants { + { + ch := cn.Elem(i) + stringsCatalogPrivilegeAssignment(c, &ch, &v.Grants[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if s := string(v.Secret.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.Secret.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.Secret.Comment == "" && Forced(v.Secret.ForceSendFields, "Comment") { + ch := n.Child("comment") + c.fn(&ch, "") + } + if s := string(v.Secret.Comment); c.match(s) { + ch := n.Child("comment") + if r, ok := c.fn(&ch, s); ok { + v.Secret.Comment = (r) + } + } + if c.mode == EmptyFields && v.Secret.CreatedBy == "" && Forced(v.Secret.ForceSendFields, "CreatedBy") { + ch := n.Child("created_by") + c.fn(&ch, "") + } + if s := string(v.Secret.CreatedBy); c.match(s) { + ch := n.Child("created_by") + if r, ok := c.fn(&ch, s); ok { + v.Secret.CreatedBy = (r) + } + } + if c.mode == EmptyFields && v.Secret.EffectiveOwner == "" && Forced(v.Secret.ForceSendFields, "EffectiveOwner") { + ch := n.Child("effective_owner") + c.fn(&ch, "") + } + if s := string(v.Secret.EffectiveOwner); c.match(s) { + ch := n.Child("effective_owner") + if r, ok := c.fn(&ch, s); ok { + v.Secret.EffectiveOwner = (r) + } + } + if c.mode == EmptyFields && v.Secret.EffectiveValue == "" && Forced(v.Secret.ForceSendFields, "EffectiveValue") { + ch := n.Child("effective_value") + c.fn(&ch, "") + } + if s := string(v.Secret.EffectiveValue); c.match(s) { + ch := n.Child("effective_value") + if r, ok := c.fn(&ch, s); ok { + v.Secret.EffectiveValue = (r) + } + } + if c.mode == EmptyFields && v.Secret.FullName == "" && Forced(v.Secret.ForceSendFields, "FullName") { + ch := n.Child("full_name") + c.fn(&ch, "") + } + if s := string(v.Secret.FullName); c.match(s) { + ch := n.Child("full_name") + if r, ok := c.fn(&ch, s); ok { + v.Secret.FullName = (r) + } + } + if c.mode == EmptyFields && v.Secret.MetastoreId == "" && Forced(v.Secret.ForceSendFields, "MetastoreId") { + ch := n.Child("metastore_id") + c.fn(&ch, "") + } + if s := string(v.Secret.MetastoreId); c.match(s) { + ch := n.Child("metastore_id") + if r, ok := c.fn(&ch, s); ok { + v.Secret.MetastoreId = (r) + } + } + if s := string(v.Secret.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Secret.Name = (r) + } + } + if c.mode == EmptyFields && v.Secret.Owner == "" && Forced(v.Secret.ForceSendFields, "Owner") { + ch := n.Child("owner") + c.fn(&ch, "") + } + if s := string(v.Secret.Owner); c.match(s) { + ch := n.Child("owner") + if r, ok := c.fn(&ch, s); ok { + v.Secret.Owner = (r) + } + } + if s := string(v.Secret.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.Secret.SchemaName = (r) + } + } + if c.mode == EmptyFields && v.Secret.UpdatedBy == "" && Forced(v.Secret.ForceSendFields, "UpdatedBy") { + ch := n.Child("updated_by") + c.fn(&ch, "") + } + if s := string(v.Secret.UpdatedBy); c.match(s) { + ch := n.Child("updated_by") + if r, ok := c.fn(&ch, s); ok { + v.Secret.UpdatedBy = (r) + } + } + if s := string(v.Secret.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Secret.Value = (r) + } + } +} + +func stringsResourcesClusterPolicy(c *stringsWalk, n *Node, v *resources.ClusterPolicy) { + if n.isRef() { + return + } + if s := string(v.ID); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.ID = (r) + } + } + if c.mode == EmptyFields && v.Definition == "" { + ch := n.Child("definition") + c.fn(&ch, "") + } + if v.Definition != nil { + ch := n.Child("definition") + c.any(&ch, &v.Definition) + } + if c.mode == EmptyFields && v.PolicyFamilyDefinitionOverrides == "" { + ch := n.Child("policy_family_definition_overrides") + c.fn(&ch, "") + } + if v.PolicyFamilyDefinitionOverrides != nil { + ch := n.Child("policy_family_definition_overrides") + c.any(&ch, &v.PolicyFamilyDefinitionOverrides) + } + if !n.IsRef("permissions") { + if len(v.Permissions) > 0 { + cn := n.Child("permissions") + for i := range v.Permissions { + { + ch := cn.Elem(i) + stringsResourcesClusterPolicyPermission(c, &ch, &v.Permissions[i]) + } + } + } + } + if s := string(v.BaseResource.URL); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.URL = (r) + } + } + if s := string(v.BaseResource.ModifiedStatus); c.match(s) { + ch := n.Child("modified_status") + if r, ok := c.fn(&ch, s); ok { + v.BaseResource.ModifiedStatus = (r) + } + } + if !n.IsRef("lifecycle") { + { + ch := n.Child("lifecycle") + stringsResourcesLifecycle(c, &ch, &v.BaseResource.Lifecycle) + } + } + if c.mode == EmptyFields && v.CreatePolicy.Description == "" && Forced(v.CreatePolicy.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.CreatePolicy.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.CreatePolicy.Description = (r) + } + } + if !n.IsRef("libraries") { + if len(v.CreatePolicy.Libraries) > 0 { + cn := n.Child("libraries") + for i := range v.CreatePolicy.Libraries { + { + ch := cn.Elem(i) + stringsComputeLibrary(c, &ch, &v.CreatePolicy.Libraries[i]) + } + } + } + } + if c.mode == EmptyFields && v.CreatePolicy.Name == "" && Forced(v.CreatePolicy.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.CreatePolicy.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.CreatePolicy.Name = (r) + } + } + if c.mode == EmptyFields && v.CreatePolicy.PolicyFamilyId == "" && Forced(v.CreatePolicy.ForceSendFields, "PolicyFamilyId") { + ch := n.Child("policy_family_id") + c.fn(&ch, "") + } + if s := string(v.CreatePolicy.PolicyFamilyId); c.match(s) { + ch := n.Child("policy_family_id") + if r, ok := c.fn(&ch, s); ok { + v.CreatePolicy.PolicyFamilyId = (r) + } + } +} + +func stringsResourcesSnapshot(c *stringsWalk, n *Node, v *resources.Snapshot) { + if n.isRef() { + return + } + if s := string(v.BundleID); c.match(s) { + ch := n.Child("bundle_id") + if r, ok := c.fn(&ch, s); ok { + v.BundleID = (r) + } + } + if !n.IsRef("acl") { + if len(v.ACL) > 0 { + cn := n.Child("acl") + for i := range v.ACL { + { + ch := cn.Elem(i) + stringsSnapshotACLEntry(c, &ch, &v.ACL[i]) + } + } + } + } + if !n.IsRef("can_manage") { + if len(v.CanManage) > 0 { + cn := n.Child("can_manage") + for i := range v.CanManage { + { + ch := cn.Elem(i) + stringsSnapshotManagePrincipal(c, &ch, &v.CanManage[i]) + } + } + } + } + if s := string(v.ZipPath); c.match(s) { + ch := n.Child("zip_path") + if r, ok := c.fn(&ch, s); ok { + v.ZipPath = (r) + } + } + if s := string(v.RemoteRoot); c.match(s) { + ch := n.Child("remote_root") + if r, ok := c.fn(&ch, s); ok { + v.RemoteRoot = (r) + } + } +} + +func stringsVariableTargetVariable(c *stringsWalk, n *Node, v *variable.TargetVariable) { + if n.isRef() { + return + } + if s := string(v.Type); c.match(s) { + ch := n.Child("type") + if r, ok := c.fn(&ch, s); ok { + v.Type = variable.VariableType(r) + } + } + if c.mode == EmptyFields && v.Default == "" { + ch := n.Child("default") + c.fn(&ch, "") + } + if v.Default != nil { + ch := n.Child("default") + c.any(&ch, &v.Default) + } + if s := string(v.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.Description = (r) + } + } + if c.mode == EmptyFields && v.Value == "" { + ch := n.Child("value") + c.fn(&ch, "") + } + if v.Value != nil { + ch := n.Child("value") + c.any(&ch, &v.Value) + } + if !n.IsRef("lookup") { + if x := v.Lookup; x != nil { + ch := n.Child("lookup") + stringsVariableLookup(c, &ch, x) + } + } +} + +func stringsIamComplexValue(c *stringsWalk, n *Node, v *iam.ComplexValue) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Display == "" && Forced(v.ForceSendFields, "Display") { + ch := n.Child("display") + c.fn(&ch, "") + } + if s := string(v.Display); c.match(s) { + ch := n.Child("display") + if r, ok := c.fn(&ch, s); ok { + v.Display = (r) + } + } + if c.mode == EmptyFields && v.Ref == "" && Forced(v.ForceSendFields, "Ref") { + ch := n.Child("$ref") + c.fn(&ch, "") + } + if s := string(v.Ref); c.match(s) { + ch := n.Child("$ref") + if r, ok := c.fn(&ch, s); ok { + v.Ref = (r) + } + } + if c.mode == EmptyFields && v.Type == "" && Forced(v.ForceSendFields, "Type") { + ch := n.Child("type") + c.fn(&ch, "") + } + if s := string(v.Type); c.match(s) { + ch := n.Child("type") + if r, ok := c.fn(&ch, s); ok { + v.Type = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsIamName(c *stringsWalk, n *Node, v *iam.Name) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.FamilyName == "" && Forced(v.ForceSendFields, "FamilyName") { + ch := n.Child("familyName") + c.fn(&ch, "") + } + if s := string(v.FamilyName); c.match(s) { + ch := n.Child("familyName") + if r, ok := c.fn(&ch, s); ok { + v.FamilyName = (r) + } + } + if c.mode == EmptyFields && v.GivenName == "" && Forced(v.ForceSendFields, "GivenName") { + ch := n.Child("givenName") + c.fn(&ch, "") + } + if s := string(v.GivenName); c.match(s) { + ch := n.Child("givenName") + if r, ok := c.fn(&ch, s); ok { + v.GivenName = (r) + } + } +} + +func stringsResourcesJobPermission(c *stringsWalk, n *Node, v *resources.JobPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = jobs.JobPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsResourcesLifecycle(c *stringsWalk, n *Node, v *resources.Lifecycle) { + if n.isRef() { + return + } + if !n.IsRef("depends_on") { + if len(v.DependsOn) > 0 { + cn := n.Child("depends_on") + for i := range v.DependsOn { + { + ch := cn.Elem(i) + stringsResourcesDependsOn(c, &ch, &v.DependsOn[i]) + } + } + } + } +} + +func stringsJobsContinuous(c *stringsWalk, n *Node, v *jobs.Continuous) { + if n.isRef() { + return + } + if !n.IsRef("maintenance_window") { + if x := v.MaintenanceWindow; x != nil { + ch := n.Child("maintenance_window") + stringsJobsMaintenanceWindow(c, &ch, x) + } + } + if s := string(v.PauseStatus); c.match(s) { + ch := n.Child("pause_status") + if r, ok := c.fn(&ch, s); ok { + v.PauseStatus = jobs.PauseStatus(r) + } + } + if s := string(v.TaskRetryMode); c.match(s) { + ch := n.Child("task_retry_mode") + if r, ok := c.fn(&ch, s); ok { + v.TaskRetryMode = jobs.TaskRetryMode(r) + } + } +} + +func stringsJobsJobDeployment(c *stringsWalk, n *Node, v *jobs.JobDeployment) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DeploymentId == "" && Forced(v.ForceSendFields, "DeploymentId") { + ch := n.Child("deployment_id") + c.fn(&ch, "") + } + if s := string(v.DeploymentId); c.match(s) { + ch := n.Child("deployment_id") + if r, ok := c.fn(&ch, s); ok { + v.DeploymentId = (r) + } + } + if s := string(v.Kind); c.match(s) { + ch := n.Child("kind") + if r, ok := c.fn(&ch, s); ok { + v.Kind = jobs.JobDeploymentKind(r) + } + } + if c.mode == EmptyFields && v.MetadataFilePath == "" && Forced(v.ForceSendFields, "MetadataFilePath") { + ch := n.Child("metadata_file_path") + c.fn(&ch, "") + } + if s := string(v.MetadataFilePath); c.match(s) { + ch := n.Child("metadata_file_path") + if r, ok := c.fn(&ch, s); ok { + v.MetadataFilePath = (r) + } + } + if c.mode == EmptyFields && v.VersionId == "" && Forced(v.ForceSendFields, "VersionId") { + ch := n.Child("version_id") + c.fn(&ch, "") + } + if s := string(v.VersionId); c.match(s) { + ch := n.Child("version_id") + if r, ok := c.fn(&ch, s); ok { + v.VersionId = (r) + } + } +} + +func stringsJobsJobEmailNotifications(c *stringsWalk, n *Node, v *jobs.JobEmailNotifications) { + if n.isRef() { + return + } + if !n.IsRef("on_duration_warning_threshold_exceeded") { + if len(v.OnDurationWarningThresholdExceeded) > 0 { + cn := n.Child("on_duration_warning_threshold_exceeded") + for i := range v.OnDurationWarningThresholdExceeded { + if s := string(v.OnDurationWarningThresholdExceeded[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnDurationWarningThresholdExceeded[i] = (r) + } + } + } + } + } + if !n.IsRef("on_failure") { + if len(v.OnFailure) > 0 { + cn := n.Child("on_failure") + for i := range v.OnFailure { + if s := string(v.OnFailure[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnFailure[i] = (r) + } + } + } + } + } + if !n.IsRef("on_maintenance_complete") { + if len(v.OnMaintenanceComplete) > 0 { + cn := n.Child("on_maintenance_complete") + for i := range v.OnMaintenanceComplete { + if s := string(v.OnMaintenanceComplete[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnMaintenanceComplete[i] = (r) + } + } + } + } + } + if !n.IsRef("on_maintenance_start") { + if len(v.OnMaintenanceStart) > 0 { + cn := n.Child("on_maintenance_start") + for i := range v.OnMaintenanceStart { + if s := string(v.OnMaintenanceStart[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnMaintenanceStart[i] = (r) + } + } + } + } + } + if !n.IsRef("on_start") { + if len(v.OnStart) > 0 { + cn := n.Child("on_start") + for i := range v.OnStart { + if s := string(v.OnStart[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnStart[i] = (r) + } + } + } + } + } + if !n.IsRef("on_streaming_backlog_exceeded") { + if len(v.OnStreamingBacklogExceeded) > 0 { + cn := n.Child("on_streaming_backlog_exceeded") + for i := range v.OnStreamingBacklogExceeded { + if s := string(v.OnStreamingBacklogExceeded[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnStreamingBacklogExceeded[i] = (r) + } + } + } + } + } + if !n.IsRef("on_success") { + if len(v.OnSuccess) > 0 { + cn := n.Child("on_success") + for i := range v.OnSuccess { + if s := string(v.OnSuccess[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnSuccess[i] = (r) + } + } + } + } + } +} + +func stringsJobsJobEnvironmentVariables(c *stringsWalk, n *Node, v *jobs.JobEnvironmentVariables) { + if n.isRef() { + return + } + if s := string(v.EnvironmentVariablesKey); c.match(s) { + ch := n.Child("environment_variables_key") + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentVariablesKey = (r) + } + } + if !n.IsRef("spec") { + if x := v.Spec; x != nil { + ch := n.Child("spec") + stringsJobsJobEnvironmentVariablesSpec(c, &ch, x) + } + } +} + +func stringsJobsJobEnvironment(c *stringsWalk, n *Node, v *jobs.JobEnvironment) { + if n.isRef() { + return + } + if s := string(v.EnvironmentKey); c.match(s) { + ch := n.Child("environment_key") + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentKey = (r) + } + } + if !n.IsRef("spec") { + if x := v.Spec; x != nil { + ch := n.Child("spec") + stringsComputeEnvironment(c, &ch, x) + } + } +} + +func stringsJobsGitSource(c *stringsWalk, n *Node, v *jobs.GitSource) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.GitBranch == "" && Forced(v.ForceSendFields, "GitBranch") { + ch := n.Child("git_branch") + c.fn(&ch, "") + } + if s := string(v.GitBranch); c.match(s) { + ch := n.Child("git_branch") + if r, ok := c.fn(&ch, s); ok { + v.GitBranch = (r) + } + } + if c.mode == EmptyFields && v.GitCommit == "" && Forced(v.ForceSendFields, "GitCommit") { + ch := n.Child("git_commit") + c.fn(&ch, "") + } + if s := string(v.GitCommit); c.match(s) { + ch := n.Child("git_commit") + if r, ok := c.fn(&ch, s); ok { + v.GitCommit = (r) + } + } + if s := string(v.GitProvider); c.match(s) { + ch := n.Child("git_provider") + if r, ok := c.fn(&ch, s); ok { + v.GitProvider = jobs.GitProvider(r) + } + } + if !n.IsRef("git_snapshot") { + if x := v.GitSnapshot; x != nil { + ch := n.Child("git_snapshot") + stringsJobsGitSnapshot(c, &ch, x) + } + } + if c.mode == EmptyFields && v.GitTag == "" && Forced(v.ForceSendFields, "GitTag") { + ch := n.Child("git_tag") + c.fn(&ch, "") + } + if s := string(v.GitTag); c.match(s) { + ch := n.Child("git_tag") + if r, ok := c.fn(&ch, s); ok { + v.GitTag = (r) + } + } + if s := string(v.GitUrl); c.match(s) { + ch := n.Child("git_url") + if r, ok := c.fn(&ch, s); ok { + v.GitUrl = (r) + } + } + if !n.IsRef("job_source") { + if x := v.JobSource; x != nil { + ch := n.Child("job_source") + stringsJobsJobSource(c, &ch, x) + } + } + if !n.IsRef("sparse_checkout") { + if x := v.SparseCheckout; x != nil { + ch := n.Child("sparse_checkout") + stringsJobsSparseCheckout(c, &ch, x) + } + } +} + +func stringsJobsJobsHealthRules(c *stringsWalk, n *Node, v *jobs.JobsHealthRules) { + if n.isRef() { + return + } + if !n.IsRef("rules") { + if len(v.Rules) > 0 { + cn := n.Child("rules") + for i := range v.Rules { + { + ch := cn.Elem(i) + stringsJobsJobsHealthRule(c, &ch, &v.Rules[i]) + } + } + } + } +} + +func stringsJobsJobCluster(c *stringsWalk, n *Node, v *jobs.JobCluster) { + if n.isRef() { + return + } + if s := string(v.JobClusterKey); c.match(s) { + ch := n.Child("job_cluster_key") + if r, ok := c.fn(&ch, s); ok { + v.JobClusterKey = (r) + } + } + if !n.IsRef("new_cluster") { + if x := v.NewCluster; x != nil { + ch := n.Child("new_cluster") + stringsComputeClusterSpec(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ServerlessComputeId == "" && Forced(v.ForceSendFields, "ServerlessComputeId") { + ch := n.Child("serverless_compute_id") + c.fn(&ch, "") + } + if s := string(v.ServerlessComputeId); c.match(s) { + ch := n.Child("serverless_compute_id") + if r, ok := c.fn(&ch, s); ok { + v.ServerlessComputeId = (r) + } + } +} + +func stringsJobsJobParameterDefinition(c *stringsWalk, n *Node, v *jobs.JobParameterDefinition) { + if n.isRef() { + return + } + if s := string(v.Default); c.match(s) { + ch := n.Child("default") + if r, ok := c.fn(&ch, s); ok { + v.Default = (r) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsJobsCronSchedule(c *stringsWalk, n *Node, v *jobs.CronSchedule) { + if n.isRef() { + return + } + if s := string(v.PauseStatus); c.match(s) { + ch := n.Child("pause_status") + if r, ok := c.fn(&ch, s); ok { + v.PauseStatus = jobs.PauseStatus(r) + } + } + if s := string(v.QuartzCronExpression); c.match(s) { + ch := n.Child("quartz_cron_expression") + if r, ok := c.fn(&ch, s); ok { + v.QuartzCronExpression = (r) + } + } + if !n.IsRef("sql_condition") { + if x := v.SqlCondition; x != nil { + ch := n.Child("sql_condition") + stringsJobsSqlConditionConfiguration(c, &ch, x) + } + } + if s := string(v.TimezoneId); c.match(s) { + ch := n.Child("timezone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimezoneId = (r) + } + } +} + +func stringsJobsTask(c *stringsWalk, n *Node, v *jobs.Task) { + if n.isRef() { + return + } + if !n.IsRef("ai_runtime_task") { + if x := v.AiRuntimeTask; x != nil { + ch := n.Child("ai_runtime_task") + stringsJobsAiRuntimeTask(c, &ch, x) + } + } + if !n.IsRef("alert_task") { + if x := v.AlertTask; x != nil { + ch := n.Child("alert_task") + stringsJobsAlertTask(c, &ch, x) + } + } + if !n.IsRef("clean_rooms_notebook_task") { + if x := v.CleanRoomsNotebookTask; x != nil { + ch := n.Child("clean_rooms_notebook_task") + stringsJobsCleanRoomsNotebookTask(c, &ch, x) + } + } + if !n.IsRef("compute") { + if x := v.Compute; x != nil { + ch := n.Child("compute") + stringsJobsCompute(c, &ch, x) + } + } + if !n.IsRef("condition_task") { + if x := v.ConditionTask; x != nil { + ch := n.Child("condition_task") + stringsJobsConditionTask(c, &ch, x) + } + } + if !n.IsRef("dashboard_task") { + if x := v.DashboardTask; x != nil { + ch := n.Child("dashboard_task") + stringsJobsDashboardTask(c, &ch, x) + } + } + if !n.IsRef("dbt_cloud_task") { + if x := v.DbtCloudTask; x != nil { + ch := n.Child("dbt_cloud_task") + stringsJobsDbtCloudTask(c, &ch, x) + } + } + if !n.IsRef("dbt_platform_task") { + if x := v.DbtPlatformTask; x != nil { + ch := n.Child("dbt_platform_task") + stringsJobsDbtPlatformTask(c, &ch, x) + } + } + if !n.IsRef("dbt_task") { + if x := v.DbtTask; x != nil { + ch := n.Child("dbt_task") + stringsJobsDbtTask(c, &ch, x) + } + } + if !n.IsRef("depends_on") { + if len(v.DependsOn) > 0 { + cn := n.Child("depends_on") + for i := range v.DependsOn { + { + ch := cn.Elem(i) + stringsJobsTaskDependency(c, &ch, &v.DependsOn[i]) + } + } + } + } + if c.mode == EmptyFields && v.Description == "" && Forced(v.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.Description = (r) + } + } + if !n.IsRef("email_notifications") { + if x := v.EmailNotifications; x != nil { + ch := n.Child("email_notifications") + stringsJobsTaskEmailNotifications(c, &ch, x) + } + } + if c.mode == EmptyFields && v.EnvironmentKey == "" && Forced(v.ForceSendFields, "EnvironmentKey") { + ch := n.Child("environment_key") + c.fn(&ch, "") + } + if s := string(v.EnvironmentKey); c.match(s) { + ch := n.Child("environment_key") + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentKey = (r) + } + } + if c.mode == EmptyFields && v.EnvironmentVariablesKey == "" && Forced(v.ForceSendFields, "EnvironmentVariablesKey") { + ch := n.Child("environment_variables_key") + c.fn(&ch, "") + } + if s := string(v.EnvironmentVariablesKey); c.match(s) { + ch := n.Child("environment_variables_key") + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentVariablesKey = (r) + } + } + if c.mode == EmptyFields && v.ExistingClusterId == "" && Forced(v.ForceSendFields, "ExistingClusterId") { + ch := n.Child("existing_cluster_id") + c.fn(&ch, "") + } + if s := string(v.ExistingClusterId); c.match(s) { + ch := n.Child("existing_cluster_id") + if r, ok := c.fn(&ch, s); ok { + v.ExistingClusterId = (r) + } + } + if !n.IsRef("for_each_task") { + if x := v.ForEachTask; x != nil { + ch := n.Child("for_each_task") + stringsJobsForEachTask(c, &ch, x) + } + } + if !n.IsRef("gen_ai_compute_task") { + if x := v.GenAiComputeTask; x != nil { + ch := n.Child("gen_ai_compute_task") + stringsJobsGenAiComputeTask(c, &ch, x) + } + } + if !n.IsRef("health") { + if x := v.Health; x != nil { + ch := n.Child("health") + stringsJobsJobsHealthRules(c, &ch, x) + } + } + if c.mode == EmptyFields && v.JobClusterKey == "" && Forced(v.ForceSendFields, "JobClusterKey") { + ch := n.Child("job_cluster_key") + c.fn(&ch, "") + } + if s := string(v.JobClusterKey); c.match(s) { + ch := n.Child("job_cluster_key") + if r, ok := c.fn(&ch, s); ok { + v.JobClusterKey = (r) + } + } + if !n.IsRef("libraries") { + if len(v.Libraries) > 0 { + cn := n.Child("libraries") + for i := range v.Libraries { + { + ch := cn.Elem(i) + stringsComputeLibrary(c, &ch, &v.Libraries[i]) + } + } + } + } + if !n.IsRef("new_cluster") { + if x := v.NewCluster; x != nil { + ch := n.Child("new_cluster") + stringsComputeClusterSpec(c, &ch, x) + } + } + if !n.IsRef("notebook_task") { + if x := v.NotebookTask; x != nil { + ch := n.Child("notebook_task") + stringsJobsNotebookTask(c, &ch, x) + } + } + if !n.IsRef("pipeline_task") { + if x := v.PipelineTask; x != nil { + ch := n.Child("pipeline_task") + stringsJobsPipelineTask(c, &ch, x) + } + } + if !n.IsRef("power_bi_task") { + if x := v.PowerBiTask; x != nil { + ch := n.Child("power_bi_task") + stringsJobsPowerBiTask(c, &ch, x) + } + } + if !n.IsRef("python_operator_task") { + if x := v.PythonOperatorTask; x != nil { + ch := n.Child("python_operator_task") + stringsJobsPythonOperatorTask(c, &ch, x) + } + } + if !n.IsRef("python_wheel_task") { + if x := v.PythonWheelTask; x != nil { + ch := n.Child("python_wheel_task") + stringsJobsPythonWheelTask(c, &ch, x) + } + } + if c.mode == EmptyFields && v.RunIf == "" && Forced(v.ForceSendFields, "RunIf") { + ch := n.Child("run_if") + c.fn(&ch, "") + } + if s := string(v.RunIf); c.match(s) { + ch := n.Child("run_if") + if r, ok := c.fn(&ch, s); ok { + v.RunIf = jobs.RunIf(r) + } + } + if !n.IsRef("run_job_task") { + if x := v.RunJobTask; x != nil { + ch := n.Child("run_job_task") + stringsJobsRunJobTask(c, &ch, x) + } + } + if !n.IsRef("spark_jar_task") { + if x := v.SparkJarTask; x != nil { + ch := n.Child("spark_jar_task") + stringsJobsSparkJarTask(c, &ch, x) + } + } + if !n.IsRef("spark_python_task") { + if x := v.SparkPythonTask; x != nil { + ch := n.Child("spark_python_task") + stringsJobsSparkPythonTask(c, &ch, x) + } + } + if !n.IsRef("spark_submit_task") { + if x := v.SparkSubmitTask; x != nil { + ch := n.Child("spark_submit_task") + stringsJobsSparkSubmitTask(c, &ch, x) + } + } + if !n.IsRef("sql_task") { + if x := v.SqlTask; x != nil { + ch := n.Child("sql_task") + stringsJobsSqlTask(c, &ch, x) + } + } + if s := string(v.TaskKey); c.match(s) { + ch := n.Child("task_key") + if r, ok := c.fn(&ch, s); ok { + v.TaskKey = (r) + } + } + if !n.IsRef("webhook_notifications") { + if x := v.WebhookNotifications; x != nil { + ch := n.Child("webhook_notifications") + stringsJobsWebhookNotifications(c, &ch, x) + } + } +} + +func stringsJobsTriggerSettings(c *stringsWalk, n *Node, v *jobs.TriggerSettings) { + if n.isRef() { + return + } + if !n.IsRef("file_arrival") { + if x := v.FileArrival; x != nil { + ch := n.Child("file_arrival") + stringsJobsFileArrivalTriggerConfiguration(c, &ch, x) + } + } + if !n.IsRef("model") { + if x := v.Model; x != nil { + ch := n.Child("model") + stringsJobsModelTriggerConfiguration(c, &ch, x) + } + } + if s := string(v.PauseStatus); c.match(s) { + ch := n.Child("pause_status") + if r, ok := c.fn(&ch, s); ok { + v.PauseStatus = jobs.PauseStatus(r) + } + } + if !n.IsRef("periodic") { + if x := v.Periodic; x != nil { + ch := n.Child("periodic") + stringsJobsPeriodicTriggerConfiguration(c, &ch, x) + } + } + if !n.IsRef("sql_condition") { + if x := v.SqlCondition; x != nil { + ch := n.Child("sql_condition") + stringsJobsSqlConditionConfiguration(c, &ch, x) + } + } + if !n.IsRef("table_update") { + if x := v.TableUpdate; x != nil { + ch := n.Child("table_update") + stringsJobsTableUpdateTriggerConfiguration(c, &ch, x) + } + } +} + +func stringsJobsTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.TriggerConfiguration) { + if n.isRef() { + return + } + if !n.IsRef("continuous") { + if x := v.Continuous; x != nil { + ch := n.Child("continuous") + stringsJobsContinuousTriggerConfiguration(c, &ch, x) + } + } + if !n.IsRef("file_arrival") { + if x := v.FileArrival; x != nil { + ch := n.Child("file_arrival") + stringsJobsFileArrivalTriggerConfiguration(c, &ch, x) + } + } + if !n.IsRef("model") { + if x := v.Model; x != nil { + ch := n.Child("model") + stringsJobsModelTriggerConfiguration(c, &ch, x) + } + } + if s := string(v.PauseStatus); c.match(s) { + ch := n.Child("pause_status") + if r, ok := c.fn(&ch, s); ok { + v.PauseStatus = jobs.PauseStatus(r) + } + } + if !n.IsRef("periodic") { + if x := v.Periodic; x != nil { + ch := n.Child("periodic") + stringsJobsPeriodicTriggerConfiguration(c, &ch, x) + } + } + if !n.IsRef("schedule") { + if x := v.Schedule; x != nil { + ch := n.Child("schedule") + stringsJobsCronTriggerConfiguration(c, &ch, x) + } + } + if !n.IsRef("sql_condition") { + if x := v.SqlCondition; x != nil { + ch := n.Child("sql_condition") + stringsJobsSqlConditionConfiguration(c, &ch, x) + } + } + if !n.IsRef("table_update") { + if x := v.TableUpdate; x != nil { + ch := n.Child("table_update") + stringsJobsTableUpdateTriggerConfiguration(c, &ch, x) + } + } +} + +func stringsJobsWebhookNotifications(c *stringsWalk, n *Node, v *jobs.WebhookNotifications) { + if n.isRef() { + return + } + if !n.IsRef("on_duration_warning_threshold_exceeded") { + if len(v.OnDurationWarningThresholdExceeded) > 0 { + cn := n.Child("on_duration_warning_threshold_exceeded") + for i := range v.OnDurationWarningThresholdExceeded { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnDurationWarningThresholdExceeded[i]) + } + } + } + } + if !n.IsRef("on_failure") { + if len(v.OnFailure) > 0 { + cn := n.Child("on_failure") + for i := range v.OnFailure { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnFailure[i]) + } + } + } + } + if !n.IsRef("on_maintenance_complete") { + if len(v.OnMaintenanceComplete) > 0 { + cn := n.Child("on_maintenance_complete") + for i := range v.OnMaintenanceComplete { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnMaintenanceComplete[i]) + } + } + } + } + if !n.IsRef("on_maintenance_start") { + if len(v.OnMaintenanceStart) > 0 { + cn := n.Child("on_maintenance_start") + for i := range v.OnMaintenanceStart { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnMaintenanceStart[i]) + } + } + } + } + if !n.IsRef("on_start") { + if len(v.OnStart) > 0 { + cn := n.Child("on_start") + for i := range v.OnStart { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnStart[i]) + } + } + } + } + if !n.IsRef("on_streaming_backlog_exceeded") { + if len(v.OnStreamingBacklogExceeded) > 0 { + cn := n.Child("on_streaming_backlog_exceeded") + for i := range v.OnStreamingBacklogExceeded { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnStreamingBacklogExceeded[i]) + } + } + } + } + if !n.IsRef("on_success") { + if len(v.OnSuccess) > 0 { + cn := n.Child("on_success") + for i := range v.OnSuccess { + { + ch := cn.Elem(i) + stringsJobsWebhook(c, &ch, &v.OnSuccess[i]) + } + } + } + } +} + +func stringsResourcesJobRunLifecycle(c *stringsWalk, n *Node, v *resources.JobRunLifecycle) { + if n.isRef() { + return + } + if !n.IsRef("triggers") { + if len(v.Triggers) > 0 { + cn := n.Child("triggers") + for i := range v.Triggers { + { + ch := cn.Elem(i) + stringsResourcesJobRunTrigger(c, &ch, &v.Triggers[i]) + } + } + } + } + if !n.IsRef("triggers_state") { + if x := v.TriggersState; x != nil { + ch := n.Child("triggers_state") + stringsResourcesJobRunTriggersState(c, &ch, x) + } + } + if !n.IsRef("depends_on") { + if len(v.Lifecycle.DependsOn) > 0 { + cn := n.Child("depends_on") + for i := range v.Lifecycle.DependsOn { + { + ch := cn.Elem(i) + stringsResourcesDependsOn(c, &ch, &v.Lifecycle.DependsOn[i]) + } + } + } + } +} + +func stringsJobsPipelineParams(c *stringsWalk, n *Node, v *jobs.PipelineParams) { + if n.isRef() { + return + } + if !n.IsRef("full_refresh_selection") { + if len(v.FullRefreshSelection) > 0 { + cn := n.Child("full_refresh_selection") + for i := range v.FullRefreshSelection { + if s := string(v.FullRefreshSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.FullRefreshSelection[i] = (r) + } + } + } + } + } + if !n.IsRef("refresh_flow_selection") { + if len(v.RefreshFlowSelection) > 0 { + cn := n.Child("refresh_flow_selection") + for i := range v.RefreshFlowSelection { + if s := string(v.RefreshFlowSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RefreshFlowSelection[i] = (r) + } + } + } + } + } + if !n.IsRef("refresh_selection") { + if len(v.RefreshSelection) > 0 { + cn := n.Child("refresh_selection") + for i := range v.RefreshSelection { + if s := string(v.RefreshSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RefreshSelection[i] = (r) + } + } + } + } + } + if !n.IsRef("reset_checkpoint_selection") { + if len(v.ResetCheckpointSelection) > 0 { + cn := n.Child("reset_checkpoint_selection") + for i := range v.ResetCheckpointSelection { + if s := string(v.ResetCheckpointSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ResetCheckpointSelection[i] = (r) + } + } + } + } + } +} + +func stringsResourcesPipelinePermission(c *stringsWalk, n *Node, v *resources.PipelinePermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = pipelines.PipelinePermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsPipelinesPipelineCluster(c *stringsWalk, n *Node, v *pipelines.PipelineCluster) { + if n.isRef() { + return + } + if !n.IsRef("autoscale") { + if x := v.Autoscale; x != nil { + ch := n.Child("autoscale") + stringsPipelinesPipelineClusterAutoscale(c, &ch, x) + } + } + if !n.IsRef("aws_attributes") { + if x := v.AwsAttributes; x != nil { + ch := n.Child("aws_attributes") + stringsComputeAwsAttributes(c, &ch, x) + } + } + if !n.IsRef("azure_attributes") { + if x := v.AzureAttributes; x != nil { + ch := n.Child("azure_attributes") + stringsComputeAzureAttributes(c, &ch, x) + } + } + if !n.IsRef("cluster_log_conf") { + if x := v.ClusterLogConf; x != nil { + ch := n.Child("cluster_log_conf") + stringsComputeClusterLogConf(c, &ch, x) + } + } + if !n.IsRef("custom_tags") { + if len(v.CustomTags) > 0 { + cn := n.Child("custom_tags") + for _, k := range sortedKeys(v.CustomTags) { + if s := string(v.CustomTags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CustomTags[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.DriverInstancePoolId == "" && Forced(v.ForceSendFields, "DriverInstancePoolId") { + ch := n.Child("driver_instance_pool_id") + c.fn(&ch, "") + } + if s := string(v.DriverInstancePoolId); c.match(s) { + ch := n.Child("driver_instance_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.DriverInstancePoolId = (r) + } + } + if c.mode == EmptyFields && v.DriverNodeTypeId == "" && Forced(v.ForceSendFields, "DriverNodeTypeId") { + ch := n.Child("driver_node_type_id") + c.fn(&ch, "") + } + if s := string(v.DriverNodeTypeId); c.match(s) { + ch := n.Child("driver_node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.DriverNodeTypeId = (r) + } + } + if !n.IsRef("gcp_attributes") { + if x := v.GcpAttributes; x != nil { + ch := n.Child("gcp_attributes") + stringsComputeGcpAttributes(c, &ch, x) + } + } + if !n.IsRef("init_scripts") { + if len(v.InitScripts) > 0 { + cn := n.Child("init_scripts") + for i := range v.InitScripts { + { + ch := cn.Elem(i) + stringsComputeInitScriptInfo(c, &ch, &v.InitScripts[i]) + } + } + } + } + if c.mode == EmptyFields && v.InstancePoolId == "" && Forced(v.ForceSendFields, "InstancePoolId") { + ch := n.Child("instance_pool_id") + c.fn(&ch, "") + } + if s := string(v.InstancePoolId); c.match(s) { + ch := n.Child("instance_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.InstancePoolId = (r) + } + } + if c.mode == EmptyFields && v.Label == "" && Forced(v.ForceSendFields, "Label") { + ch := n.Child("label") + c.fn(&ch, "") + } + if s := string(v.Label); c.match(s) { + ch := n.Child("label") + if r, ok := c.fn(&ch, s); ok { + v.Label = (r) + } + } + if c.mode == EmptyFields && v.NodeTypeId == "" && Forced(v.ForceSendFields, "NodeTypeId") { + ch := n.Child("node_type_id") + c.fn(&ch, "") + } + if s := string(v.NodeTypeId); c.match(s) { + ch := n.Child("node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.NodeTypeId = (r) + } + } + if c.mode == EmptyFields && v.PolicyId == "" && Forced(v.ForceSendFields, "PolicyId") { + ch := n.Child("policy_id") + c.fn(&ch, "") + } + if s := string(v.PolicyId); c.match(s) { + ch := n.Child("policy_id") + if r, ok := c.fn(&ch, s); ok { + v.PolicyId = (r) + } + } + if !n.IsRef("spark_conf") { + if len(v.SparkConf) > 0 { + cn := n.Child("spark_conf") + for _, k := range sortedKeys(v.SparkConf) { + if s := string(v.SparkConf[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.SparkConf[k] = (r) + } + } + } + } + } + if !n.IsRef("spark_env_vars") { + if len(v.SparkEnvVars) > 0 { + cn := n.Child("spark_env_vars") + for _, k := range sortedKeys(v.SparkEnvVars) { + if s := string(v.SparkEnvVars[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.SparkEnvVars[k] = (r) + } + } + } + } + } + if !n.IsRef("ssh_public_keys") { + if len(v.SshPublicKeys) > 0 { + cn := n.Child("ssh_public_keys") + for i := range v.SshPublicKeys { + if s := string(v.SshPublicKeys[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.SshPublicKeys[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesPipelineDeployment(c *stringsWalk, n *Node, v *pipelines.PipelineDeployment) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DeploymentId == "" && Forced(v.ForceSendFields, "DeploymentId") { + ch := n.Child("deployment_id") + c.fn(&ch, "") + } + if s := string(v.DeploymentId); c.match(s) { + ch := n.Child("deployment_id") + if r, ok := c.fn(&ch, s); ok { + v.DeploymentId = (r) + } + } + if s := string(v.Kind); c.match(s) { + ch := n.Child("kind") + if r, ok := c.fn(&ch, s); ok { + v.Kind = pipelines.DeploymentKind(r) + } + } + if c.mode == EmptyFields && v.MetadataFilePath == "" && Forced(v.ForceSendFields, "MetadataFilePath") { + ch := n.Child("metadata_file_path") + c.fn(&ch, "") + } + if s := string(v.MetadataFilePath); c.match(s) { + ch := n.Child("metadata_file_path") + if r, ok := c.fn(&ch, s); ok { + v.MetadataFilePath = (r) + } + } + if c.mode == EmptyFields && v.VersionId == "" && Forced(v.ForceSendFields, "VersionId") { + ch := n.Child("version_id") + c.fn(&ch, "") + } + if s := string(v.VersionId); c.match(s) { + ch := n.Child("version_id") + if r, ok := c.fn(&ch, s); ok { + v.VersionId = (r) + } + } +} + +func stringsPipelinesPipelinesEnvironment(c *stringsWalk, n *Node, v *pipelines.PipelinesEnvironment) { + if n.isRef() { + return + } + if !n.IsRef("dependencies") { + if len(v.Dependencies) > 0 { + cn := n.Child("dependencies") + for i := range v.Dependencies { + if s := string(v.Dependencies[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Dependencies[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.EnvironmentVersion == "" && Forced(v.ForceSendFields, "EnvironmentVersion") { + ch := n.Child("environment_version") + c.fn(&ch, "") + } + if s := string(v.EnvironmentVersion); c.match(s) { + ch := n.Child("environment_version") + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentVersion = (r) + } + } +} + +func stringsPipelinesEventLogSpec(c *stringsWalk, n *Node, v *pipelines.EventLogSpec) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Catalog == "" && Forced(v.ForceSendFields, "Catalog") { + ch := n.Child("catalog") + c.fn(&ch, "") + } + if s := string(v.Catalog); c.match(s) { + ch := n.Child("catalog") + if r, ok := c.fn(&ch, s); ok { + v.Catalog = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Schema == "" && Forced(v.ForceSendFields, "Schema") { + ch := n.Child("schema") + c.fn(&ch, "") + } + if s := string(v.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.Schema = (r) + } + } +} + +func stringsPipelinesFilters(c *stringsWalk, n *Node, v *pipelines.Filters) { + if n.isRef() { + return + } + if !n.IsRef("exclude") { + if len(v.Exclude) > 0 { + cn := n.Child("exclude") + for i := range v.Exclude { + if s := string(v.Exclude[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Exclude[i] = (r) + } + } + } + } + } + if !n.IsRef("include") { + if len(v.Include) > 0 { + cn := n.Child("include") + for i := range v.Include { + if s := string(v.Include[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Include[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesIngestionGatewayPipelineDefinition(c *stringsWalk, n *Node, v *pipelines.IngestionGatewayPipelineDefinition) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ConnectionId == "" && Forced(v.ForceSendFields, "ConnectionId") { + ch := n.Child("connection_id") + c.fn(&ch, "") + } + if s := string(v.ConnectionId); c.match(s) { + ch := n.Child("connection_id") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionId = (r) + } + } + if s := string(v.ConnectionName); c.match(s) { + ch := n.Child("connection_name") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionName = (r) + } + } + if !n.IsRef("connection_parameters") { + if x := v.ConnectionParameters; x != nil { + ch := n.Child("connection_parameters") + stringsPipelinesConnectionParameters(c, &ch, x) + } + } + if s := string(v.GatewayStorageCatalog); c.match(s) { + ch := n.Child("gateway_storage_catalog") + if r, ok := c.fn(&ch, s); ok { + v.GatewayStorageCatalog = (r) + } + } + if c.mode == EmptyFields && v.GatewayStorageName == "" && Forced(v.ForceSendFields, "GatewayStorageName") { + ch := n.Child("gateway_storage_name") + c.fn(&ch, "") + } + if s := string(v.GatewayStorageName); c.match(s) { + ch := n.Child("gateway_storage_name") + if r, ok := c.fn(&ch, s); ok { + v.GatewayStorageName = (r) + } + } + if s := string(v.GatewayStorageSchema); c.match(s) { + ch := n.Child("gateway_storage_schema") + if r, ok := c.fn(&ch, s); ok { + v.GatewayStorageSchema = (r) + } + } +} + +func stringsPipelinesIngestionPipelineDefinition(c *stringsWalk, n *Node, v *pipelines.IngestionPipelineDefinition) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ConnectionName == "" && Forced(v.ForceSendFields, "ConnectionName") { + ch := n.Child("connection_name") + c.fn(&ch, "") + } + if s := string(v.ConnectionName); c.match(s) { + ch := n.Child("connection_name") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionName = (r) + } + } + if c.mode == EmptyFields && v.ConnectorType == "" && Forced(v.ForceSendFields, "ConnectorType") { + ch := n.Child("connector_type") + c.fn(&ch, "") + } + if s := string(v.ConnectorType); c.match(s) { + ch := n.Child("connector_type") + if r, ok := c.fn(&ch, s); ok { + v.ConnectorType = pipelines.ConnectorType(r) + } + } + if !n.IsRef("data_staging_options") { + if x := v.DataStagingOptions; x != nil { + ch := n.Child("data_staging_options") + stringsPipelinesDataStagingOptions(c, &ch, x) + } + } + if !n.IsRef("full_refresh_window") { + if x := v.FullRefreshWindow; x != nil { + ch := n.Child("full_refresh_window") + stringsPipelinesOperationTimeWindow(c, &ch, x) + } + } + if c.mode == EmptyFields && v.IngestionGatewayId == "" && Forced(v.ForceSendFields, "IngestionGatewayId") { + ch := n.Child("ingestion_gateway_id") + c.fn(&ch, "") + } + if s := string(v.IngestionGatewayId); c.match(s) { + ch := n.Child("ingestion_gateway_id") + if r, ok := c.fn(&ch, s); ok { + v.IngestionGatewayId = (r) + } + } + if c.mode == EmptyFields && v.NetsuiteJarPath == "" && Forced(v.ForceSendFields, "NetsuiteJarPath") { + ch := n.Child("netsuite_jar_path") + c.fn(&ch, "") + } + if s := string(v.NetsuiteJarPath); c.match(s) { + ch := n.Child("netsuite_jar_path") + if r, ok := c.fn(&ch, s); ok { + v.NetsuiteJarPath = (r) + } + } + if !n.IsRef("objects") { + if len(v.Objects) > 0 { + cn := n.Child("objects") + for i := range v.Objects { + { + ch := cn.Elem(i) + stringsPipelinesIngestionConfig(c, &ch, &v.Objects[i]) + } + } + } + } + if !n.IsRef("source_configurations") { + if len(v.SourceConfigurations) > 0 { + cn := n.Child("source_configurations") + for i := range v.SourceConfigurations { + { + ch := cn.Elem(i) + stringsPipelinesSourceConfig(c, &ch, &v.SourceConfigurations[i]) + } + } + } + } + if c.mode == EmptyFields && v.SourceType == "" && Forced(v.ForceSendFields, "SourceType") { + ch := n.Child("source_type") + c.fn(&ch, "") + } + if s := string(v.SourceType); c.match(s) { + ch := n.Child("source_type") + if r, ok := c.fn(&ch, s); ok { + v.SourceType = pipelines.IngestionSourceType(r) + } + } + if !n.IsRef("table_configuration") { + if x := v.TableConfiguration; x != nil { + ch := n.Child("table_configuration") + stringsPipelinesTableSpecificConfig(c, &ch, x) + } + } +} + +func stringsPipelinesPipelineLibrary(c *stringsWalk, n *Node, v *pipelines.PipelineLibrary) { + if n.isRef() { + return + } + if !n.IsRef("file") { + if x := v.File; x != nil { + ch := n.Child("file") + stringsPipelinesFileLibrary(c, &ch, x) + } + } + if !n.IsRef("glob") { + if x := v.Glob; x != nil { + ch := n.Child("glob") + stringsPipelinesPathPattern(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Jar == "" && Forced(v.ForceSendFields, "Jar") { + ch := n.Child("jar") + c.fn(&ch, "") + } + if s := string(v.Jar); c.match(s) { + ch := n.Child("jar") + if r, ok := c.fn(&ch, s); ok { + v.Jar = (r) + } + } + if !n.IsRef("maven") { + if x := v.Maven; x != nil { + ch := n.Child("maven") + stringsComputeMavenLibrary(c, &ch, x) + } + } + if !n.IsRef("notebook") { + if x := v.Notebook; x != nil { + ch := n.Child("notebook") + stringsPipelinesNotebookLibrary(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Whl == "" && Forced(v.ForceSendFields, "Whl") { + ch := n.Child("whl") + c.fn(&ch, "") + } + if s := string(v.Whl); c.match(s) { + ch := n.Child("whl") + if r, ok := c.fn(&ch, s); ok { + v.Whl = (r) + } + } +} + +func stringsPipelinesNotifications(c *stringsWalk, n *Node, v *pipelines.Notifications) { + if n.isRef() { + return + } + if !n.IsRef("alerts") { + if len(v.Alerts) > 0 { + cn := n.Child("alerts") + for i := range v.Alerts { + if s := string(v.Alerts[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Alerts[i] = (r) + } + } + } + } + } + if !n.IsRef("email_recipients") { + if len(v.EmailRecipients) > 0 { + cn := n.Child("email_recipients") + for i := range v.EmailRecipients { + if s := string(v.EmailRecipients[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.EmailRecipients[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesRestartWindow(c *stringsWalk, n *Node, v *pipelines.RestartWindow) { + if n.isRef() { + return + } + if !n.IsRef("days_of_week") { + if len(v.DaysOfWeek) > 0 { + cn := n.Child("days_of_week") + for i := range v.DaysOfWeek { + if s := string(v.DaysOfWeek[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.DaysOfWeek[i] = pipelines.DayOfWeek(r) + } + } + } + } + } + if c.mode == EmptyFields && v.TimeZoneId == "" && Forced(v.ForceSendFields, "TimeZoneId") { + ch := n.Child("time_zone_id") + c.fn(&ch, "") + } + if s := string(v.TimeZoneId); c.match(s) { + ch := n.Child("time_zone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimeZoneId = (r) + } + } +} + +func stringsPipelinesRunAs(c *stringsWalk, n *Node, v *pipelines.RunAs) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.GroupName == "" && Forced(v.ForceSendFields, "GroupName") { + ch := n.Child("group_name") + c.fn(&ch, "") + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } + if c.mode == EmptyFields && v.ServicePrincipalName == "" && Forced(v.ForceSendFields, "ServicePrincipalName") { + ch := n.Child("service_principal_name") + c.fn(&ch, "") + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if c.mode == EmptyFields && v.UserName == "" && Forced(v.ForceSendFields, "UserName") { + ch := n.Child("user_name") + c.fn(&ch, "") + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } +} + +func stringsPipelinesPipelineTrigger(c *stringsWalk, n *Node, v *pipelines.PipelineTrigger) { + if n.isRef() { + return + } + if !n.IsRef("cron") { + if x := v.Cron; x != nil { + ch := n.Child("cron") + stringsPipelinesCronTrigger(c, &ch, x) + } + } +} + +func stringsMlEntityColumn(c *stringsWalk, n *Node, v *ml.EntityColumn) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsMlFunction(c *stringsWalk, n *Node, v *ml.Function) { + if n.isRef() { + return + } + if !n.IsRef("aggregation_function") { + if x := v.AggregationFunction; x != nil { + ch := n.Child("aggregation_function") + stringsMlAggregationFunction(c, &ch, x) + } + } + if !n.IsRef("column_selection") { + if x := v.ColumnSelection; x != nil { + ch := n.Child("column_selection") + stringsMlColumnSelection(c, &ch, x) + } + } + if !n.IsRef("custom_udf") { + if x := v.CustomUdf; x != nil { + ch := n.Child("custom_udf") + stringsMlCustomUdf(c, &ch, x) + } + } + if !n.IsRef("extra_parameters") { + if len(v.ExtraParameters) > 0 { + cn := n.Child("extra_parameters") + for i := range v.ExtraParameters { + { + ch := cn.Elem(i) + stringsMlFunctionExtraParameter(c, &ch, &v.ExtraParameters[i]) + } + } + } + } + if s := string(v.FunctionType); c.match(s) { + ch := n.Child("function_type") + if r, ok := c.fn(&ch, s); ok { + v.FunctionType = ml.FunctionFunctionType(r) + } + } +} + +func stringsMlDataSource(c *stringsWalk, n *Node, v *ml.DataSource) { + if n.isRef() { + return + } + if !n.IsRef("delta_table_source") { + if x := v.DeltaTableSource; x != nil { + ch := n.Child("delta_table_source") + stringsMlDeltaTableSource(c, &ch, x) + } + } + if !n.IsRef("feature_view_source") { + if x := v.FeatureViewSource; x != nil { + ch := n.Child("feature_view_source") + stringsMlFeatureViewSource(c, &ch, x) + } + } + if !n.IsRef("kafka_source") { + if x := v.KafkaSource; x != nil { + ch := n.Child("kafka_source") + stringsMlKafkaSource(c, &ch, x) + } + } + if !n.IsRef("request_source") { + if x := v.RequestSource; x != nil { + ch := n.Child("request_source") + stringsMlRequestSource(c, &ch, x) + } + } + if !n.IsRef("sdp_source") { + if x := v.SdpSource; x != nil { + ch := n.Child("sdp_source") + stringsMlSdpSource(c, &ch, x) + } + } + if !n.IsRef("stream_source") { + if x := v.StreamSource; x != nil { + ch := n.Child("stream_source") + stringsMlStreamSource(c, &ch, x) + } + } +} + +func stringsMlTimeWindow(c *stringsWalk, n *Node, v *ml.TimeWindow) { + if n.isRef() { + return + } + if !n.IsRef("continuous") { + if x := v.Continuous; x != nil { + ch := n.Child("continuous") + stringsMlContinuousWindow(c, &ch, x) + } + } + if !n.IsRef("sliding") { + if x := v.Sliding; x != nil { + ch := n.Child("sliding") + stringsMlSlidingWindow(c, &ch, x) + } + } + if !n.IsRef("tumbling") { + if x := v.Tumbling; x != nil { + ch := n.Child("tumbling") + stringsMlTumblingWindow(c, &ch, x) + } + } +} + +func stringsMlTimeseriesColumn(c *stringsWalk, n *Node, v *ml.TimeseriesColumn) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsResourcesMlflowModelPermission(c *stringsWalk, n *Node, v *resources.MlflowModelPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = ml.RegisteredModelPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsMlModelTag(c *stringsWalk, n *Node, v *ml.ModelTag) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsResourcesMlflowExperimentPermission(c *stringsWalk, n *Node, v *resources.MlflowExperimentPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = ml.ExperimentPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsMlExperimentTag(c *stringsWalk, n *Node, v *ml.ExperimentTag) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsMlExperimentTraceLocation(c *stringsWalk, n *Node, v *ml.ExperimentTraceLocation) { + if n.isRef() { + return + } + if !n.IsRef("uc_trace_location") { + if x := v.UcTraceLocation; x != nil { + ch := n.Child("uc_trace_location") + stringsMlUcTraceLocation(c, &ch, x) + } + } +} + +func stringsResourcesModelServingEndpointPermission(c *stringsWalk, n *Node, v *resources.ModelServingEndpointPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = serving.ServingEndpointPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsServingAiGatewayConfig(c *stringsWalk, n *Node, v *serving.AiGatewayConfig) { + if n.isRef() { + return + } + if !n.IsRef("guardrails") { + if x := v.Guardrails; x != nil { + ch := n.Child("guardrails") + stringsServingAiGatewayGuardrails(c, &ch, x) + } + } + if !n.IsRef("inference_table_config") { + if x := v.InferenceTableConfig; x != nil { + ch := n.Child("inference_table_config") + stringsServingAiGatewayInferenceTableConfig(c, &ch, x) + } + } + if !n.IsRef("rate_limits") { + if len(v.RateLimits) > 0 { + cn := n.Child("rate_limits") + for i := range v.RateLimits { + { + ch := cn.Elem(i) + stringsServingAiGatewayRateLimit(c, &ch, &v.RateLimits[i]) + } + } + } + } +} + +func stringsServingEndpointCoreConfigInput(c *stringsWalk, n *Node, v *serving.EndpointCoreConfigInput) { + if n.isRef() { + return + } + if !n.IsRef("auto_capture_config") { + if x := v.AutoCaptureConfig; x != nil { + ch := n.Child("auto_capture_config") + stringsServingAutoCaptureConfigInput(c, &ch, x) + } + } + if !n.IsRef("served_entities") { + if len(v.ServedEntities) > 0 { + cn := n.Child("served_entities") + for i := range v.ServedEntities { + { + ch := cn.Elem(i) + stringsServingServedEntityInput(c, &ch, &v.ServedEntities[i]) + } + } + } + } + if !n.IsRef("served_models") { + if len(v.ServedModels) > 0 { + cn := n.Child("served_models") + for i := range v.ServedModels { + { + ch := cn.Elem(i) + stringsServingServedModelInput(c, &ch, &v.ServedModels[i]) + } + } + } + } + if !n.IsRef("traffic_config") { + if x := v.TrafficConfig; x != nil { + ch := n.Child("traffic_config") + stringsServingTrafficConfig(c, &ch, x) + } + } +} + +func stringsServingEmailNotifications(c *stringsWalk, n *Node, v *serving.EmailNotifications) { + if n.isRef() { + return + } + if !n.IsRef("on_update_failure") { + if len(v.OnUpdateFailure) > 0 { + cn := n.Child("on_update_failure") + for i := range v.OnUpdateFailure { + if s := string(v.OnUpdateFailure[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnUpdateFailure[i] = (r) + } + } + } + } + } + if !n.IsRef("on_update_success") { + if len(v.OnUpdateSuccess) > 0 { + cn := n.Child("on_update_success") + for i := range v.OnUpdateSuccess { + if s := string(v.OnUpdateSuccess[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnUpdateSuccess[i] = (r) + } + } + } + } + } +} + +func stringsServingRateLimit(c *stringsWalk, n *Node, v *serving.RateLimit) { + if n.isRef() { + return + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = serving.RateLimitKey(r) + } + } + if s := string(v.RenewalPeriod); c.match(s) { + ch := n.Child("renewal_period") + if r, ok := c.fn(&ch, s); ok { + v.RenewalPeriod = serving.RateLimitRenewalPeriod(r) + } + } +} + +func stringsServingEndpointTag(c *stringsWalk, n *Node, v *serving.EndpointTag) { + if n.isRef() { + return + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsServingTelemetryConfig(c *stringsWalk, n *Node, v *serving.TelemetryConfig) { + if n.isRef() { + return + } + if !n.IsRef("enabled_telemetry_features") { + if len(v.EnabledTelemetryFeatures) > 0 { + cn := n.Child("enabled_telemetry_features") + for i := range v.EnabledTelemetryFeatures { + if s := string(v.EnabledTelemetryFeatures[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.EnabledTelemetryFeatures[i] = serving.TelemetryFeature(r) + } + } + } + } + } + if !n.IsRef("inference_table_config") { + if x := v.InferenceTableConfig; x != nil { + ch := n.Child("inference_table_config") + stringsServingTelemetryInferenceTableConfig(c, &ch, x) + } + } + if !n.IsRef("table_names") { + if x := v.TableNames; x != nil { + ch := n.Child("table_names") + stringsServingUnityCatalogTableNames(c, &ch, x) + } + } + if c.mode == EmptyFields && v.TelemetryProfileId == "" && Forced(v.ForceSendFields, "TelemetryProfileId") { + ch := n.Child("telemetry_profile_id") + c.fn(&ch, "") + } + if s := string(v.TelemetryProfileId); c.match(s) { + ch := n.Child("telemetry_profile_id") + if r, ok := c.fn(&ch, s); ok { + v.TelemetryProfileId = (r) + } + } +} + +func stringsCatalogPrivilegeAssignment(c *stringsWalk, n *Node, v *catalog.PrivilegeAssignment) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Principal == "" && Forced(v.ForceSendFields, "Principal") { + ch := n.Child("principal") + c.fn(&ch, "") + } + if s := string(v.Principal); c.match(s) { + ch := n.Child("principal") + if r, ok := c.fn(&ch, s); ok { + v.Principal = (r) + } + } + if !n.IsRef("privileges") { + if len(v.Privileges) > 0 { + cn := n.Child("privileges") + for i := range v.Privileges { + if s := string(v.Privileges[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Privileges[i] = catalog.Privilege(r) + } + } + } + } + } +} + +func stringsCatalogModelServiceConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfig) { + if n.isRef() { + return + } + if !n.IsRef("inference_table") { + if x := v.InferenceTable; x != nil { + ch := n.Child("inference_table") + stringsCatalogInferenceTableConfig(c, &ch, x) + } + } + if !n.IsRef("rate_limits") { + if len(v.RateLimits) > 0 { + cn := n.Child("rate_limits") + for i := range v.RateLimits { + { + ch := cn.Elem(i) + stringsCatalogRateLimit(c, &ch, &v.RateLimits[i]) + } + } + } + } + if !n.IsRef("routing") { + if x := v.Routing; x != nil { + ch := n.Child("routing") + stringsCatalogModelServiceConfigRoutingConfig(c, &ch, x) + } + } +} + +func stringsCatalogMcpServiceConfig(c *stringsWalk, n *Node, v *catalog.McpServiceConfig) { + if n.isRef() { + return + } + if !n.IsRef("include_tool_selectors") { + if len(v.IncludeToolSelectors) > 0 { + cn := n.Child("include_tool_selectors") + for i := range v.IncludeToolSelectors { + if s := string(v.IncludeToolSelectors[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeToolSelectors[i] = (r) + } + } + } + } + } + if !n.IsRef("rate_limits") { + if len(v.RateLimits) > 0 { + cn := n.Child("rate_limits") + for i := range v.RateLimits { + { + ch := cn.Elem(i) + stringsCatalogRateLimit(c, &ch, &v.RateLimits[i]) + } + } + } + } + if !n.IsRef("source_connection") { + if x := v.SourceConnection; x != nil { + ch := n.Child("source_connection") + stringsCatalogMcpServiceConfigSourceConnection(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfig) { + if n.isRef() { + return + } + if !n.IsRef("amazon_bedrock") { + if x := v.AmazonBedrock; x != nil { + ch := n.Child("amazon_bedrock") + stringsCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(c, &ch, x) + } + } + if !n.IsRef("anthropic") { + if x := v.Anthropic; x != nil { + ch := n.Child("anthropic") + stringsCatalogModelProviderServiceConfigAnthropicProviderConfig(c, &ch, x) + } + } + if !n.IsRef("azure_openai") { + if x := v.AzureOpenai; x != nil { + ch := n.Child("azure_openai") + stringsCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(c, &ch, x) + } + } + if !n.IsRef("bedrock_mantle") { + if x := v.BedrockMantle; x != nil { + ch := n.Child("bedrock_mantle") + stringsCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(c, &ch, x) + } + } + if !n.IsRef("custom") { + if x := v.Custom; x != nil { + ch := n.Child("custom") + stringsCatalogModelProviderServiceConfigCustomProviderConfig(c, &ch, x) + } + } + if !n.IsRef("gemini_enterprise") { + if x := v.GeminiEnterprise; x != nil { + ch := n.Child("gemini_enterprise") + stringsCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(c, &ch, x) + } + } + if !n.IsRef("inference_table") { + if x := v.InferenceTable; x != nil { + ch := n.Child("inference_table") + stringsCatalogInferenceTableConfig(c, &ch, x) + } + } + if !n.IsRef("microsoft_foundry") { + if x := v.MicrosoftFoundry; x != nil { + ch := n.Child("microsoft_foundry") + stringsCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(c, &ch, x) + } + } + if !n.IsRef("openai") { + if x := v.Openai; x != nil { + ch := n.Child("openai") + stringsCatalogModelProviderServiceConfigOpenAiProviderConfig(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ProviderType == "" && Forced(v.ForceSendFields, "ProviderType") { + ch := n.Child("provider_type") + c.fn(&ch, "") + } + if s := string(v.ProviderType); c.match(s) { + ch := n.Child("provider_type") + if r, ok := c.fn(&ch, s); ok { + v.ProviderType = catalog.ModelProviderServiceConfigExternalModelProviderType(r) + } + } + if !n.IsRef("rate_limits") { + if len(v.RateLimits) > 0 { + cn := n.Child("rate_limits") + for i := range v.RateLimits { + { + ch := cn.Elem(i) + stringsCatalogRateLimit(c, &ch, &v.RateLimits[i]) + } + } + } + } + if !n.IsRef("targets") { + if len(v.Targets) > 0 { + cn := n.Child("targets") + for i := range v.Targets { + { + ch := cn.Elem(i) + stringsCatalogModelProviderServiceConfigModelTargetConfig(c, &ch, &v.Targets[i]) + } + } + } + } +} + +func stringsCatalogRegisteredModelAlias(c *stringsWalk, n *Node, v *catalog.RegisteredModelAlias) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AliasName == "" && Forced(v.ForceSendFields, "AliasName") { + ch := n.Child("alias_name") + c.fn(&ch, "") + } + if s := string(v.AliasName); c.match(s) { + ch := n.Child("alias_name") + if r, ok := c.fn(&ch, s); ok { + v.AliasName = (r) + } + } + if c.mode == EmptyFields && v.CatalogName == "" && Forced(v.ForceSendFields, "CatalogName") { + ch := n.Child("catalog_name") + c.fn(&ch, "") + } + if s := string(v.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.Id == "" && Forced(v.ForceSendFields, "Id") { + ch := n.Child("id") + c.fn(&ch, "") + } + if s := string(v.Id); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.Id = (r) + } + } + if c.mode == EmptyFields && v.ModelName == "" && Forced(v.ForceSendFields, "ModelName") { + ch := n.Child("model_name") + c.fn(&ch, "") + } + if s := string(v.ModelName); c.match(s) { + ch := n.Child("model_name") + if r, ok := c.fn(&ch, s); ok { + v.ModelName = (r) + } + } + if c.mode == EmptyFields && v.SchemaName == "" && Forced(v.ForceSendFields, "SchemaName") { + ch := n.Child("schema_name") + c.fn(&ch, "") + } + if s := string(v.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.SchemaName = (r) + } + } +} + +func stringsCatalogMonitorMetric(c *stringsWalk, n *Node, v *catalog.MonitorMetric) { + if n.isRef() { + return + } + if s := string(v.Definition); c.match(s) { + ch := n.Child("definition") + if r, ok := c.fn(&ch, s); ok { + v.Definition = (r) + } + } + if !n.IsRef("input_columns") { + if len(v.InputColumns) > 0 { + cn := n.Child("input_columns") + for i := range v.InputColumns { + if s := string(v.InputColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.InputColumns[i] = (r) + } + } + } + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if s := string(v.OutputDataType); c.match(s) { + ch := n.Child("output_data_type") + if r, ok := c.fn(&ch, s); ok { + v.OutputDataType = (r) + } + } + if s := string(v.Type); c.match(s) { + ch := n.Child("type") + if r, ok := c.fn(&ch, s); ok { + v.Type = catalog.MonitorMetricType(r) + } + } +} + +func stringsCatalogMonitorInferenceLog(c *stringsWalk, n *Node, v *catalog.MonitorInferenceLog) { + if n.isRef() { + return + } + if !n.IsRef("granularities") { + if len(v.Granularities) > 0 { + cn := n.Child("granularities") + for i := range v.Granularities { + if s := string(v.Granularities[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Granularities[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.LabelCol == "" && Forced(v.ForceSendFields, "LabelCol") { + ch := n.Child("label_col") + c.fn(&ch, "") + } + if s := string(v.LabelCol); c.match(s) { + ch := n.Child("label_col") + if r, ok := c.fn(&ch, s); ok { + v.LabelCol = (r) + } + } + if s := string(v.ModelIdCol); c.match(s) { + ch := n.Child("model_id_col") + if r, ok := c.fn(&ch, s); ok { + v.ModelIdCol = (r) + } + } + if s := string(v.PredictionCol); c.match(s) { + ch := n.Child("prediction_col") + if r, ok := c.fn(&ch, s); ok { + v.PredictionCol = (r) + } + } + if c.mode == EmptyFields && v.PredictionProbaCol == "" && Forced(v.ForceSendFields, "PredictionProbaCol") { + ch := n.Child("prediction_proba_col") + c.fn(&ch, "") + } + if s := string(v.PredictionProbaCol); c.match(s) { + ch := n.Child("prediction_proba_col") + if r, ok := c.fn(&ch, s); ok { + v.PredictionProbaCol = (r) + } + } + if s := string(v.ProblemType); c.match(s) { + ch := n.Child("problem_type") + if r, ok := c.fn(&ch, s); ok { + v.ProblemType = catalog.MonitorInferenceLogProblemType(r) + } + } + if s := string(v.TimestampCol); c.match(s) { + ch := n.Child("timestamp_col") + if r, ok := c.fn(&ch, s); ok { + v.TimestampCol = (r) + } + } +} + +func stringsCatalogMonitorNotifications(c *stringsWalk, n *Node, v *catalog.MonitorNotifications) { + if n.isRef() { + return + } + if !n.IsRef("on_failure") { + if x := v.OnFailure; x != nil { + ch := n.Child("on_failure") + stringsCatalogMonitorDestination(c, &ch, x) + } + } + if !n.IsRef("on_new_classification_tag_detected") { + if x := v.OnNewClassificationTagDetected; x != nil { + ch := n.Child("on_new_classification_tag_detected") + stringsCatalogMonitorDestination(c, &ch, x) + } + } +} + +func stringsCatalogMonitorCronSchedule(c *stringsWalk, n *Node, v *catalog.MonitorCronSchedule) { + if n.isRef() { + return + } + if s := string(v.PauseStatus); c.match(s) { + ch := n.Child("pause_status") + if r, ok := c.fn(&ch, s); ok { + v.PauseStatus = catalog.MonitorCronSchedulePauseStatus(r) + } + } + if s := string(v.QuartzCronExpression); c.match(s) { + ch := n.Child("quartz_cron_expression") + if r, ok := c.fn(&ch, s); ok { + v.QuartzCronExpression = (r) + } + } + if s := string(v.TimezoneId); c.match(s) { + ch := n.Child("timezone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimezoneId = (r) + } + } +} + +func stringsCatalogMonitorTimeSeries(c *stringsWalk, n *Node, v *catalog.MonitorTimeSeries) { + if n.isRef() { + return + } + if !n.IsRef("granularities") { + if len(v.Granularities) > 0 { + cn := n.Child("granularities") + for i := range v.Granularities { + if s := string(v.Granularities[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Granularities[i] = (r) + } + } + } + } + } + if s := string(v.TimestampCol); c.match(s) { + ch := n.Child("timestamp_col") + if r, ok := c.fn(&ch, s); ok { + v.TimestampCol = (r) + } + } +} + +func stringsCatalogEncryptionSettings(c *stringsWalk, n *Node, v *catalog.EncryptionSettings) { + if n.isRef() { + return + } + if !n.IsRef("azure_encryption_settings") { + if x := v.AzureEncryptionSettings; x != nil { + ch := n.Child("azure_encryption_settings") + stringsCatalogAzureEncryptionSettings(c, &ch, x) + } + } + if c.mode == EmptyFields && v.AzureKeyVaultKeyId == "" && Forced(v.ForceSendFields, "AzureKeyVaultKeyId") { + ch := n.Child("azure_key_vault_key_id") + c.fn(&ch, "") + } + if s := string(v.AzureKeyVaultKeyId); c.match(s) { + ch := n.Child("azure_key_vault_key_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureKeyVaultKeyId = (r) + } + } + if c.mode == EmptyFields && v.CustomerManagedKeyId == "" && Forced(v.ForceSendFields, "CustomerManagedKeyId") { + ch := n.Child("customer_managed_key_id") + c.fn(&ch, "") + } + if s := string(v.CustomerManagedKeyId); c.match(s) { + ch := n.Child("customer_managed_key_id") + if r, ok := c.fn(&ch, s); ok { + v.CustomerManagedKeyId = (r) + } + } +} + +func stringsCatalogFileEventQueue(c *stringsWalk, n *Node, v *catalog.FileEventQueue) { + if n.isRef() { + return + } + if !n.IsRef("managed_aqs") { + if x := v.ManagedAqs; x != nil { + ch := n.Child("managed_aqs") + stringsCatalogAzureQueueStorage(c, &ch, x) + } + } + if !n.IsRef("managed_pubsub") { + if x := v.ManagedPubsub; x != nil { + ch := n.Child("managed_pubsub") + stringsCatalogGcpPubsub(c, &ch, x) + } + } + if !n.IsRef("managed_sqs") { + if x := v.ManagedSqs; x != nil { + ch := n.Child("managed_sqs") + stringsCatalogAwsSqsQueue(c, &ch, x) + } + } + if !n.IsRef("provided_aqs") { + if x := v.ProvidedAqs; x != nil { + ch := n.Child("provided_aqs") + stringsCatalogAzureQueueStorage(c, &ch, x) + } + } + if !n.IsRef("provided_pubsub") { + if x := v.ProvidedPubsub; x != nil { + ch := n.Child("provided_pubsub") + stringsCatalogGcpPubsub(c, &ch, x) + } + } + if !n.IsRef("provided_sqs") { + if x := v.ProvidedSqs; x != nil { + ch := n.Child("provided_sqs") + stringsCatalogAwsSqsQueue(c, &ch, x) + } + } +} + +func stringsCatalogEncryptionDetails(c *stringsWalk, n *Node, v *catalog.EncryptionDetails) { + if n.isRef() { + return + } + if !n.IsRef("sse_encryption_details") { + if x := v.SseEncryptionDetails; x != nil { + ch := n.Child("sse_encryption_details") + stringsCatalogSseEncryptionDetails(c, &ch, x) + } + } +} + +func stringsResourcesLifecycleWithStarted(c *stringsWalk, n *Node, v *resources.LifecycleWithStarted) { + if n.isRef() { + return + } + if !n.IsRef("depends_on") { + if len(v.Lifecycle.DependsOn) > 0 { + cn := n.Child("depends_on") + for i := range v.Lifecycle.DependsOn { + { + ch := cn.Elem(i) + stringsResourcesDependsOn(c, &ch, &v.Lifecycle.DependsOn[i]) + } + } + } + } +} + +func stringsComputeLibrary(c *stringsWalk, n *Node, v *compute.Library) { + if n.isRef() { + return + } + if !n.IsRef("cran") { + if x := v.Cran; x != nil { + ch := n.Child("cran") + stringsComputeRCranLibrary(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Egg == "" && Forced(v.ForceSendFields, "Egg") { + ch := n.Child("egg") + c.fn(&ch, "") + } + if s := string(v.Egg); c.match(s) { + ch := n.Child("egg") + if r, ok := c.fn(&ch, s); ok { + v.Egg = (r) + } + } + if c.mode == EmptyFields && v.Jar == "" && Forced(v.ForceSendFields, "Jar") { + ch := n.Child("jar") + c.fn(&ch, "") + } + if s := string(v.Jar); c.match(s) { + ch := n.Child("jar") + if r, ok := c.fn(&ch, s); ok { + v.Jar = (r) + } + } + if !n.IsRef("maven") { + if x := v.Maven; x != nil { + ch := n.Child("maven") + stringsComputeMavenLibrary(c, &ch, x) + } + } + if !n.IsRef("pypi") { + if x := v.Pypi; x != nil { + ch := n.Child("pypi") + stringsComputePythonPyPiLibrary(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Requirements == "" && Forced(v.ForceSendFields, "Requirements") { + ch := n.Child("requirements") + c.fn(&ch, "") + } + if s := string(v.Requirements); c.match(s) { + ch := n.Child("requirements") + if r, ok := c.fn(&ch, s); ok { + v.Requirements = (r) + } + } + if c.mode == EmptyFields && v.Whl == "" && Forced(v.ForceSendFields, "Whl") { + ch := n.Child("whl") + c.fn(&ch, "") + } + if s := string(v.Whl); c.match(s) { + ch := n.Child("whl") + if r, ok := c.fn(&ch, s); ok { + v.Whl = (r) + } + } +} + +func stringsResourcesClusterPermission(c *stringsWalk, n *Node, v *resources.ClusterPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = compute.ClusterPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsComputeAwsAttributes(c *stringsWalk, n *Node, v *compute.AwsAttributes) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Availability == "" && Forced(v.ForceSendFields, "Availability") { + ch := n.Child("availability") + c.fn(&ch, "") + } + if s := string(v.Availability); c.match(s) { + ch := n.Child("availability") + if r, ok := c.fn(&ch, s); ok { + v.Availability = compute.AwsAvailability(r) + } + } + if c.mode == EmptyFields && v.EbsVolumeType == "" && Forced(v.ForceSendFields, "EbsVolumeType") { + ch := n.Child("ebs_volume_type") + c.fn(&ch, "") + } + if s := string(v.EbsVolumeType); c.match(s) { + ch := n.Child("ebs_volume_type") + if r, ok := c.fn(&ch, s); ok { + v.EbsVolumeType = compute.EbsVolumeType(r) + } + } + if c.mode == EmptyFields && v.InstanceProfileArn == "" && Forced(v.ForceSendFields, "InstanceProfileArn") { + ch := n.Child("instance_profile_arn") + c.fn(&ch, "") + } + if s := string(v.InstanceProfileArn); c.match(s) { + ch := n.Child("instance_profile_arn") + if r, ok := c.fn(&ch, s); ok { + v.InstanceProfileArn = (r) + } + } + if c.mode == EmptyFields && v.ZoneId == "" && Forced(v.ForceSendFields, "ZoneId") { + ch := n.Child("zone_id") + c.fn(&ch, "") + } + if s := string(v.ZoneId); c.match(s) { + ch := n.Child("zone_id") + if r, ok := c.fn(&ch, s); ok { + v.ZoneId = (r) + } + } +} + +func stringsComputeAzureAttributes(c *stringsWalk, n *Node, v *compute.AzureAttributes) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Availability == "" && Forced(v.ForceSendFields, "Availability") { + ch := n.Child("availability") + c.fn(&ch, "") + } + if s := string(v.Availability); c.match(s) { + ch := n.Child("availability") + if r, ok := c.fn(&ch, s); ok { + v.Availability = compute.AzureAvailability(r) + } + } + if c.mode == EmptyFields && v.CapacityReservationGroup == "" && Forced(v.ForceSendFields, "CapacityReservationGroup") { + ch := n.Child("capacity_reservation_group") + c.fn(&ch, "") + } + if s := string(v.CapacityReservationGroup); c.match(s) { + ch := n.Child("capacity_reservation_group") + if r, ok := c.fn(&ch, s); ok { + v.CapacityReservationGroup = (r) + } + } + if !n.IsRef("log_analytics_info") { + if x := v.LogAnalyticsInfo; x != nil { + ch := n.Child("log_analytics_info") + stringsComputeLogAnalyticsInfo(c, &ch, x) + } + } +} + +func stringsComputeClusterLogConf(c *stringsWalk, n *Node, v *compute.ClusterLogConf) { + if n.isRef() { + return + } + if !n.IsRef("dbfs") { + if x := v.Dbfs; x != nil { + ch := n.Child("dbfs") + stringsComputeDbfsStorageInfo(c, &ch, x) + } + } + if !n.IsRef("s3") { + if x := v.S3; x != nil { + ch := n.Child("s3") + stringsComputeS3StorageInfo(c, &ch, x) + } + } + if !n.IsRef("volumes") { + if x := v.Volumes; x != nil { + ch := n.Child("volumes") + stringsComputeVolumesStorageInfo(c, &ch, x) + } + } +} + +func stringsComputeDockerImage(c *stringsWalk, n *Node, v *compute.DockerImage) { + if n.isRef() { + return + } + if !n.IsRef("basic_auth") { + if x := v.BasicAuth; x != nil { + ch := n.Child("basic_auth") + stringsComputeDockerBasicAuth(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Url == "" && Forced(v.ForceSendFields, "Url") { + ch := n.Child("url") + c.fn(&ch, "") + } + if s := string(v.Url); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.Url = (r) + } + } +} + +func stringsComputeNodeTypeFlexibility(c *stringsWalk, n *Node, v *compute.NodeTypeFlexibility) { + if n.isRef() { + return + } + if !n.IsRef("alternate_node_type_ids") { + if len(v.AlternateNodeTypeIds) > 0 { + cn := n.Child("alternate_node_type_ids") + for i := range v.AlternateNodeTypeIds { + if s := string(v.AlternateNodeTypeIds[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.AlternateNodeTypeIds[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.AwsContextId == "" && Forced(v.ForceSendFields, "AwsContextId") { + ch := n.Child("aws_context_id") + c.fn(&ch, "") + } + if s := string(v.AwsContextId); c.match(s) { + ch := n.Child("aws_context_id") + if r, ok := c.fn(&ch, s); ok { + v.AwsContextId = (r) + } + } +} + +func stringsComputeGcpAttributes(c *stringsWalk, n *Node, v *compute.GcpAttributes) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Availability == "" && Forced(v.ForceSendFields, "Availability") { + ch := n.Child("availability") + c.fn(&ch, "") + } + if s := string(v.Availability); c.match(s) { + ch := n.Child("availability") + if r, ok := c.fn(&ch, s); ok { + v.Availability = compute.GcpAvailability(r) + } + } + if c.mode == EmptyFields && v.ConfidentialComputeType == "" && Forced(v.ForceSendFields, "ConfidentialComputeType") { + ch := n.Child("confidential_compute_type") + c.fn(&ch, "") + } + if s := string(v.ConfidentialComputeType); c.match(s) { + ch := n.Child("confidential_compute_type") + if r, ok := c.fn(&ch, s); ok { + v.ConfidentialComputeType = compute.ConfidentialComputeType(r) + } + } + if c.mode == EmptyFields && v.GoogleServiceAccount == "" && Forced(v.ForceSendFields, "GoogleServiceAccount") { + ch := n.Child("google_service_account") + c.fn(&ch, "") + } + if s := string(v.GoogleServiceAccount); c.match(s) { + ch := n.Child("google_service_account") + if r, ok := c.fn(&ch, s); ok { + v.GoogleServiceAccount = (r) + } + } + if c.mode == EmptyFields && v.ZoneId == "" && Forced(v.ForceSendFields, "ZoneId") { + ch := n.Child("zone_id") + c.fn(&ch, "") + } + if s := string(v.ZoneId); c.match(s) { + ch := n.Child("zone_id") + if r, ok := c.fn(&ch, s); ok { + v.ZoneId = (r) + } + } +} + +func stringsComputeInitScriptInfo(c *stringsWalk, n *Node, v *compute.InitScriptInfo) { + if n.isRef() { + return + } + if !n.IsRef("abfss") { + if x := v.Abfss; x != nil { + ch := n.Child("abfss") + stringsComputeAdlsgen2Info(c, &ch, x) + } + } + if !n.IsRef("dbfs") { + if x := v.Dbfs; x != nil { + ch := n.Child("dbfs") + stringsComputeDbfsStorageInfo(c, &ch, x) + } + } + if !n.IsRef("file") { + if x := v.File; x != nil { + ch := n.Child("file") + stringsComputeLocalFileInfo(c, &ch, x) + } + } + if !n.IsRef("gcs") { + if x := v.Gcs; x != nil { + ch := n.Child("gcs") + stringsComputeGcsStorageInfo(c, &ch, x) + } + } + if !n.IsRef("s3") { + if x := v.S3; x != nil { + ch := n.Child("s3") + stringsComputeS3StorageInfo(c, &ch, x) + } + } + if !n.IsRef("volumes") { + if x := v.Volumes; x != nil { + ch := n.Child("volumes") + stringsComputeVolumesStorageInfo(c, &ch, x) + } + } + if !n.IsRef("workspace") { + if x := v.Workspace; x != nil { + ch := n.Child("workspace") + stringsComputeWorkspaceStorageInfo(c, &ch, x) + } + } +} + +func stringsResourcesAppConfig(c *stringsWalk, n *Node, v *resources.AppConfig) { + if n.isRef() { + return + } + if !n.IsRef("command") { + if len(v.Command) > 0 { + cn := n.Child("command") + for i := range v.Command { + if s := string(v.Command[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Command[i] = (r) + } + } + } + } + } + if !n.IsRef("env") { + if len(v.Env) > 0 { + cn := n.Child("env") + for i := range v.Env { + { + ch := cn.Elem(i) + stringsResourcesAppEnvVar(c, &ch, &v.Env[i]) + } + } + } + } +} + +func stringsResourcesAppPermission(c *stringsWalk, n *Node, v *resources.AppPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = apps.AppPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsAppsAppDeployment(c *stringsWalk, n *Node, v *apps.AppDeployment) { + if n.isRef() { + return + } + if !n.IsRef("command") { + if len(v.Command) > 0 { + cn := n.Child("command") + for i := range v.Command { + if s := string(v.Command[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Command[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.CreateTime == "" && Forced(v.ForceSendFields, "CreateTime") { + ch := n.Child("create_time") + c.fn(&ch, "") + } + if s := string(v.CreateTime); c.match(s) { + ch := n.Child("create_time") + if r, ok := c.fn(&ch, s); ok { + v.CreateTime = (r) + } + } + if c.mode == EmptyFields && v.Creator == "" && Forced(v.ForceSendFields, "Creator") { + ch := n.Child("creator") + c.fn(&ch, "") + } + if s := string(v.Creator); c.match(s) { + ch := n.Child("creator") + if r, ok := c.fn(&ch, s); ok { + v.Creator = (r) + } + } + if !n.IsRef("deployment_artifacts") { + if x := v.DeploymentArtifacts; x != nil { + ch := n.Child("deployment_artifacts") + stringsAppsAppDeploymentArtifacts(c, &ch, x) + } + } + if c.mode == EmptyFields && v.DeploymentId == "" && Forced(v.ForceSendFields, "DeploymentId") { + ch := n.Child("deployment_id") + c.fn(&ch, "") + } + if s := string(v.DeploymentId); c.match(s) { + ch := n.Child("deployment_id") + if r, ok := c.fn(&ch, s); ok { + v.DeploymentId = (r) + } + } + if !n.IsRef("env_vars") { + if len(v.EnvVars) > 0 { + cn := n.Child("env_vars") + for i := range v.EnvVars { + { + ch := cn.Elem(i) + stringsAppsEnvVar(c, &ch, &v.EnvVars[i]) + } + } + } + } + if !n.IsRef("git_source") { + if x := v.GitSource; x != nil { + ch := n.Child("git_source") + stringsAppsGitSource(c, &ch, x) + } + } + if !n.IsRef("health_check") { + if x := v.HealthCheck; x != nil { + ch := n.Child("health_check") + stringsAppsAppHealthCheck(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Mode == "" && Forced(v.ForceSendFields, "Mode") { + ch := n.Child("mode") + c.fn(&ch, "") + } + if s := string(v.Mode); c.match(s) { + ch := n.Child("mode") + if r, ok := c.fn(&ch, s); ok { + v.Mode = apps.AppDeploymentMode(r) + } + } + if c.mode == EmptyFields && v.SourceCodePath == "" && Forced(v.ForceSendFields, "SourceCodePath") { + ch := n.Child("source_code_path") + c.fn(&ch, "") + } + if s := string(v.SourceCodePath); c.match(s) { + ch := n.Child("source_code_path") + if r, ok := c.fn(&ch, s); ok { + v.SourceCodePath = (r) + } + } + if !n.IsRef("status") { + if x := v.Status; x != nil { + ch := n.Child("status") + stringsAppsAppDeploymentStatus(c, &ch, x) + } + } + if c.mode == EmptyFields && v.UpdateTime == "" && Forced(v.ForceSendFields, "UpdateTime") { + ch := n.Child("update_time") + c.fn(&ch, "") + } + if s := string(v.UpdateTime); c.match(s) { + ch := n.Child("update_time") + if r, ok := c.fn(&ch, s); ok { + v.UpdateTime = (r) + } + } +} + +func stringsAppsApplicationStatus(c *stringsWalk, n *Node, v *apps.ApplicationStatus) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Message == "" && Forced(v.ForceSendFields, "Message") { + ch := n.Child("message") + c.fn(&ch, "") + } + if s := string(v.Message); c.match(s) { + ch := n.Child("message") + if r, ok := c.fn(&ch, s); ok { + v.Message = (r) + } + } + if c.mode == EmptyFields && v.State == "" && Forced(v.ForceSendFields, "State") { + ch := n.Child("state") + c.fn(&ch, "") + } + if s := string(v.State); c.match(s) { + ch := n.Child("state") + if r, ok := c.fn(&ch, s); ok { + v.State = apps.ApplicationState(r) + } + } +} + +func stringsAppsComputeStatus(c *stringsWalk, n *Node, v *apps.ComputeStatus) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Message == "" && Forced(v.ForceSendFields, "Message") { + ch := n.Child("message") + c.fn(&ch, "") + } + if s := string(v.Message); c.match(s) { + ch := n.Child("message") + if r, ok := c.fn(&ch, s); ok { + v.Message = (r) + } + } + if c.mode == EmptyFields && v.State == "" && Forced(v.ForceSendFields, "State") { + ch := n.Child("state") + c.fn(&ch, "") + } + if s := string(v.State); c.match(s) { + ch := n.Child("state") + if r, ok := c.fn(&ch, s); ok { + v.State = apps.ComputeState(r) + } + } +} + +func stringsAppsGitSource(c *stringsWalk, n *Node, v *apps.GitSource) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Branch == "" && Forced(v.ForceSendFields, "Branch") { + ch := n.Child("branch") + c.fn(&ch, "") + } + if s := string(v.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.Branch = (r) + } + } + if c.mode == EmptyFields && v.Commit == "" && Forced(v.ForceSendFields, "Commit") { + ch := n.Child("commit") + c.fn(&ch, "") + } + if s := string(v.Commit); c.match(s) { + ch := n.Child("commit") + if r, ok := c.fn(&ch, s); ok { + v.Commit = (r) + } + } + if !n.IsRef("git_repository") { + if x := v.GitRepository; x != nil { + ch := n.Child("git_repository") + stringsAppsGitRepository(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ResolvedCommit == "" && Forced(v.ForceSendFields, "ResolvedCommit") { + ch := n.Child("resolved_commit") + c.fn(&ch, "") + } + if s := string(v.ResolvedCommit); c.match(s) { + ch := n.Child("resolved_commit") + if r, ok := c.fn(&ch, s); ok { + v.ResolvedCommit = (r) + } + } + if c.mode == EmptyFields && v.SourceCodePath == "" && Forced(v.ForceSendFields, "SourceCodePath") { + ch := n.Child("source_code_path") + c.fn(&ch, "") + } + if s := string(v.SourceCodePath); c.match(s) { + ch := n.Child("source_code_path") + if r, ok := c.fn(&ch, s); ok { + v.SourceCodePath = (r) + } + } + if c.mode == EmptyFields && v.Tag == "" && Forced(v.ForceSendFields, "Tag") { + ch := n.Child("tag") + c.fn(&ch, "") + } + if s := string(v.Tag); c.match(s) { + ch := n.Child("tag") + if r, ok := c.fn(&ch, s); ok { + v.Tag = (r) + } + } +} + +func stringsAppsGitRepository(c *stringsWalk, n *Node, v *apps.GitRepository) { + if n.isRef() { + return + } + if s := string(v.Provider); c.match(s) { + ch := n.Child("provider") + if r, ok := c.fn(&ch, s); ok { + v.Provider = (r) + } + } + if s := string(v.Url); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.Url = (r) + } + } +} + +func stringsAppsAppResource(c *stringsWalk, n *Node, v *apps.AppResource) { + if n.isRef() { + return + } + if !n.IsRef("app") { + if x := v.App; x != nil { + ch := n.Child("app") + stringsAppsAppResourceApp(c, &ch, x) + } + } + if !n.IsRef("database") { + if x := v.Database; x != nil { + ch := n.Child("database") + stringsAppsAppResourceDatabase(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Description == "" && Forced(v.ForceSendFields, "Description") { + ch := n.Child("description") + c.fn(&ch, "") + } + if s := string(v.Description); c.match(s) { + ch := n.Child("description") + if r, ok := c.fn(&ch, s); ok { + v.Description = (r) + } + } + if !n.IsRef("experiment") { + if x := v.Experiment; x != nil { + ch := n.Child("experiment") + stringsAppsAppResourceExperiment(c, &ch, x) + } + } + if !n.IsRef("genie_space") { + if x := v.GenieSpace; x != nil { + ch := n.Child("genie_space") + stringsAppsAppResourceGenieSpace(c, &ch, x) + } + } + if !n.IsRef("job") { + if x := v.Job; x != nil { + ch := n.Child("job") + stringsAppsAppResourceJob(c, &ch, x) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if !n.IsRef("postgres") { + if x := v.Postgres; x != nil { + ch := n.Child("postgres") + stringsAppsAppResourcePostgres(c, &ch, x) + } + } + if !n.IsRef("secret") { + if x := v.Secret; x != nil { + ch := n.Child("secret") + stringsAppsAppResourceSecret(c, &ch, x) + } + } + if !n.IsRef("serving_endpoint") { + if x := v.ServingEndpoint; x != nil { + ch := n.Child("serving_endpoint") + stringsAppsAppResourceServingEndpoint(c, &ch, x) + } + } + if !n.IsRef("sql_warehouse") { + if x := v.SqlWarehouse; x != nil { + ch := n.Child("sql_warehouse") + stringsAppsAppResourceSqlWarehouse(c, &ch, x) + } + } + if !n.IsRef("uc_securable") { + if x := v.UcSecurable; x != nil { + ch := n.Child("uc_securable") + stringsAppsAppResourceUcSecurable(c, &ch, x) + } + } +} + +func stringsAppsTelemetryExportDestination(c *stringsWalk, n *Node, v *apps.TelemetryExportDestination) { + if n.isRef() { + return + } + if !n.IsRef("unity_catalog") { + if x := v.UnityCatalog; x != nil { + ch := n.Child("unity_catalog") + stringsAppsUnityCatalog(c, &ch, x) + } + } +} + +func stringsResourcesSecretScopePermission(c *stringsWalk, n *Node, v *resources.SecretScopePermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = resources.SecretScopePermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsWorkspaceAzureKeyVaultSecretScopeMetadata(c *stringsWalk, n *Node, v *workspace.AzureKeyVaultSecretScopeMetadata) { + if n.isRef() { + return + } + if s := string(v.DnsName); c.match(s) { + ch := n.Child("dns_name") + if r, ok := c.fn(&ch, s); ok { + v.DnsName = (r) + } + } + if s := string(v.ResourceId); c.match(s) { + ch := n.Child("resource_id") + if r, ok := c.fn(&ch, s); ok { + v.ResourceId = (r) + } + } +} + +func stringsSqlAlertV2RunAs(c *stringsWalk, n *Node, v *sql.AlertV2RunAs) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ServicePrincipalName == "" && Forced(v.ForceSendFields, "ServicePrincipalName") { + ch := n.Child("service_principal_name") + c.fn(&ch, "") + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if c.mode == EmptyFields && v.UserName == "" && Forced(v.ForceSendFields, "UserName") { + ch := n.Child("user_name") + c.fn(&ch, "") + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } +} + +func stringsSqlAlertV2Evaluation(c *stringsWalk, n *Node, v *sql.AlertV2Evaluation) { + if n.isRef() { + return + } + if s := string(v.ComparisonOperator); c.match(s) { + ch := n.Child("comparison_operator") + if r, ok := c.fn(&ch, s); ok { + v.ComparisonOperator = sql.ComparisonOperator(r) + } + } + if c.mode == EmptyFields && v.EmptyResultState == "" && Forced(v.ForceSendFields, "EmptyResultState") { + ch := n.Child("empty_result_state") + c.fn(&ch, "") + } + if s := string(v.EmptyResultState); c.match(s) { + ch := n.Child("empty_result_state") + if r, ok := c.fn(&ch, s); ok { + v.EmptyResultState = sql.AlertEvaluationState(r) + } + } + if c.mode == EmptyFields && v.LastEvaluatedAt == "" && Forced(v.ForceSendFields, "LastEvaluatedAt") { + ch := n.Child("last_evaluated_at") + c.fn(&ch, "") + } + if s := string(v.LastEvaluatedAt); c.match(s) { + ch := n.Child("last_evaluated_at") + if r, ok := c.fn(&ch, s); ok { + v.LastEvaluatedAt = (r) + } + } + if !n.IsRef("notification") { + if x := v.Notification; x != nil { + ch := n.Child("notification") + stringsSqlAlertV2Notification(c, &ch, x) + } + } + if !n.IsRef("source") { + { + ch := n.Child("source") + stringsSqlAlertV2OperandColumn(c, &ch, &v.Source) + } + } + if c.mode == EmptyFields && v.State == "" && Forced(v.ForceSendFields, "State") { + ch := n.Child("state") + c.fn(&ch, "") + } + if s := string(v.State); c.match(s) { + ch := n.Child("state") + if r, ok := c.fn(&ch, s); ok { + v.State = sql.AlertEvaluationState(r) + } + } + if !n.IsRef("threshold") { + if x := v.Threshold; x != nil { + ch := n.Child("threshold") + stringsSqlAlertV2Operand(c, &ch, x) + } + } +} + +func stringsSqlAlertStatementParameter(c *stringsWalk, n *Node, v *sql.AlertStatementParameter) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Type == "" && Forced(v.ForceSendFields, "Type") { + ch := n.Child("type") + c.fn(&ch, "") + } + if s := string(v.Type); c.match(s) { + ch := n.Child("type") + if r, ok := c.fn(&ch, s); ok { + v.Type = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsSqlCronSchedule(c *stringsWalk, n *Node, v *sql.CronSchedule) { + if n.isRef() { + return + } + if s := string(v.PauseStatus); c.match(s) { + ch := n.Child("pause_status") + if r, ok := c.fn(&ch, s); ok { + v.PauseStatus = sql.SchedulePauseStatus(r) + } + } + if s := string(v.QuartzCronSchedule); c.match(s) { + ch := n.Child("quartz_cron_schedule") + if r, ok := c.fn(&ch, s); ok { + v.QuartzCronSchedule = (r) + } + } + if s := string(v.TimezoneId); c.match(s) { + ch := n.Child("timezone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimezoneId = (r) + } + } +} + +func stringsResourcesSqlWarehousePermission(c *stringsWalk, n *Node, v *resources.SqlWarehousePermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = sql.WarehousePermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsSqlChannel(c *stringsWalk, n *Node, v *sql.Channel) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DbsqlVersion == "" && Forced(v.ForceSendFields, "DbsqlVersion") { + ch := n.Child("dbsql_version") + c.fn(&ch, "") + } + if s := string(v.DbsqlVersion); c.match(s) { + ch := n.Child("dbsql_version") + if r, ok := c.fn(&ch, s); ok { + v.DbsqlVersion = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = sql.ChannelName(r) + } + } +} + +func stringsSqlEndpointTags(c *stringsWalk, n *Node, v *sql.EndpointTags) { + if n.isRef() { + return + } + if !n.IsRef("custom_tags") { + if len(v.CustomTags) > 0 { + cn := n.Child("custom_tags") + for i := range v.CustomTags { + { + ch := cn.Elem(i) + stringsSqlEndpointTagPair(c, &ch, &v.CustomTags[i]) + } + } + } + } +} + +func stringsDatabaseDatabaseInstanceRef(c *stringsWalk, n *Node, v *database.DatabaseInstanceRef) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.BranchTime == "" && Forced(v.ForceSendFields, "BranchTime") { + ch := n.Child("branch_time") + c.fn(&ch, "") + } + if s := string(v.BranchTime); c.match(s) { + ch := n.Child("branch_time") + if r, ok := c.fn(&ch, s); ok { + v.BranchTime = (r) + } + } + if c.mode == EmptyFields && v.EffectiveLsn == "" && Forced(v.ForceSendFields, "EffectiveLsn") { + ch := n.Child("effective_lsn") + c.fn(&ch, "") + } + if s := string(v.EffectiveLsn); c.match(s) { + ch := n.Child("effective_lsn") + if r, ok := c.fn(&ch, s); ok { + v.EffectiveLsn = (r) + } + } + if c.mode == EmptyFields && v.Lsn == "" && Forced(v.ForceSendFields, "Lsn") { + ch := n.Child("lsn") + c.fn(&ch, "") + } + if s := string(v.Lsn); c.match(s) { + ch := n.Child("lsn") + if r, ok := c.fn(&ch, s); ok { + v.Lsn = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Uid == "" && Forced(v.ForceSendFields, "Uid") { + ch := n.Child("uid") + c.fn(&ch, "") + } + if s := string(v.Uid); c.match(s) { + ch := n.Child("uid") + if r, ok := c.fn(&ch, s); ok { + v.Uid = (r) + } + } +} + +func stringsDatabaseCustomTag(c *stringsWalk, n *Node, v *database.CustomTag) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsDatabaseSyncedTableStatus(c *stringsWalk, n *Node, v *database.SyncedTableStatus) { + if n.isRef() { + return + } + if !n.IsRef("continuous_update_status") { + if x := v.ContinuousUpdateStatus; x != nil { + ch := n.Child("continuous_update_status") + stringsDatabaseSyncedTableContinuousUpdateStatus(c, &ch, x) + } + } + if c.mode == EmptyFields && v.DetailedState == "" && Forced(v.ForceSendFields, "DetailedState") { + ch := n.Child("detailed_state") + c.fn(&ch, "") + } + if s := string(v.DetailedState); c.match(s) { + ch := n.Child("detailed_state") + if r, ok := c.fn(&ch, s); ok { + v.DetailedState = database.SyncedTableState(r) + } + } + if !n.IsRef("failed_status") { + if x := v.FailedStatus; x != nil { + ch := n.Child("failed_status") + stringsDatabaseSyncedTableFailedStatus(c, &ch, x) + } + } + if !n.IsRef("last_sync") { + if x := v.LastSync; x != nil { + ch := n.Child("last_sync") + stringsDatabaseSyncedTablePosition(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Message == "" && Forced(v.ForceSendFields, "Message") { + ch := n.Child("message") + c.fn(&ch, "") + } + if s := string(v.Message); c.match(s) { + ch := n.Child("message") + if r, ok := c.fn(&ch, s); ok { + v.Message = (r) + } + } + if c.mode == EmptyFields && v.PipelineId == "" && Forced(v.ForceSendFields, "PipelineId") { + ch := n.Child("pipeline_id") + c.fn(&ch, "") + } + if s := string(v.PipelineId); c.match(s) { + ch := n.Child("pipeline_id") + if r, ok := c.fn(&ch, s); ok { + v.PipelineId = (r) + } + } + if !n.IsRef("provisioning_status") { + if x := v.ProvisioningStatus; x != nil { + ch := n.Child("provisioning_status") + stringsDatabaseSyncedTableProvisioningStatus(c, &ch, x) + } + } + if !n.IsRef("triggered_update_status") { + if x := v.TriggeredUpdateStatus; x != nil { + ch := n.Child("triggered_update_status") + stringsDatabaseSyncedTableTriggeredUpdateStatus(c, &ch, x) + } + } +} + +func stringsDatabaseSyncedTableSpec(c *stringsWalk, n *Node, v *database.SyncedTableSpec) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ExistingPipelineId == "" && Forced(v.ForceSendFields, "ExistingPipelineId") { + ch := n.Child("existing_pipeline_id") + c.fn(&ch, "") + } + if s := string(v.ExistingPipelineId); c.match(s) { + ch := n.Child("existing_pipeline_id") + if r, ok := c.fn(&ch, s); ok { + v.ExistingPipelineId = (r) + } + } + if !n.IsRef("new_pipeline_spec") { + if x := v.NewPipelineSpec; x != nil { + ch := n.Child("new_pipeline_spec") + stringsDatabaseNewPipelineSpec(c, &ch, x) + } + } + if !n.IsRef("primary_key_columns") { + if len(v.PrimaryKeyColumns) > 0 { + cn := n.Child("primary_key_columns") + for i := range v.PrimaryKeyColumns { + if s := string(v.PrimaryKeyColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.PrimaryKeyColumns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.SchedulingPolicy == "" && Forced(v.ForceSendFields, "SchedulingPolicy") { + ch := n.Child("scheduling_policy") + c.fn(&ch, "") + } + if s := string(v.SchedulingPolicy); c.match(s) { + ch := n.Child("scheduling_policy") + if r, ok := c.fn(&ch, s); ok { + v.SchedulingPolicy = database.SyncedTableSchedulingPolicy(r) + } + } + if c.mode == EmptyFields && v.SourceTableFullName == "" && Forced(v.ForceSendFields, "SourceTableFullName") { + ch := n.Child("source_table_full_name") + c.fn(&ch, "") + } + if s := string(v.SourceTableFullName); c.match(s) { + ch := n.Child("source_table_full_name") + if r, ok := c.fn(&ch, s); ok { + v.SourceTableFullName = (r) + } + } + if c.mode == EmptyFields && v.TimeseriesKey == "" && Forced(v.ForceSendFields, "TimeseriesKey") { + ch := n.Child("timeseries_key") + c.fn(&ch, "") + } + if s := string(v.TimeseriesKey); c.match(s) { + ch := n.Child("timeseries_key") + if r, ok := c.fn(&ch, s); ok { + v.TimeseriesKey = (r) + } + } + if !n.IsRef("type_overrides") { + if len(v.TypeOverrides) > 0 { + cn := n.Child("type_overrides") + for i := range v.TypeOverrides { + { + ch := cn.Elem(i) + stringsDatabaseSyncedTableSpecTypeOverride(c, &ch, &v.TypeOverrides[i]) + } + } + } + } +} + +func stringsPostgresProjectCustomTag(c *stringsWalk, n *Node, v *postgres.ProjectCustomTag) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsPostgresProjectDefaultEndpointSettings(c *stringsWalk, n *Node, v *postgres.ProjectDefaultEndpointSettings) { + if n.isRef() { + return + } + if !n.IsRef("pg_settings") { + if len(v.PgSettings) > 0 { + cn := n.Child("pg_settings") + for _, k := range sortedKeys(v.PgSettings) { + if s := string(v.PgSettings[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.PgSettings[k] = (r) + } + } + } + } + } +} + +func stringsPostgresEndpointSettings(c *stringsWalk, n *Node, v *postgres.EndpointSettings) { + if n.isRef() { + return + } + if !n.IsRef("pg_settings") { + if len(v.PgSettings) > 0 { + cn := n.Child("pg_settings") + for _, k := range sortedKeys(v.PgSettings) { + if s := string(v.PgSettings[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.PgSettings[k] = (r) + } + } + } + } + } +} + +func stringsPostgresSyncedTableSyncedTableSpecExtraColumn(c *stringsWalk, n *Node, v *postgres.SyncedTableSyncedTableSpecExtraColumn) { + if n.isRef() { + return + } + if s := string(v.ColumnName); c.match(s) { + ch := n.Child("column_name") + if r, ok := c.fn(&ch, s); ok { + v.ColumnName = (r) + } + } + if s := string(v.ColumnType); c.match(s) { + ch := n.Child("column_type") + if r, ok := c.fn(&ch, s); ok { + v.ColumnType = (r) + } + } + if c.mode == EmptyFields && v.Compute == "" && Forced(v.ForceSendFields, "Compute") { + ch := n.Child("compute") + c.fn(&ch, "") + } + if s := string(v.Compute); c.match(s) { + ch := n.Child("compute") + if r, ok := c.fn(&ch, s); ok { + v.Compute = (r) + } + } + if c.mode == EmptyFields && v.Maintenance == "" && Forced(v.ForceSendFields, "Maintenance") { + ch := n.Child("maintenance") + c.fn(&ch, "") + } + if s := string(v.Maintenance); c.match(s) { + ch := n.Child("maintenance") + if r, ok := c.fn(&ch, s); ok { + v.Maintenance = postgres.SyncedTableSyncedTableSpecExtraColumnMaintenance(r) + } + } +} + +func stringsPostgresNewPipelineSpec(c *stringsWalk, n *Node, v *postgres.NewPipelineSpec) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.BudgetPolicyId == "" && Forced(v.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.BudgetPolicyId = (r) + } + } + if c.mode == EmptyFields && v.PipelineChannel == "" && Forced(v.ForceSendFields, "PipelineChannel") { + ch := n.Child("pipeline_channel") + c.fn(&ch, "") + } + if s := string(v.PipelineChannel); c.match(s) { + ch := n.Child("pipeline_channel") + if r, ok := c.fn(&ch, s); ok { + v.PipelineChannel = postgres.NewPipelineSpecPipelineChannel(r) + } + } + if c.mode == EmptyFields && v.StorageCatalog == "" && Forced(v.ForceSendFields, "StorageCatalog") { + ch := n.Child("storage_catalog") + c.fn(&ch, "") + } + if s := string(v.StorageCatalog); c.match(s) { + ch := n.Child("storage_catalog") + if r, ok := c.fn(&ch, s); ok { + v.StorageCatalog = (r) + } + } + if c.mode == EmptyFields && v.StorageSchema == "" && Forced(v.ForceSendFields, "StorageSchema") { + ch := n.Child("storage_schema") + c.fn(&ch, "") + } + if s := string(v.StorageSchema); c.match(s) { + ch := n.Child("storage_schema") + if r, ok := c.fn(&ch, s); ok { + v.StorageSchema = (r) + } + } +} + +func stringsPostgresSyncedTableSyncedTableSpecTypeOverride(c *stringsWalk, n *Node, v *postgres.SyncedTableSyncedTableSpecTypeOverride) { + if n.isRef() { + return + } + if s := string(v.ColumnName); c.match(s) { + ch := n.Child("column_name") + if r, ok := c.fn(&ch, s); ok { + v.ColumnName = (r) + } + } + if s := string(v.PgType); c.match(s) { + ch := n.Child("pg_type") + if r, ok := c.fn(&ch, s); ok { + v.PgType = postgres.SyncedTableSyncedTableSpecPgSpecificType(r) + } + } +} + +func stringsPostgresScheduleCadence(c *stringsWalk, n *Node, v *postgres.ScheduleCadence) { + if n.isRef() { + return + } + if !n.IsRef("weekly_schedule") { + if x := v.WeeklySchedule; x != nil { + ch := n.Child("weekly_schedule") + stringsPostgresWeeklySchedule(c, &ch, x) + } + } +} + +func stringsResourcesVectorSearchEndpointPermission(c *stringsWalk, n *Node, v *resources.VectorSearchEndpointPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = vectorsearch.VectorSearchEndpointPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsVectorsearchDeltaSyncVectorIndexSpecRequest(c *stringsWalk, n *Node, v *vectorsearch.DeltaSyncVectorIndexSpecRequest) { + if n.isRef() { + return + } + if !n.IsRef("columns_to_index") { + if len(v.ColumnsToIndex) > 0 { + cn := n.Child("columns_to_index") + for i := range v.ColumnsToIndex { + if s := string(v.ColumnsToIndex[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ColumnsToIndex[i] = (r) + } + } + } + } + } + if !n.IsRef("columns_to_sync") { + if len(v.ColumnsToSync) > 0 { + cn := n.Child("columns_to_sync") + for i := range v.ColumnsToSync { + if s := string(v.ColumnsToSync[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ColumnsToSync[i] = (r) + } + } + } + } + } + if !n.IsRef("embedding_source_columns") { + if len(v.EmbeddingSourceColumns) > 0 { + cn := n.Child("embedding_source_columns") + for i := range v.EmbeddingSourceColumns { + { + ch := cn.Elem(i) + stringsVectorsearchEmbeddingSourceColumn(c, &ch, &v.EmbeddingSourceColumns[i]) + } + } + } + } + if !n.IsRef("embedding_vector_columns") { + if len(v.EmbeddingVectorColumns) > 0 { + cn := n.Child("embedding_vector_columns") + for i := range v.EmbeddingVectorColumns { + { + ch := cn.Elem(i) + stringsVectorsearchEmbeddingVectorColumn(c, &ch, &v.EmbeddingVectorColumns[i]) + } + } + } + } + if c.mode == EmptyFields && v.EmbeddingWritebackTable == "" && Forced(v.ForceSendFields, "EmbeddingWritebackTable") { + ch := n.Child("embedding_writeback_table") + c.fn(&ch, "") + } + if s := string(v.EmbeddingWritebackTable); c.match(s) { + ch := n.Child("embedding_writeback_table") + if r, ok := c.fn(&ch, s); ok { + v.EmbeddingWritebackTable = (r) + } + } + if c.mode == EmptyFields && v.PipelineType == "" && Forced(v.ForceSendFields, "PipelineType") { + ch := n.Child("pipeline_type") + c.fn(&ch, "") + } + if s := string(v.PipelineType); c.match(s) { + ch := n.Child("pipeline_type") + if r, ok := c.fn(&ch, s); ok { + v.PipelineType = vectorsearch.PipelineType(r) + } + } + if c.mode == EmptyFields && v.SourceTable == "" && Forced(v.ForceSendFields, "SourceTable") { + ch := n.Child("source_table") + c.fn(&ch, "") + } + if s := string(v.SourceTable); c.match(s) { + ch := n.Child("source_table") + if r, ok := c.fn(&ch, s); ok { + v.SourceTable = (r) + } + } +} + +func stringsVectorsearchDirectAccessVectorIndexSpec(c *stringsWalk, n *Node, v *vectorsearch.DirectAccessVectorIndexSpec) { + if n.isRef() { + return + } + if !n.IsRef("embedding_source_columns") { + if len(v.EmbeddingSourceColumns) > 0 { + cn := n.Child("embedding_source_columns") + for i := range v.EmbeddingSourceColumns { + { + ch := cn.Elem(i) + stringsVectorsearchEmbeddingSourceColumn(c, &ch, &v.EmbeddingSourceColumns[i]) + } + } + } + } + if !n.IsRef("embedding_vector_columns") { + if len(v.EmbeddingVectorColumns) > 0 { + cn := n.Child("embedding_vector_columns") + for i := range v.EmbeddingVectorColumns { + { + ch := cn.Elem(i) + stringsVectorsearchEmbeddingVectorColumn(c, &ch, &v.EmbeddingVectorColumns[i]) + } + } + } + } + if c.mode == EmptyFields && v.SchemaJson == "" && Forced(v.ForceSendFields, "SchemaJson") { + ch := n.Child("schema_json") + c.fn(&ch, "") + } + if s := string(v.SchemaJson); c.match(s) { + ch := n.Child("schema_json") + if r, ok := c.fn(&ch, s); ok { + v.SchemaJson = (r) + } + } +} + +func stringsResourcesInstancePoolPermission(c *stringsWalk, n *Node, v *resources.InstancePoolPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = compute.InstancePoolPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsComputeInstancePoolAwsAttributes(c *stringsWalk, n *Node, v *compute.InstancePoolAwsAttributes) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Availability == "" && Forced(v.ForceSendFields, "Availability") { + ch := n.Child("availability") + c.fn(&ch, "") + } + if s := string(v.Availability); c.match(s) { + ch := n.Child("availability") + if r, ok := c.fn(&ch, s); ok { + v.Availability = compute.InstancePoolAwsAttributesAvailability(r) + } + } + if c.mode == EmptyFields && v.InstanceProfileArn == "" && Forced(v.ForceSendFields, "InstanceProfileArn") { + ch := n.Child("instance_profile_arn") + c.fn(&ch, "") + } + if s := string(v.InstanceProfileArn); c.match(s) { + ch := n.Child("instance_profile_arn") + if r, ok := c.fn(&ch, s); ok { + v.InstanceProfileArn = (r) + } + } + if c.mode == EmptyFields && v.ZoneId == "" && Forced(v.ForceSendFields, "ZoneId") { + ch := n.Child("zone_id") + c.fn(&ch, "") + } + if s := string(v.ZoneId); c.match(s) { + ch := n.Child("zone_id") + if r, ok := c.fn(&ch, s); ok { + v.ZoneId = (r) + } + } +} + +func stringsComputeInstancePoolAzureAttributes(c *stringsWalk, n *Node, v *compute.InstancePoolAzureAttributes) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Availability == "" && Forced(v.ForceSendFields, "Availability") { + ch := n.Child("availability") + c.fn(&ch, "") + } + if s := string(v.Availability); c.match(s) { + ch := n.Child("availability") + if r, ok := c.fn(&ch, s); ok { + v.Availability = compute.InstancePoolAzureAttributesAvailability(r) + } + } + if c.mode == EmptyFields && v.CapacityReservationGroup == "" && Forced(v.ForceSendFields, "CapacityReservationGroup") { + ch := n.Child("capacity_reservation_group") + c.fn(&ch, "") + } + if s := string(v.CapacityReservationGroup); c.match(s) { + ch := n.Child("capacity_reservation_group") + if r, ok := c.fn(&ch, s); ok { + v.CapacityReservationGroup = (r) + } + } +} + +func stringsComputeDiskSpec(c *stringsWalk, n *Node, v *compute.DiskSpec) { + if n.isRef() { + return + } + if !n.IsRef("disk_type") { + if x := v.DiskType; x != nil { + ch := n.Child("disk_type") + stringsComputeDiskType(c, &ch, x) + } + } +} + +func stringsComputeInstancePoolGcpAttributes(c *stringsWalk, n *Node, v *compute.InstancePoolGcpAttributes) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.GcpAvailability == "" && Forced(v.ForceSendFields, "GcpAvailability") { + ch := n.Child("gcp_availability") + c.fn(&ch, "") + } + if s := string(v.GcpAvailability); c.match(s) { + ch := n.Child("gcp_availability") + if r, ok := c.fn(&ch, s); ok { + v.GcpAvailability = compute.GcpAvailability(r) + } + } + if c.mode == EmptyFields && v.ZoneId == "" && Forced(v.ForceSendFields, "ZoneId") { + ch := n.Child("zone_id") + c.fn(&ch, "") + } + if s := string(v.ZoneId); c.match(s) { + ch := n.Child("zone_id") + if r, ok := c.fn(&ch, s); ok { + v.ZoneId = (r) + } + } +} + +func stringsResourcesClusterPolicyPermission(c *stringsWalk, n *Node, v *resources.ClusterPolicyPermission) { + if n.isRef() { + return + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = compute.ClusterPolicyPermissionLevel(r) + } + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } +} + +func stringsSnapshotACLEntry(c *stringsWalk, n *Node, v *snapshot.ACLEntry) { + if n.isRef() { + return + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } + if s := string(v.PermissionLevel); c.match(s) { + ch := n.Child("permission_level") + if r, ok := c.fn(&ch, s); ok { + v.PermissionLevel = (r) + } + } +} + +func stringsSnapshotManagePrincipal(c *stringsWalk, n *Node, v *snapshot.ManagePrincipal) { + if n.isRef() { + return + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } + if s := string(v.GroupName); c.match(s) { + ch := n.Child("group_name") + if r, ok := c.fn(&ch, s); ok { + v.GroupName = (r) + } + } + if s := string(v.ServicePrincipalName); c.match(s) { + ch := n.Child("service_principal_name") + if r, ok := c.fn(&ch, s); ok { + v.ServicePrincipalName = (r) + } + } +} + +func stringsResourcesDependsOn(c *stringsWalk, n *Node, v *resources.DependsOn) { + if n.isRef() { + return + } + if s := string(v.Resource); c.match(s) { + ch := n.Child("resource") + if r, ok := c.fn(&ch, s); ok { + v.Resource = (r) + } + } +} + +func stringsJobsMaintenanceWindow(c *stringsWalk, n *Node, v *jobs.MaintenanceWindow) { + if n.isRef() { + return + } + if s := string(v.DayOfWeek); c.match(s) { + ch := n.Child("day_of_week") + if r, ok := c.fn(&ch, s); ok { + v.DayOfWeek = jobs.DayOfWeek(r) + } + } + if s := string(v.TimezoneId); c.match(s) { + ch := n.Child("timezone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimezoneId = (r) + } + } +} + +func stringsJobsJobEnvironmentVariablesSpec(c *stringsWalk, n *Node, v *jobs.JobEnvironmentVariablesSpec) { + if n.isRef() { + return + } + if !n.IsRef("files") { + if len(v.Files) > 0 { + cn := n.Child("files") + for i := range v.Files { + if s := string(v.Files[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Files[i] = (r) + } + } + } + } + } + if !n.IsRef("variables") { + if len(v.Variables) > 0 { + cn := n.Child("variables") + for _, k := range sortedKeys(v.Variables) { + if s := string(v.Variables[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Variables[k] = (r) + } + } + } + } + } +} + +func stringsComputeEnvironment(c *stringsWalk, n *Node, v *compute.Environment) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.BaseEnvironment == "" && Forced(v.ForceSendFields, "BaseEnvironment") { + ch := n.Child("base_environment") + c.fn(&ch, "") + } + if s := string(v.BaseEnvironment); c.match(s) { + ch := n.Child("base_environment") + if r, ok := c.fn(&ch, s); ok { + v.BaseEnvironment = (r) + } + } + if c.mode == EmptyFields && v.Client == "" && Forced(v.ForceSendFields, "Client") { + ch := n.Child("client") + c.fn(&ch, "") + } + if s := string(v.Client); c.match(s) { + ch := n.Child("client") + if r, ok := c.fn(&ch, s); ok { + v.Client = (r) + } + } + if !n.IsRef("dependencies") { + if len(v.Dependencies) > 0 { + cn := n.Child("dependencies") + for i := range v.Dependencies { + if s := string(v.Dependencies[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Dependencies[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.EnvironmentVersion == "" && Forced(v.ForceSendFields, "EnvironmentVersion") { + ch := n.Child("environment_version") + c.fn(&ch, "") + } + if s := string(v.EnvironmentVersion); c.match(s) { + ch := n.Child("environment_version") + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentVersion = (r) + } + } + if !n.IsRef("java_dependencies") { + if len(v.JavaDependencies) > 0 { + cn := n.Child("java_dependencies") + for i := range v.JavaDependencies { + if s := string(v.JavaDependencies[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.JavaDependencies[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ProjectEnvironment == "" && Forced(v.ForceSendFields, "ProjectEnvironment") { + ch := n.Child("project_environment") + c.fn(&ch, "") + } + if s := string(v.ProjectEnvironment); c.match(s) { + ch := n.Child("project_environment") + if r, ok := c.fn(&ch, s); ok { + v.ProjectEnvironment = (r) + } + } +} + +func stringsJobsGitSnapshot(c *stringsWalk, n *Node, v *jobs.GitSnapshot) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.UsedCommit == "" && Forced(v.ForceSendFields, "UsedCommit") { + ch := n.Child("used_commit") + c.fn(&ch, "") + } + if s := string(v.UsedCommit); c.match(s) { + ch := n.Child("used_commit") + if r, ok := c.fn(&ch, s); ok { + v.UsedCommit = (r) + } + } +} + +func stringsJobsJobSource(c *stringsWalk, n *Node, v *jobs.JobSource) { + if n.isRef() { + return + } + if s := string(v.DirtyState); c.match(s) { + ch := n.Child("dirty_state") + if r, ok := c.fn(&ch, s); ok { + v.DirtyState = jobs.JobSourceDirtyState(r) + } + } + if s := string(v.ImportFromGitBranch); c.match(s) { + ch := n.Child("import_from_git_branch") + if r, ok := c.fn(&ch, s); ok { + v.ImportFromGitBranch = (r) + } + } + if s := string(v.JobConfigPath); c.match(s) { + ch := n.Child("job_config_path") + if r, ok := c.fn(&ch, s); ok { + v.JobConfigPath = (r) + } + } +} + +func stringsJobsSparseCheckout(c *stringsWalk, n *Node, v *jobs.SparseCheckout) { + if n.isRef() { + return + } + if !n.IsRef("patterns") { + if len(v.Patterns) > 0 { + cn := n.Child("patterns") + for i := range v.Patterns { + if s := string(v.Patterns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Patterns[i] = (r) + } + } + } + } + } +} + +func stringsJobsJobsHealthRule(c *stringsWalk, n *Node, v *jobs.JobsHealthRule) { + if n.isRef() { + return + } + if s := string(v.Metric); c.match(s) { + ch := n.Child("metric") + if r, ok := c.fn(&ch, s); ok { + v.Metric = jobs.JobsHealthMetric(r) + } + } + if s := string(v.Op); c.match(s) { + ch := n.Child("op") + if r, ok := c.fn(&ch, s); ok { + v.Op = jobs.JobsHealthOperator(r) + } + } +} + +func stringsComputeClusterSpec(c *stringsWalk, n *Node, v *compute.ClusterSpec) { + if n.isRef() { + return + } + if !n.IsRef("aws_attributes") { + if x := v.AwsAttributes; x != nil { + ch := n.Child("aws_attributes") + stringsComputeAwsAttributes(c, &ch, x) + } + } + if !n.IsRef("azure_attributes") { + if x := v.AzureAttributes; x != nil { + ch := n.Child("azure_attributes") + stringsComputeAzureAttributes(c, &ch, x) + } + } + if !n.IsRef("cluster_log_conf") { + if x := v.ClusterLogConf; x != nil { + ch := n.Child("cluster_log_conf") + stringsComputeClusterLogConf(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ClusterName == "" && Forced(v.ForceSendFields, "ClusterName") { + ch := n.Child("cluster_name") + c.fn(&ch, "") + } + if s := string(v.ClusterName); c.match(s) { + ch := n.Child("cluster_name") + if r, ok := c.fn(&ch, s); ok { + v.ClusterName = (r) + } + } + if !n.IsRef("custom_tags") { + if len(v.CustomTags) > 0 { + cn := n.Child("custom_tags") + for _, k := range sortedKeys(v.CustomTags) { + if s := string(v.CustomTags[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.CustomTags[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.DataSecurityMode == "" && Forced(v.ForceSendFields, "DataSecurityMode") { + ch := n.Child("data_security_mode") + c.fn(&ch, "") + } + if s := string(v.DataSecurityMode); c.match(s) { + ch := n.Child("data_security_mode") + if r, ok := c.fn(&ch, s); ok { + v.DataSecurityMode = compute.DataSecurityMode(r) + } + } + if c.mode == EmptyFields && v.DependencyMode == "" && Forced(v.ForceSendFields, "DependencyMode") { + ch := n.Child("dependency_mode") + c.fn(&ch, "") + } + if s := string(v.DependencyMode); c.match(s) { + ch := n.Child("dependency_mode") + if r, ok := c.fn(&ch, s); ok { + v.DependencyMode = compute.DependencyMode(r) + } + } + if !n.IsRef("docker_image") { + if x := v.DockerImage; x != nil { + ch := n.Child("docker_image") + stringsComputeDockerImage(c, &ch, x) + } + } + if c.mode == EmptyFields && v.DriverInstancePoolId == "" && Forced(v.ForceSendFields, "DriverInstancePoolId") { + ch := n.Child("driver_instance_pool_id") + c.fn(&ch, "") + } + if s := string(v.DriverInstancePoolId); c.match(s) { + ch := n.Child("driver_instance_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.DriverInstancePoolId = (r) + } + } + if !n.IsRef("driver_node_type_flexibility") { + if x := v.DriverNodeTypeFlexibility; x != nil { + ch := n.Child("driver_node_type_flexibility") + stringsComputeNodeTypeFlexibility(c, &ch, x) + } + } + if c.mode == EmptyFields && v.DriverNodeTypeId == "" && Forced(v.ForceSendFields, "DriverNodeTypeId") { + ch := n.Child("driver_node_type_id") + c.fn(&ch, "") + } + if s := string(v.DriverNodeTypeId); c.match(s) { + ch := n.Child("driver_node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.DriverNodeTypeId = (r) + } + } + if !n.IsRef("gcp_attributes") { + if x := v.GcpAttributes; x != nil { + ch := n.Child("gcp_attributes") + stringsComputeGcpAttributes(c, &ch, x) + } + } + if !n.IsRef("init_scripts") { + if len(v.InitScripts) > 0 { + cn := n.Child("init_scripts") + for i := range v.InitScripts { + { + ch := cn.Elem(i) + stringsComputeInitScriptInfo(c, &ch, &v.InitScripts[i]) + } + } + } + } + if c.mode == EmptyFields && v.InstancePoolId == "" && Forced(v.ForceSendFields, "InstancePoolId") { + ch := n.Child("instance_pool_id") + c.fn(&ch, "") + } + if s := string(v.InstancePoolId); c.match(s) { + ch := n.Child("instance_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.InstancePoolId = (r) + } + } + if c.mode == EmptyFields && v.Kind == "" && Forced(v.ForceSendFields, "Kind") { + ch := n.Child("kind") + c.fn(&ch, "") + } + if s := string(v.Kind); c.match(s) { + ch := n.Child("kind") + if r, ok := c.fn(&ch, s); ok { + v.Kind = compute.Kind(r) + } + } + if c.mode == EmptyFields && v.NodeTypeId == "" && Forced(v.ForceSendFields, "NodeTypeId") { + ch := n.Child("node_type_id") + c.fn(&ch, "") + } + if s := string(v.NodeTypeId); c.match(s) { + ch := n.Child("node_type_id") + if r, ok := c.fn(&ch, s); ok { + v.NodeTypeId = (r) + } + } + if c.mode == EmptyFields && v.PolicyId == "" && Forced(v.ForceSendFields, "PolicyId") { + ch := n.Child("policy_id") + c.fn(&ch, "") + } + if s := string(v.PolicyId); c.match(s) { + ch := n.Child("policy_id") + if r, ok := c.fn(&ch, s); ok { + v.PolicyId = (r) + } + } + if c.mode == EmptyFields && v.RuntimeEngine == "" && Forced(v.ForceSendFields, "RuntimeEngine") { + ch := n.Child("runtime_engine") + c.fn(&ch, "") + } + if s := string(v.RuntimeEngine); c.match(s) { + ch := n.Child("runtime_engine") + if r, ok := c.fn(&ch, s); ok { + v.RuntimeEngine = compute.RuntimeEngine(r) + } + } + if c.mode == EmptyFields && v.SingleUserName == "" && Forced(v.ForceSendFields, "SingleUserName") { + ch := n.Child("single_user_name") + c.fn(&ch, "") + } + if s := string(v.SingleUserName); c.match(s) { + ch := n.Child("single_user_name") + if r, ok := c.fn(&ch, s); ok { + v.SingleUserName = (r) + } + } + if !n.IsRef("spark_conf") { + if len(v.SparkConf) > 0 { + cn := n.Child("spark_conf") + for _, k := range sortedKeys(v.SparkConf) { + if s := string(v.SparkConf[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.SparkConf[k] = (r) + } + } + } + } + } + if !n.IsRef("spark_env_vars") { + if len(v.SparkEnvVars) > 0 { + cn := n.Child("spark_env_vars") + for _, k := range sortedKeys(v.SparkEnvVars) { + if s := string(v.SparkEnvVars[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.SparkEnvVars[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.SparkVersion == "" && Forced(v.ForceSendFields, "SparkVersion") { + ch := n.Child("spark_version") + c.fn(&ch, "") + } + if s := string(v.SparkVersion); c.match(s) { + ch := n.Child("spark_version") + if r, ok := c.fn(&ch, s); ok { + v.SparkVersion = (r) + } + } + if !n.IsRef("ssh_public_keys") { + if len(v.SshPublicKeys) > 0 { + cn := n.Child("ssh_public_keys") + for i := range v.SshPublicKeys { + if s := string(v.SshPublicKeys[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.SshPublicKeys[i] = (r) + } + } + } + } + } + if !n.IsRef("worker_node_type_flexibility") { + if x := v.WorkerNodeTypeFlexibility; x != nil { + ch := n.Child("worker_node_type_flexibility") + stringsComputeNodeTypeFlexibility(c, &ch, x) + } + } +} + +func stringsJobsSqlConditionConfiguration(c *stringsWalk, n *Node, v *jobs.SqlConditionConfiguration) { + if n.isRef() { + return + } + if s := string(v.SqlQueryId); c.match(s) { + ch := n.Child("sql_query_id") + if r, ok := c.fn(&ch, s); ok { + v.SqlQueryId = (r) + } + } + if s := string(v.TriggerMode); c.match(s) { + ch := n.Child("trigger_mode") + if r, ok := c.fn(&ch, s); ok { + v.TriggerMode = jobs.SqlConditionTriggerMode(r) + } + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } +} + +func stringsJobsAiRuntimeTask(c *stringsWalk, n *Node, v *jobs.AiRuntimeTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CodeSourcePath == "" && Forced(v.ForceSendFields, "CodeSourcePath") { + ch := n.Child("code_source_path") + c.fn(&ch, "") + } + if s := string(v.CodeSourcePath); c.match(s) { + ch := n.Child("code_source_path") + if r, ok := c.fn(&ch, s); ok { + v.CodeSourcePath = (r) + } + } + if !n.IsRef("deployments") { + if len(v.Deployments) > 0 { + cn := n.Child("deployments") + for i := range v.Deployments { + { + ch := cn.Elem(i) + stringsJobsDeploymentSpec(c, &ch, &v.Deployments[i]) + } + } + } + } + if c.mode == EmptyFields && v.DockerImageUrl == "" && Forced(v.ForceSendFields, "DockerImageUrl") { + ch := n.Child("docker_image_url") + c.fn(&ch, "") + } + if s := string(v.DockerImageUrl); c.match(s) { + ch := n.Child("docker_image_url") + if r, ok := c.fn(&ch, s); ok { + v.DockerImageUrl = (r) + } + } + if s := string(v.Experiment); c.match(s) { + ch := n.Child("experiment") + if r, ok := c.fn(&ch, s); ok { + v.Experiment = (r) + } + } + if c.mode == EmptyFields && v.MlflowArtifactLocation == "" && Forced(v.ForceSendFields, "MlflowArtifactLocation") { + ch := n.Child("mlflow_artifact_location") + c.fn(&ch, "") + } + if s := string(v.MlflowArtifactLocation); c.match(s) { + ch := n.Child("mlflow_artifact_location") + if r, ok := c.fn(&ch, s); ok { + v.MlflowArtifactLocation = (r) + } + } + if c.mode == EmptyFields && v.MlflowExperimentDirectory == "" && Forced(v.ForceSendFields, "MlflowExperimentDirectory") { + ch := n.Child("mlflow_experiment_directory") + c.fn(&ch, "") + } + if s := string(v.MlflowExperimentDirectory); c.match(s) { + ch := n.Child("mlflow_experiment_directory") + if r, ok := c.fn(&ch, s); ok { + v.MlflowExperimentDirectory = (r) + } + } + if c.mode == EmptyFields && v.MlflowRun == "" && Forced(v.ForceSendFields, "MlflowRun") { + ch := n.Child("mlflow_run") + c.fn(&ch, "") + } + if s := string(v.MlflowRun); c.match(s) { + ch := n.Child("mlflow_run") + if r, ok := c.fn(&ch, s); ok { + v.MlflowRun = (r) + } + } + if c.mode == EmptyFields && v.PriorityClass == "" && Forced(v.ForceSendFields, "PriorityClass") { + ch := n.Child("priority_class") + c.fn(&ch, "") + } + if s := string(v.PriorityClass); c.match(s) { + ch := n.Child("priority_class") + if r, ok := c.fn(&ch, s); ok { + v.PriorityClass = jobs.AiRuntimeTaskPriorityClass(r) + } + } + if c.mode == EmptyFields && v.UnityCatalogImagePath == "" && Forced(v.ForceSendFields, "UnityCatalogImagePath") { + ch := n.Child("unity_catalog_image_path") + c.fn(&ch, "") + } + if s := string(v.UnityCatalogImagePath); c.match(s) { + ch := n.Child("unity_catalog_image_path") + if r, ok := c.fn(&ch, s); ok { + v.UnityCatalogImagePath = (r) + } + } +} + +func stringsJobsAlertTask(c *stringsWalk, n *Node, v *jobs.AlertTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AlertId == "" && Forced(v.ForceSendFields, "AlertId") { + ch := n.Child("alert_id") + c.fn(&ch, "") + } + if s := string(v.AlertId); c.match(s) { + ch := n.Child("alert_id") + if r, ok := c.fn(&ch, s); ok { + v.AlertId = (r) + } + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for _, k := range sortedKeys(v.Parameters) { + if s := string(v.Parameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[k] = (r) + } + } + } + } + } + if !n.IsRef("subscribers") { + if len(v.Subscribers) > 0 { + cn := n.Child("subscribers") + for i := range v.Subscribers { + { + ch := cn.Elem(i) + stringsJobsAlertTaskSubscriber(c, &ch, &v.Subscribers[i]) + } + } + } + } + if c.mode == EmptyFields && v.WarehouseId == "" && Forced(v.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } + if c.mode == EmptyFields && v.WorkspacePath == "" && Forced(v.ForceSendFields, "WorkspacePath") { + ch := n.Child("workspace_path") + c.fn(&ch, "") + } + if s := string(v.WorkspacePath); c.match(s) { + ch := n.Child("workspace_path") + if r, ok := c.fn(&ch, s); ok { + v.WorkspacePath = (r) + } + } +} + +func stringsJobsCleanRoomsNotebookTask(c *stringsWalk, n *Node, v *jobs.CleanRoomsNotebookTask) { + if n.isRef() { + return + } + if s := string(v.CleanRoomName); c.match(s) { + ch := n.Child("clean_room_name") + if r, ok := c.fn(&ch, s); ok { + v.CleanRoomName = (r) + } + } + if c.mode == EmptyFields && v.Etag == "" && Forced(v.ForceSendFields, "Etag") { + ch := n.Child("etag") + c.fn(&ch, "") + } + if s := string(v.Etag); c.match(s) { + ch := n.Child("etag") + if r, ok := c.fn(&ch, s); ok { + v.Etag = (r) + } + } + if !n.IsRef("notebook_base_parameters") { + if len(v.NotebookBaseParameters) > 0 { + cn := n.Child("notebook_base_parameters") + for _, k := range sortedKeys(v.NotebookBaseParameters) { + if s := string(v.NotebookBaseParameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.NotebookBaseParameters[k] = (r) + } + } + } + } + } + if s := string(v.NotebookName); c.match(s) { + ch := n.Child("notebook_name") + if r, ok := c.fn(&ch, s); ok { + v.NotebookName = (r) + } + } +} + +func stringsJobsCompute(c *stringsWalk, n *Node, v *jobs.Compute) { + if n.isRef() { + return + } + if s := string(v.HardwareAccelerator); c.match(s) { + ch := n.Child("hardware_accelerator") + if r, ok := c.fn(&ch, s); ok { + v.HardwareAccelerator = compute.HardwareAcceleratorType(r) + } + } +} + +func stringsJobsConditionTask(c *stringsWalk, n *Node, v *jobs.ConditionTask) { + if n.isRef() { + return + } + if s := string(v.Left); c.match(s) { + ch := n.Child("left") + if r, ok := c.fn(&ch, s); ok { + v.Left = (r) + } + } + if s := string(v.Op); c.match(s) { + ch := n.Child("op") + if r, ok := c.fn(&ch, s); ok { + v.Op = jobs.ConditionTaskOp(r) + } + } + if s := string(v.Right); c.match(s) { + ch := n.Child("right") + if r, ok := c.fn(&ch, s); ok { + v.Right = (r) + } + } +} + +func stringsJobsDashboardTask(c *stringsWalk, n *Node, v *jobs.DashboardTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DashboardId == "" && Forced(v.ForceSendFields, "DashboardId") { + ch := n.Child("dashboard_id") + c.fn(&ch, "") + } + if s := string(v.DashboardId); c.match(s) { + ch := n.Child("dashboard_id") + if r, ok := c.fn(&ch, s); ok { + v.DashboardId = (r) + } + } + if !n.IsRef("filters") { + if len(v.Filters) > 0 { + cn := n.Child("filters") + for _, k := range sortedKeys(v.Filters) { + if s := string(v.Filters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Filters[k] = (r) + } + } + } + } + } + if !n.IsRef("subscription") { + if x := v.Subscription; x != nil { + ch := n.Child("subscription") + stringsJobsSubscription(c, &ch, x) + } + } + if c.mode == EmptyFields && v.WarehouseId == "" && Forced(v.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } +} + +func stringsJobsDbtCloudTask(c *stringsWalk, n *Node, v *jobs.DbtCloudTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ConnectionResourceName == "" && Forced(v.ForceSendFields, "ConnectionResourceName") { + ch := n.Child("connection_resource_name") + c.fn(&ch, "") + } + if s := string(v.ConnectionResourceName); c.match(s) { + ch := n.Child("connection_resource_name") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionResourceName = (r) + } + } +} + +func stringsJobsDbtPlatformTask(c *stringsWalk, n *Node, v *jobs.DbtPlatformTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ConnectionResourceName == "" && Forced(v.ForceSendFields, "ConnectionResourceName") { + ch := n.Child("connection_resource_name") + c.fn(&ch, "") + } + if s := string(v.ConnectionResourceName); c.match(s) { + ch := n.Child("connection_resource_name") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionResourceName = (r) + } + } + if c.mode == EmptyFields && v.DbtPlatformJobId == "" && Forced(v.ForceSendFields, "DbtPlatformJobId") { + ch := n.Child("dbt_platform_job_id") + c.fn(&ch, "") + } + if s := string(v.DbtPlatformJobId); c.match(s) { + ch := n.Child("dbt_platform_job_id") + if r, ok := c.fn(&ch, s); ok { + v.DbtPlatformJobId = (r) + } + } +} + +func stringsJobsDbtTask(c *stringsWalk, n *Node, v *jobs.DbtTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Catalog == "" && Forced(v.ForceSendFields, "Catalog") { + ch := n.Child("catalog") + c.fn(&ch, "") + } + if s := string(v.Catalog); c.match(s) { + ch := n.Child("catalog") + if r, ok := c.fn(&ch, s); ok { + v.Catalog = (r) + } + } + if !n.IsRef("commands") { + if len(v.Commands) > 0 { + cn := n.Child("commands") + for i := range v.Commands { + if s := string(v.Commands[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Commands[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ProfilesDirectory == "" && Forced(v.ForceSendFields, "ProfilesDirectory") { + ch := n.Child("profiles_directory") + c.fn(&ch, "") + } + if s := string(v.ProfilesDirectory); c.match(s) { + ch := n.Child("profiles_directory") + if r, ok := c.fn(&ch, s); ok { + v.ProfilesDirectory = (r) + } + } + if c.mode == EmptyFields && v.ProjectDirectory == "" && Forced(v.ForceSendFields, "ProjectDirectory") { + ch := n.Child("project_directory") + c.fn(&ch, "") + } + if s := string(v.ProjectDirectory); c.match(s) { + ch := n.Child("project_directory") + if r, ok := c.fn(&ch, s); ok { + v.ProjectDirectory = (r) + } + } + if c.mode == EmptyFields && v.Schema == "" && Forced(v.ForceSendFields, "Schema") { + ch := n.Child("schema") + c.fn(&ch, "") + } + if s := string(v.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.Schema = (r) + } + } + if c.mode == EmptyFields && v.Source == "" && Forced(v.ForceSendFields, "Source") { + ch := n.Child("source") + c.fn(&ch, "") + } + if s := string(v.Source); c.match(s) { + ch := n.Child("source") + if r, ok := c.fn(&ch, s); ok { + v.Source = jobs.Source(r) + } + } + if c.mode == EmptyFields && v.WarehouseId == "" && Forced(v.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } +} + +func stringsJobsTaskDependency(c *stringsWalk, n *Node, v *jobs.TaskDependency) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Outcome == "" && Forced(v.ForceSendFields, "Outcome") { + ch := n.Child("outcome") + c.fn(&ch, "") + } + if s := string(v.Outcome); c.match(s) { + ch := n.Child("outcome") + if r, ok := c.fn(&ch, s); ok { + v.Outcome = (r) + } + } + if s := string(v.TaskKey); c.match(s) { + ch := n.Child("task_key") + if r, ok := c.fn(&ch, s); ok { + v.TaskKey = (r) + } + } +} + +func stringsJobsTaskEmailNotifications(c *stringsWalk, n *Node, v *jobs.TaskEmailNotifications) { + if n.isRef() { + return + } + if !n.IsRef("on_duration_warning_threshold_exceeded") { + if len(v.OnDurationWarningThresholdExceeded) > 0 { + cn := n.Child("on_duration_warning_threshold_exceeded") + for i := range v.OnDurationWarningThresholdExceeded { + if s := string(v.OnDurationWarningThresholdExceeded[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnDurationWarningThresholdExceeded[i] = (r) + } + } + } + } + } + if !n.IsRef("on_failure") { + if len(v.OnFailure) > 0 { + cn := n.Child("on_failure") + for i := range v.OnFailure { + if s := string(v.OnFailure[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnFailure[i] = (r) + } + } + } + } + } + if !n.IsRef("on_maintenance_complete") { + if len(v.OnMaintenanceComplete) > 0 { + cn := n.Child("on_maintenance_complete") + for i := range v.OnMaintenanceComplete { + if s := string(v.OnMaintenanceComplete[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnMaintenanceComplete[i] = (r) + } + } + } + } + } + if !n.IsRef("on_maintenance_start") { + if len(v.OnMaintenanceStart) > 0 { + cn := n.Child("on_maintenance_start") + for i := range v.OnMaintenanceStart { + if s := string(v.OnMaintenanceStart[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnMaintenanceStart[i] = (r) + } + } + } + } + } + if !n.IsRef("on_start") { + if len(v.OnStart) > 0 { + cn := n.Child("on_start") + for i := range v.OnStart { + if s := string(v.OnStart[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnStart[i] = (r) + } + } + } + } + } + if !n.IsRef("on_streaming_backlog_exceeded") { + if len(v.OnStreamingBacklogExceeded) > 0 { + cn := n.Child("on_streaming_backlog_exceeded") + for i := range v.OnStreamingBacklogExceeded { + if s := string(v.OnStreamingBacklogExceeded[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnStreamingBacklogExceeded[i] = (r) + } + } + } + } + } + if !n.IsRef("on_success") { + if len(v.OnSuccess) > 0 { + cn := n.Child("on_success") + for i := range v.OnSuccess { + if s := string(v.OnSuccess[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.OnSuccess[i] = (r) + } + } + } + } + } +} + +func stringsJobsForEachTask(c *stringsWalk, n *Node, v *jobs.ForEachTask) { + if n.isRef() { + return + } + if s := string(v.Inputs); c.match(s) { + ch := n.Child("inputs") + if r, ok := c.fn(&ch, s); ok { + v.Inputs = (r) + } + } + if !n.IsRef("task") { + { + ch := n.Child("task") + stringsJobsTask(c, &ch, &v.Task) + } + } +} + +func stringsJobsGenAiComputeTask(c *stringsWalk, n *Node, v *jobs.GenAiComputeTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Command == "" && Forced(v.ForceSendFields, "Command") { + ch := n.Child("command") + c.fn(&ch, "") + } + if s := string(v.Command); c.match(s) { + ch := n.Child("command") + if r, ok := c.fn(&ch, s); ok { + v.Command = (r) + } + } + if !n.IsRef("compute") { + if x := v.Compute; x != nil { + ch := n.Child("compute") + stringsJobsComputeConfig(c, &ch, x) + } + } + if s := string(v.DlRuntimeImage); c.match(s) { + ch := n.Child("dl_runtime_image") + if r, ok := c.fn(&ch, s); ok { + v.DlRuntimeImage = (r) + } + } + if c.mode == EmptyFields && v.MlflowExperimentName == "" && Forced(v.ForceSendFields, "MlflowExperimentName") { + ch := n.Child("mlflow_experiment_name") + c.fn(&ch, "") + } + if s := string(v.MlflowExperimentName); c.match(s) { + ch := n.Child("mlflow_experiment_name") + if r, ok := c.fn(&ch, s); ok { + v.MlflowExperimentName = (r) + } + } + if c.mode == EmptyFields && v.Source == "" && Forced(v.ForceSendFields, "Source") { + ch := n.Child("source") + c.fn(&ch, "") + } + if s := string(v.Source); c.match(s) { + ch := n.Child("source") + if r, ok := c.fn(&ch, s); ok { + v.Source = jobs.Source(r) + } + } + if c.mode == EmptyFields && v.TrainingScriptPath == "" && Forced(v.ForceSendFields, "TrainingScriptPath") { + ch := n.Child("training_script_path") + c.fn(&ch, "") + } + if s := string(v.TrainingScriptPath); c.match(s) { + ch := n.Child("training_script_path") + if r, ok := c.fn(&ch, s); ok { + v.TrainingScriptPath = (r) + } + } + if c.mode == EmptyFields && v.YamlParameters == "" && Forced(v.ForceSendFields, "YamlParameters") { + ch := n.Child("yaml_parameters") + c.fn(&ch, "") + } + if s := string(v.YamlParameters); c.match(s) { + ch := n.Child("yaml_parameters") + if r, ok := c.fn(&ch, s); ok { + v.YamlParameters = (r) + } + } + if c.mode == EmptyFields && v.YamlParametersFilePath == "" && Forced(v.ForceSendFields, "YamlParametersFilePath") { + ch := n.Child("yaml_parameters_file_path") + c.fn(&ch, "") + } + if s := string(v.YamlParametersFilePath); c.match(s) { + ch := n.Child("yaml_parameters_file_path") + if r, ok := c.fn(&ch, s); ok { + v.YamlParametersFilePath = (r) + } + } +} + +func stringsJobsNotebookTask(c *stringsWalk, n *Node, v *jobs.NotebookTask) { + if n.isRef() { + return + } + if !n.IsRef("base_parameters") { + if len(v.BaseParameters) > 0 { + cn := n.Child("base_parameters") + for _, k := range sortedKeys(v.BaseParameters) { + if s := string(v.BaseParameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.BaseParameters[k] = (r) + } + } + } + } + } + if s := string(v.NotebookPath); c.match(s) { + ch := n.Child("notebook_path") + if r, ok := c.fn(&ch, s); ok { + v.NotebookPath = (r) + } + } + if c.mode == EmptyFields && v.Source == "" && Forced(v.ForceSendFields, "Source") { + ch := n.Child("source") + c.fn(&ch, "") + } + if s := string(v.Source); c.match(s) { + ch := n.Child("source") + if r, ok := c.fn(&ch, s); ok { + v.Source = jobs.Source(r) + } + } + if c.mode == EmptyFields && v.WarehouseId == "" && Forced(v.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } +} + +func stringsJobsPipelineTask(c *stringsWalk, n *Node, v *jobs.PipelineTask) { + if n.isRef() { + return + } + if !n.IsRef("full_refresh_selection") { + if len(v.FullRefreshSelection) > 0 { + cn := n.Child("full_refresh_selection") + for i := range v.FullRefreshSelection { + if s := string(v.FullRefreshSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.FullRefreshSelection[i] = (r) + } + } + } + } + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for _, k := range sortedKeys(v.Parameters) { + if s := string(v.Parameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[k] = (r) + } + } + } + } + } + if s := string(v.PipelineId); c.match(s) { + ch := n.Child("pipeline_id") + if r, ok := c.fn(&ch, s); ok { + v.PipelineId = (r) + } + } + if !n.IsRef("refresh_flow_selection") { + if len(v.RefreshFlowSelection) > 0 { + cn := n.Child("refresh_flow_selection") + for i := range v.RefreshFlowSelection { + if s := string(v.RefreshFlowSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RefreshFlowSelection[i] = (r) + } + } + } + } + } + if !n.IsRef("refresh_selection") { + if len(v.RefreshSelection) > 0 { + cn := n.Child("refresh_selection") + for i := range v.RefreshSelection { + if s := string(v.RefreshSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.RefreshSelection[i] = (r) + } + } + } + } + } + if !n.IsRef("reset_checkpoint_selection") { + if len(v.ResetCheckpointSelection) > 0 { + cn := n.Child("reset_checkpoint_selection") + for i := range v.ResetCheckpointSelection { + if s := string(v.ResetCheckpointSelection[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ResetCheckpointSelection[i] = (r) + } + } + } + } + } +} + +func stringsJobsPowerBiTask(c *stringsWalk, n *Node, v *jobs.PowerBiTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ConnectionResourceName == "" && Forced(v.ForceSendFields, "ConnectionResourceName") { + ch := n.Child("connection_resource_name") + c.fn(&ch, "") + } + if s := string(v.ConnectionResourceName); c.match(s) { + ch := n.Child("connection_resource_name") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionResourceName = (r) + } + } + if !n.IsRef("power_bi_model") { + if x := v.PowerBiModel; x != nil { + ch := n.Child("power_bi_model") + stringsJobsPowerBiModel(c, &ch, x) + } + } + if !n.IsRef("tables") { + if len(v.Tables) > 0 { + cn := n.Child("tables") + for i := range v.Tables { + { + ch := cn.Elem(i) + stringsJobsPowerBiTable(c, &ch, &v.Tables[i]) + } + } + } + } + if c.mode == EmptyFields && v.WarehouseId == "" && Forced(v.ForceSendFields, "WarehouseId") { + ch := n.Child("warehouse_id") + c.fn(&ch, "") + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } +} + +func stringsJobsPythonOperatorTask(c *stringsWalk, n *Node, v *jobs.PythonOperatorTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Main == "" && Forced(v.ForceSendFields, "Main") { + ch := n.Child("main") + c.fn(&ch, "") + } + if s := string(v.Main); c.match(s) { + ch := n.Child("main") + if r, ok := c.fn(&ch, s); ok { + v.Main = (r) + } + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.Parameters { + { + ch := cn.Elem(i) + stringsJobsPythonOperatorTaskParameter(c, &ch, &v.Parameters[i]) + } + } + } + } +} + +func stringsJobsPythonWheelTask(c *stringsWalk, n *Node, v *jobs.PythonWheelTask) { + if n.isRef() { + return + } + if s := string(v.EntryPoint); c.match(s) { + ch := n.Child("entry_point") + if r, ok := c.fn(&ch, s); ok { + v.EntryPoint = (r) + } + } + if !n.IsRef("named_parameters") { + if len(v.NamedParameters) > 0 { + cn := n.Child("named_parameters") + for _, k := range sortedKeys(v.NamedParameters) { + if s := string(v.NamedParameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.NamedParameters[k] = (r) + } + } + } + } + } + if s := string(v.PackageName); c.match(s) { + ch := n.Child("package_name") + if r, ok := c.fn(&ch, s); ok { + v.PackageName = (r) + } + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.Parameters { + if s := string(v.Parameters[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[i] = (r) + } + } + } + } + } +} + +func stringsJobsRunJobTask(c *stringsWalk, n *Node, v *jobs.RunJobTask) { + if n.isRef() { + return + } + if !n.IsRef("dbt_commands") { + if len(v.DbtCommands) > 0 { + cn := n.Child("dbt_commands") + for i := range v.DbtCommands { + if s := string(v.DbtCommands[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.DbtCommands[i] = (r) + } + } + } + } + } + if !n.IsRef("jar_params") { + if len(v.JarParams) > 0 { + cn := n.Child("jar_params") + for i := range v.JarParams { + if s := string(v.JarParams[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.JarParams[i] = (r) + } + } + } + } + } + if !n.IsRef("job_parameters") { + if len(v.JobParameters) > 0 { + cn := n.Child("job_parameters") + for _, k := range sortedKeys(v.JobParameters) { + if s := string(v.JobParameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.JobParameters[k] = (r) + } + } + } + } + } + if !n.IsRef("notebook_params") { + if len(v.NotebookParams) > 0 { + cn := n.Child("notebook_params") + for _, k := range sortedKeys(v.NotebookParams) { + if s := string(v.NotebookParams[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.NotebookParams[k] = (r) + } + } + } + } + } + if !n.IsRef("pipeline_params") { + if x := v.PipelineParams; x != nil { + ch := n.Child("pipeline_params") + stringsJobsPipelineParams(c, &ch, x) + } + } + if !n.IsRef("python_named_params") { + if len(v.PythonNamedParams) > 0 { + cn := n.Child("python_named_params") + for _, k := range sortedKeys(v.PythonNamedParams) { + if s := string(v.PythonNamedParams[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.PythonNamedParams[k] = (r) + } + } + } + } + } + if !n.IsRef("python_params") { + if len(v.PythonParams) > 0 { + cn := n.Child("python_params") + for i := range v.PythonParams { + if s := string(v.PythonParams[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.PythonParams[i] = (r) + } + } + } + } + } + if !n.IsRef("spark_submit_params") { + if len(v.SparkSubmitParams) > 0 { + cn := n.Child("spark_submit_params") + for i := range v.SparkSubmitParams { + if s := string(v.SparkSubmitParams[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.SparkSubmitParams[i] = (r) + } + } + } + } + } + if !n.IsRef("sql_params") { + if len(v.SqlParams) > 0 { + cn := n.Child("sql_params") + for _, k := range sortedKeys(v.SqlParams) { + if s := string(v.SqlParams[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.SqlParams[k] = (r) + } + } + } + } + } +} + +func stringsJobsSparkJarTask(c *stringsWalk, n *Node, v *jobs.SparkJarTask) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.JarUri == "" && Forced(v.ForceSendFields, "JarUri") { + ch := n.Child("jar_uri") + c.fn(&ch, "") + } + if s := string(v.JarUri); c.match(s) { + ch := n.Child("jar_uri") + if r, ok := c.fn(&ch, s); ok { + v.JarUri = (r) + } + } + if c.mode == EmptyFields && v.MainClassName == "" && Forced(v.ForceSendFields, "MainClassName") { + ch := n.Child("main_class_name") + c.fn(&ch, "") + } + if s := string(v.MainClassName); c.match(s) { + ch := n.Child("main_class_name") + if r, ok := c.fn(&ch, s); ok { + v.MainClassName = (r) + } + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.Parameters { + if s := string(v.Parameters[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[i] = (r) + } + } + } + } + } +} + +func stringsJobsSparkPythonTask(c *stringsWalk, n *Node, v *jobs.SparkPythonTask) { + if n.isRef() { + return + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.Parameters { + if s := string(v.Parameters[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[i] = (r) + } + } + } + } + } + if s := string(v.PythonFile); c.match(s) { + ch := n.Child("python_file") + if r, ok := c.fn(&ch, s); ok { + v.PythonFile = (r) + } + } + if s := string(v.Source); c.match(s) { + ch := n.Child("source") + if r, ok := c.fn(&ch, s); ok { + v.Source = jobs.Source(r) + } + } +} + +func stringsJobsSparkSubmitTask(c *stringsWalk, n *Node, v *jobs.SparkSubmitTask) { + if n.isRef() { + return + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for i := range v.Parameters { + if s := string(v.Parameters[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[i] = (r) + } + } + } + } + } +} + +func stringsJobsSqlTask(c *stringsWalk, n *Node, v *jobs.SqlTask) { + if n.isRef() { + return + } + if !n.IsRef("alert") { + if x := v.Alert; x != nil { + ch := n.Child("alert") + stringsJobsSqlTaskAlert(c, &ch, x) + } + } + if !n.IsRef("dashboard") { + if x := v.Dashboard; x != nil { + ch := n.Child("dashboard") + stringsJobsSqlTaskDashboard(c, &ch, x) + } + } + if !n.IsRef("file") { + if x := v.File; x != nil { + ch := n.Child("file") + stringsJobsSqlTaskFile(c, &ch, x) + } + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for _, k := range sortedKeys(v.Parameters) { + if s := string(v.Parameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[k] = (r) + } + } + } + } + } + if !n.IsRef("query") { + if x := v.Query; x != nil { + ch := n.Child("query") + stringsJobsSqlTaskQuery(c, &ch, x) + } + } + if s := string(v.WarehouseId); c.match(s) { + ch := n.Child("warehouse_id") + if r, ok := c.fn(&ch, s); ok { + v.WarehouseId = (r) + } + } +} + +func stringsJobsFileArrivalTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.FileArrivalTriggerConfiguration) { + if n.isRef() { + return + } + if s := string(v.Url); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.Url = (r) + } + } +} + +func stringsJobsModelTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.ModelTriggerConfiguration) { + if n.isRef() { + return + } + if !n.IsRef("aliases") { + if len(v.Aliases) > 0 { + cn := n.Child("aliases") + for i := range v.Aliases { + if s := string(v.Aliases[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Aliases[i] = (r) + } + } + } + } + } + if s := string(v.Condition); c.match(s) { + ch := n.Child("condition") + if r, ok := c.fn(&ch, s); ok { + v.Condition = jobs.ModelTriggerConfigurationCondition(r) + } + } + if c.mode == EmptyFields && v.SecurableName == "" && Forced(v.ForceSendFields, "SecurableName") { + ch := n.Child("securable_name") + c.fn(&ch, "") + } + if s := string(v.SecurableName); c.match(s) { + ch := n.Child("securable_name") + if r, ok := c.fn(&ch, s); ok { + v.SecurableName = (r) + } + } +} + +func stringsJobsPeriodicTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.PeriodicTriggerConfiguration) { + if n.isRef() { + return + } + if s := string(v.Unit); c.match(s) { + ch := n.Child("unit") + if r, ok := c.fn(&ch, s); ok { + v.Unit = jobs.PeriodicTriggerConfigurationTimeUnit(r) + } + } +} + +func stringsJobsTableUpdateTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.TableUpdateTriggerConfiguration) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Condition == "" && Forced(v.ForceSendFields, "Condition") { + ch := n.Child("condition") + c.fn(&ch, "") + } + if s := string(v.Condition); c.match(s) { + ch := n.Child("condition") + if r, ok := c.fn(&ch, s); ok { + v.Condition = jobs.Condition(r) + } + } + if !n.IsRef("table_names") { + if len(v.TableNames) > 0 { + cn := n.Child("table_names") + for i := range v.TableNames { + if s := string(v.TableNames[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.TableNames[i] = (r) + } + } + } + } + } +} + +func stringsJobsContinuousTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.ContinuousTriggerConfiguration) { + if n.isRef() { + return + } + if !n.IsRef("maintenance_window") { + if x := v.MaintenanceWindow; x != nil { + ch := n.Child("maintenance_window") + stringsJobsMaintenanceWindow(c, &ch, x) + } + } + if s := string(v.TaskRetryMode); c.match(s) { + ch := n.Child("task_retry_mode") + if r, ok := c.fn(&ch, s); ok { + v.TaskRetryMode = jobs.TaskRetryMode(r) + } + } +} + +func stringsJobsCronTriggerConfiguration(c *stringsWalk, n *Node, v *jobs.CronTriggerConfiguration) { + if n.isRef() { + return + } + if s := string(v.QuartzCronExpression); c.match(s) { + ch := n.Child("quartz_cron_expression") + if r, ok := c.fn(&ch, s); ok { + v.QuartzCronExpression = (r) + } + } + if s := string(v.TimezoneId); c.match(s) { + ch := n.Child("timezone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimezoneId = (r) + } + } +} + +func stringsJobsWebhook(c *stringsWalk, n *Node, v *jobs.Webhook) { + if n.isRef() { + return + } + if s := string(v.Id); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.Id = (r) + } + } +} + +func stringsResourcesJobRunTrigger(c *stringsWalk, n *Node, v *resources.JobRunTrigger) { + if n.isRef() { + return + } + if !n.IsRef("on_file_change") { + if c.mode == EmptyFields && v.OnFileChange != nil && *v.OnFileChange == "" { + ch := n.Child("on_file_change") + c.fn(&ch, "") + } + if x := v.OnFileChange; x != nil { + if s := string(*x); c.match(s) { + ch := n.Child("on_file_change") + if r, ok := c.fn(&ch, s); ok { + *x = (r) + } + } + } + } +} + +func stringsResourcesJobRunTriggersState(c *stringsWalk, n *Node, v *resources.JobRunTriggersState) { + if n.isRef() { + return + } + if s := string(v.OnBundleDeploy); c.match(s) { + ch := n.Child("on_bundle_deploy") + if r, ok := c.fn(&ch, s); ok { + v.OnBundleDeploy = (r) + } + } + if !n.IsRef("on_file_change") { + if len(v.OnFileChange) > 0 { + cn := n.Child("on_file_change") + for _, k := range sortedKeys(v.OnFileChange) { + if s := string(v.OnFileChange[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.OnFileChange[k] = (r) + } + } + } + } + } +} + +func stringsPipelinesPipelineClusterAutoscale(c *stringsWalk, n *Node, v *pipelines.PipelineClusterAutoscale) { + if n.isRef() { + return + } + if s := string(v.Mode); c.match(s) { + ch := n.Child("mode") + if r, ok := c.fn(&ch, s); ok { + v.Mode = pipelines.PipelineClusterAutoscaleMode(r) + } + } +} + +func stringsPipelinesConnectionParameters(c *stringsWalk, n *Node, v *pipelines.ConnectionParameters) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.SourceCatalog == "" && Forced(v.ForceSendFields, "SourceCatalog") { + ch := n.Child("source_catalog") + c.fn(&ch, "") + } + if s := string(v.SourceCatalog); c.match(s) { + ch := n.Child("source_catalog") + if r, ok := c.fn(&ch, s); ok { + v.SourceCatalog = (r) + } + } +} + +func stringsPipelinesDataStagingOptions(c *stringsWalk, n *Node, v *pipelines.DataStagingOptions) { + if n.isRef() { + return + } + if s := string(v.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CatalogName = (r) + } + } + if s := string(v.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.SchemaName = (r) + } + } + if c.mode == EmptyFields && v.VolumeName == "" && Forced(v.ForceSendFields, "VolumeName") { + ch := n.Child("volume_name") + c.fn(&ch, "") + } + if s := string(v.VolumeName); c.match(s) { + ch := n.Child("volume_name") + if r, ok := c.fn(&ch, s); ok { + v.VolumeName = (r) + } + } +} + +func stringsPipelinesOperationTimeWindow(c *stringsWalk, n *Node, v *pipelines.OperationTimeWindow) { + if n.isRef() { + return + } + if !n.IsRef("days_of_week") { + if len(v.DaysOfWeek) > 0 { + cn := n.Child("days_of_week") + for i := range v.DaysOfWeek { + if s := string(v.DaysOfWeek[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.DaysOfWeek[i] = pipelines.DayOfWeek(r) + } + } + } + } + } + if c.mode == EmptyFields && v.TimeZoneId == "" && Forced(v.ForceSendFields, "TimeZoneId") { + ch := n.Child("time_zone_id") + c.fn(&ch, "") + } + if s := string(v.TimeZoneId); c.match(s) { + ch := n.Child("time_zone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimeZoneId = (r) + } + } +} + +func stringsPipelinesIngestionConfig(c *stringsWalk, n *Node, v *pipelines.IngestionConfig) { + if n.isRef() { + return + } + if !n.IsRef("report") { + if x := v.Report; x != nil { + ch := n.Child("report") + stringsPipelinesReportSpec(c, &ch, x) + } + } + if !n.IsRef("schema") { + if x := v.Schema; x != nil { + ch := n.Child("schema") + stringsPipelinesSchemaSpec(c, &ch, x) + } + } + if !n.IsRef("table") { + if x := v.Table; x != nil { + ch := n.Child("table") + stringsPipelinesTableSpec(c, &ch, x) + } + } +} + +func stringsPipelinesSourceConfig(c *stringsWalk, n *Node, v *pipelines.SourceConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_source_connector_config") { + if x := v.ApiSourceConnectorConfig; x != nil { + ch := n.Child("api_source_connector_config") + stringsPipelinesApiSourceConnectorConfig(c, &ch, x) + } + } + if !n.IsRef("catalog") { + if x := v.Catalog; x != nil { + ch := n.Child("catalog") + stringsPipelinesSourceCatalogConfig(c, &ch, x) + } + } + if !n.IsRef("google_ads_config") { + if x := v.GoogleAdsConfig; x != nil { + ch := n.Child("google_ads_config") + stringsPipelinesGoogleAdsConfig(c, &ch, x) + } + } +} + +func stringsPipelinesTableSpecificConfig(c *stringsWalk, n *Node, v *pipelines.TableSpecificConfig) { + if n.isRef() { + return + } + if !n.IsRef("clustering_columns") { + if len(v.ClusteringColumns) > 0 { + cn := n.Child("clustering_columns") + for i := range v.ClusteringColumns { + if s := string(v.ClusteringColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ClusteringColumns[i] = (r) + } + } + } + } + } + if !n.IsRef("exclude_columns") { + if len(v.ExcludeColumns) > 0 { + cn := n.Child("exclude_columns") + for i := range v.ExcludeColumns { + if s := string(v.ExcludeColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ExcludeColumns[i] = (r) + } + } + } + } + } + if !n.IsRef("include_columns") { + if len(v.IncludeColumns) > 0 { + cn := n.Child("include_columns") + for i := range v.IncludeColumns { + if s := string(v.IncludeColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeColumns[i] = (r) + } + } + } + } + } + if !n.IsRef("primary_keys") { + if len(v.PrimaryKeys) > 0 { + cn := n.Child("primary_keys") + for i := range v.PrimaryKeys { + if s := string(v.PrimaryKeys[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.PrimaryKeys[i] = (r) + } + } + } + } + } + if !n.IsRef("query_based_connector_config") { + if x := v.QueryBasedConnectorConfig; x != nil { + ch := n.Child("query_based_connector_config") + stringsPipelinesIngestionPipelineDefinitionTableSpecificConfigQueryBasedConnectorConfig(c, &ch, x) + } + } + if c.mode == EmptyFields && v.RowFilter == "" && Forced(v.ForceSendFields, "RowFilter") { + ch := n.Child("row_filter") + c.fn(&ch, "") + } + if s := string(v.RowFilter); c.match(s) { + ch := n.Child("row_filter") + if r, ok := c.fn(&ch, s); ok { + v.RowFilter = (r) + } + } + if c.mode == EmptyFields && v.ScdType == "" && Forced(v.ForceSendFields, "ScdType") { + ch := n.Child("scd_type") + c.fn(&ch, "") + } + if s := string(v.ScdType); c.match(s) { + ch := n.Child("scd_type") + if r, ok := c.fn(&ch, s); ok { + v.ScdType = pipelines.TableSpecificConfigScdType(r) + } + } + if !n.IsRef("sequence_by") { + if len(v.SequenceBy) > 0 { + cn := n.Child("sequence_by") + for i := range v.SequenceBy { + if s := string(v.SequenceBy[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.SequenceBy[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.SourceMetadataColumn == "" && Forced(v.ForceSendFields, "SourceMetadataColumn") { + ch := n.Child("source_metadata_column") + c.fn(&ch, "") + } + if s := string(v.SourceMetadataColumn); c.match(s) { + ch := n.Child("source_metadata_column") + if r, ok := c.fn(&ch, s); ok { + v.SourceMetadataColumn = (r) + } + } + if !n.IsRef("table_properties") { + if len(v.TableProperties) > 0 { + cn := n.Child("table_properties") + for _, k := range sortedKeys(v.TableProperties) { + if s := string(v.TableProperties[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.TableProperties[k] = (r) + } + } + } + } + } + if !n.IsRef("workday_report_parameters") { + if x := v.WorkdayReportParameters; x != nil { + ch := n.Child("workday_report_parameters") + stringsPipelinesIngestionPipelineDefinitionWorkdayReportParameters(c, &ch, x) + } + } +} + +func stringsPipelinesFileLibrary(c *stringsWalk, n *Node, v *pipelines.FileLibrary) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Path == "" && Forced(v.ForceSendFields, "Path") { + ch := n.Child("path") + c.fn(&ch, "") + } + if s := string(v.Path); c.match(s) { + ch := n.Child("path") + if r, ok := c.fn(&ch, s); ok { + v.Path = (r) + } + } +} + +func stringsPipelinesPathPattern(c *stringsWalk, n *Node, v *pipelines.PathPattern) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Include == "" && Forced(v.ForceSendFields, "Include") { + ch := n.Child("include") + c.fn(&ch, "") + } + if s := string(v.Include); c.match(s) { + ch := n.Child("include") + if r, ok := c.fn(&ch, s); ok { + v.Include = (r) + } + } +} + +func stringsComputeMavenLibrary(c *stringsWalk, n *Node, v *compute.MavenLibrary) { + if n.isRef() { + return + } + if s := string(v.Coordinates); c.match(s) { + ch := n.Child("coordinates") + if r, ok := c.fn(&ch, s); ok { + v.Coordinates = (r) + } + } + if !n.IsRef("exclusions") { + if len(v.Exclusions) > 0 { + cn := n.Child("exclusions") + for i := range v.Exclusions { + if s := string(v.Exclusions[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Exclusions[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.Repo == "" && Forced(v.ForceSendFields, "Repo") { + ch := n.Child("repo") + c.fn(&ch, "") + } + if s := string(v.Repo); c.match(s) { + ch := n.Child("repo") + if r, ok := c.fn(&ch, s); ok { + v.Repo = (r) + } + } +} + +func stringsPipelinesNotebookLibrary(c *stringsWalk, n *Node, v *pipelines.NotebookLibrary) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Path == "" && Forced(v.ForceSendFields, "Path") { + ch := n.Child("path") + c.fn(&ch, "") + } + if s := string(v.Path); c.match(s) { + ch := n.Child("path") + if r, ok := c.fn(&ch, s); ok { + v.Path = (r) + } + } +} + +func stringsPipelinesCronTrigger(c *stringsWalk, n *Node, v *pipelines.CronTrigger) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.QuartzCronSchedule == "" && Forced(v.ForceSendFields, "QuartzCronSchedule") { + ch := n.Child("quartz_cron_schedule") + c.fn(&ch, "") + } + if s := string(v.QuartzCronSchedule); c.match(s) { + ch := n.Child("quartz_cron_schedule") + if r, ok := c.fn(&ch, s); ok { + v.QuartzCronSchedule = (r) + } + } + if c.mode == EmptyFields && v.TimezoneId == "" && Forced(v.ForceSendFields, "TimezoneId") { + ch := n.Child("timezone_id") + c.fn(&ch, "") + } + if s := string(v.TimezoneId); c.match(s) { + ch := n.Child("timezone_id") + if r, ok := c.fn(&ch, s); ok { + v.TimezoneId = (r) + } + } +} + +func stringsMlAggregationFunction(c *stringsWalk, n *Node, v *ml.AggregationFunction) { + if n.isRef() { + return + } + if !n.IsRef("approx_count_distinct") { + if x := v.ApproxCountDistinct; x != nil { + ch := n.Child("approx_count_distinct") + stringsMlApproxCountDistinctFunction(c, &ch, x) + } + } + if !n.IsRef("approx_percentile") { + if x := v.ApproxPercentile; x != nil { + ch := n.Child("approx_percentile") + stringsMlApproxPercentileFunction(c, &ch, x) + } + } + if !n.IsRef("avg") { + if x := v.Avg; x != nil { + ch := n.Child("avg") + stringsMlAvgFunction(c, &ch, x) + } + } + if !n.IsRef("count_function") { + if x := v.CountFunction; x != nil { + ch := n.Child("count_function") + stringsMlCountFunction(c, &ch, x) + } + } + if !n.IsRef("first") { + if x := v.First; x != nil { + ch := n.Child("first") + stringsMlFirstFunction(c, &ch, x) + } + } + if !n.IsRef("first_distinct") { + if x := v.FirstDistinct; x != nil { + ch := n.Child("first_distinct") + stringsMlFirstDistinctFunction(c, &ch, x) + } + } + if !n.IsRef("first_n") { + if x := v.FirstN; x != nil { + ch := n.Child("first_n") + stringsMlFirstNFunction(c, &ch, x) + } + } + if !n.IsRef("last") { + if x := v.Last; x != nil { + ch := n.Child("last") + stringsMlLastFunction(c, &ch, x) + } + } + if !n.IsRef("last_distinct") { + if x := v.LastDistinct; x != nil { + ch := n.Child("last_distinct") + stringsMlLastDistinctFunction(c, &ch, x) + } + } + if !n.IsRef("last_n") { + if x := v.LastN; x != nil { + ch := n.Child("last_n") + stringsMlLastNFunction(c, &ch, x) + } + } + if !n.IsRef("max") { + if x := v.Max; x != nil { + ch := n.Child("max") + stringsMlMaxFunction(c, &ch, x) + } + } + if !n.IsRef("min") { + if x := v.Min; x != nil { + ch := n.Child("min") + stringsMlMinFunction(c, &ch, x) + } + } + if !n.IsRef("stddev_pop") { + if x := v.StddevPop; x != nil { + ch := n.Child("stddev_pop") + stringsMlStddevPopFunction(c, &ch, x) + } + } + if !n.IsRef("stddev_samp") { + if x := v.StddevSamp; x != nil { + ch := n.Child("stddev_samp") + stringsMlStddevSampFunction(c, &ch, x) + } + } + if !n.IsRef("sum") { + if x := v.Sum; x != nil { + ch := n.Child("sum") + stringsMlSumFunction(c, &ch, x) + } + } + if !n.IsRef("time_window") { + if x := v.TimeWindow; x != nil { + ch := n.Child("time_window") + stringsMlTimeWindow(c, &ch, x) + } + } + if !n.IsRef("var_pop") { + if x := v.VarPop; x != nil { + ch := n.Child("var_pop") + stringsMlVarPopFunction(c, &ch, x) + } + } + if !n.IsRef("var_samp") { + if x := v.VarSamp; x != nil { + ch := n.Child("var_samp") + stringsMlVarSampFunction(c, &ch, x) + } + } +} + +func stringsMlColumnSelection(c *stringsWalk, n *Node, v *ml.ColumnSelection) { + if n.isRef() { + return + } + if s := string(v.Column); c.match(s) { + ch := n.Child("column") + if r, ok := c.fn(&ch, s); ok { + v.Column = (r) + } + } +} + +func stringsMlCustomUdf(c *stringsWalk, n *Node, v *ml.CustomUdf) { + if n.isRef() { + return + } + if s := string(v.FunctionPath); c.match(s) { + ch := n.Child("function_path") + if r, ok := c.fn(&ch, s); ok { + v.FunctionPath = (r) + } + } + if !n.IsRef("input_bindings") { + if len(v.InputBindings) > 0 { + cn := n.Child("input_bindings") + for i := range v.InputBindings { + { + ch := cn.Elem(i) + stringsMlInputBinding(c, &ch, &v.InputBindings[i]) + } + } + } + } +} + +func stringsMlFunctionExtraParameter(c *stringsWalk, n *Node, v *ml.FunctionExtraParameter) { + if n.isRef() { + return + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsMlDeltaTableSource(c *stringsWalk, n *Node, v *ml.DeltaTableSource) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DataframeSchema == "" && Forced(v.ForceSendFields, "DataframeSchema") { + ch := n.Child("dataframe_schema") + c.fn(&ch, "") + } + if s := string(v.DataframeSchema); c.match(s) { + ch := n.Child("dataframe_schema") + if r, ok := c.fn(&ch, s); ok { + v.DataframeSchema = (r) + } + } + if !n.IsRef("entity_columns") { + if len(v.EntityColumns) > 0 { + cn := n.Child("entity_columns") + for i := range v.EntityColumns { + if s := string(v.EntityColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.EntityColumns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.FilterCondition == "" && Forced(v.ForceSendFields, "FilterCondition") { + ch := n.Child("filter_condition") + c.fn(&ch, "") + } + if s := string(v.FilterCondition); c.match(s) { + ch := n.Child("filter_condition") + if r, ok := c.fn(&ch, s); ok { + v.FilterCondition = (r) + } + } + if s := string(v.FullName); c.match(s) { + ch := n.Child("full_name") + if r, ok := c.fn(&ch, s); ok { + v.FullName = (r) + } + } + if c.mode == EmptyFields && v.TimeseriesColumn == "" && Forced(v.ForceSendFields, "TimeseriesColumn") { + ch := n.Child("timeseries_column") + c.fn(&ch, "") + } + if s := string(v.TimeseriesColumn); c.match(s) { + ch := n.Child("timeseries_column") + if r, ok := c.fn(&ch, s); ok { + v.TimeseriesColumn = (r) + } + } + if c.mode == EmptyFields && v.TransformationSql == "" && Forced(v.ForceSendFields, "TransformationSql") { + ch := n.Child("transformation_sql") + c.fn(&ch, "") + } + if s := string(v.TransformationSql); c.match(s) { + ch := n.Child("transformation_sql") + if r, ok := c.fn(&ch, s); ok { + v.TransformationSql = (r) + } + } +} + +func stringsMlFeatureViewSource(c *stringsWalk, n *Node, v *ml.FeatureViewSource) { + if n.isRef() { + return + } + if !n.IsRef("feature_references") { + if len(v.FeatureReferences) > 0 { + cn := n.Child("feature_references") + for i := range v.FeatureReferences { + { + ch := cn.Elem(i) + stringsMlFeatureReference(c, &ch, &v.FeatureReferences[i]) + } + } + } + } +} + +func stringsMlKafkaSource(c *stringsWalk, n *Node, v *ml.KafkaSource) { + if n.isRef() { + return + } + if !n.IsRef("entity_column_identifiers") { + if len(v.EntityColumnIdentifiers) > 0 { + cn := n.Child("entity_column_identifiers") + for i := range v.EntityColumnIdentifiers { + { + ch := cn.Elem(i) + stringsMlColumnIdentifier(c, &ch, &v.EntityColumnIdentifiers[i]) + } + } + } + } + if c.mode == EmptyFields && v.FilterCondition == "" && Forced(v.ForceSendFields, "FilterCondition") { + ch := n.Child("filter_condition") + c.fn(&ch, "") + } + if s := string(v.FilterCondition); c.match(s) { + ch := n.Child("filter_condition") + if r, ok := c.fn(&ch, s); ok { + v.FilterCondition = (r) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if !n.IsRef("timeseries_column_identifier") { + if x := v.TimeseriesColumnIdentifier; x != nil { + ch := n.Child("timeseries_column_identifier") + stringsMlColumnIdentifier(c, &ch, x) + } + } +} + +func stringsMlRequestSource(c *stringsWalk, n *Node, v *ml.RequestSource) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DataframeSchema == "" && Forced(v.ForceSendFields, "DataframeSchema") { + ch := n.Child("dataframe_schema") + c.fn(&ch, "") + } + if s := string(v.DataframeSchema); c.match(s) { + ch := n.Child("dataframe_schema") + if r, ok := c.fn(&ch, s); ok { + v.DataframeSchema = (r) + } + } + if !n.IsRef("flat_schema") { + if x := v.FlatSchema; x != nil { + ch := n.Child("flat_schema") + stringsMlFlatSchema(c, &ch, x) + } + } +} + +func stringsMlSdpSource(c *stringsWalk, n *Node, v *ml.SdpSource) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DataframeSchema == "" && Forced(v.ForceSendFields, "DataframeSchema") { + ch := n.Child("dataframe_schema") + c.fn(&ch, "") + } + if s := string(v.DataframeSchema); c.match(s) { + ch := n.Child("dataframe_schema") + if r, ok := c.fn(&ch, s); ok { + v.DataframeSchema = (r) + } + } + if c.mode == EmptyFields && v.FilterCondition == "" && Forced(v.ForceSendFields, "FilterCondition") { + ch := n.Child("filter_condition") + c.fn(&ch, "") + } + if s := string(v.FilterCondition); c.match(s) { + ch := n.Child("filter_condition") + if r, ok := c.fn(&ch, s); ok { + v.FilterCondition = (r) + } + } + if !n.IsRef("input_tables") { + if len(v.InputTables) > 0 { + cn := n.Child("input_tables") + for i := range v.InputTables { + if s := string(v.InputTables[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.InputTables[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.OutputTable == "" && Forced(v.ForceSendFields, "OutputTable") { + ch := n.Child("output_table") + c.fn(&ch, "") + } + if s := string(v.OutputTable); c.match(s) { + ch := n.Child("output_table") + if r, ok := c.fn(&ch, s); ok { + v.OutputTable = (r) + } + } + if c.mode == EmptyFields && v.PipelineId == "" && Forced(v.ForceSendFields, "PipelineId") { + ch := n.Child("pipeline_id") + c.fn(&ch, "") + } + if s := string(v.PipelineId); c.match(s) { + ch := n.Child("pipeline_id") + if r, ok := c.fn(&ch, s); ok { + v.PipelineId = (r) + } + } + if c.mode == EmptyFields && v.PipelineState == "" && Forced(v.ForceSendFields, "PipelineState") { + ch := n.Child("pipeline_state") + c.fn(&ch, "") + } + if s := string(v.PipelineState); c.match(s) { + ch := n.Child("pipeline_state") + if r, ok := c.fn(&ch, s); ok { + v.PipelineState = ml.SdpSourceState(r) + } + } + if c.mode == EmptyFields && v.TransformationSql == "" && Forced(v.ForceSendFields, "TransformationSql") { + ch := n.Child("transformation_sql") + c.fn(&ch, "") + } + if s := string(v.TransformationSql); c.match(s) { + ch := n.Child("transformation_sql") + if r, ok := c.fn(&ch, s); ok { + v.TransformationSql = (r) + } + } +} + +func stringsMlStreamSource(c *stringsWalk, n *Node, v *ml.StreamSource) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DataframeSchema == "" && Forced(v.ForceSendFields, "DataframeSchema") { + ch := n.Child("dataframe_schema") + c.fn(&ch, "") + } + if s := string(v.DataframeSchema); c.match(s) { + ch := n.Child("dataframe_schema") + if r, ok := c.fn(&ch, s); ok { + v.DataframeSchema = (r) + } + } + if c.mode == EmptyFields && v.FilterCondition == "" && Forced(v.ForceSendFields, "FilterCondition") { + ch := n.Child("filter_condition") + c.fn(&ch, "") + } + if s := string(v.FilterCondition); c.match(s) { + ch := n.Child("filter_condition") + if r, ok := c.fn(&ch, s); ok { + v.FilterCondition = (r) + } + } + if s := string(v.FullName); c.match(s) { + ch := n.Child("full_name") + if r, ok := c.fn(&ch, s); ok { + v.FullName = (r) + } + } + if c.mode == EmptyFields && v.TransformationSql == "" && Forced(v.ForceSendFields, "TransformationSql") { + ch := n.Child("transformation_sql") + c.fn(&ch, "") + } + if s := string(v.TransformationSql); c.match(s) { + ch := n.Child("transformation_sql") + if r, ok := c.fn(&ch, s); ok { + v.TransformationSql = (r) + } + } +} + +func stringsMlContinuousWindow(c *stringsWalk, n *Node, v *ml.ContinuousWindow) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Offset == "" && Forced(v.ForceSendFields, "Offset") { + ch := n.Child("offset") + c.fn(&ch, "") + } + if s := string(v.Offset); c.match(s) { + ch := n.Child("offset") + if r, ok := c.fn(&ch, s); ok { + v.Offset = (r) + } + } + if s := string(v.WindowDuration); c.match(s) { + ch := n.Child("window_duration") + if r, ok := c.fn(&ch, s); ok { + v.WindowDuration = (r) + } + } +} + +func stringsMlSlidingWindow(c *stringsWalk, n *Node, v *ml.SlidingWindow) { + if n.isRef() { + return + } + if s := string(v.SlideDuration); c.match(s) { + ch := n.Child("slide_duration") + if r, ok := c.fn(&ch, s); ok { + v.SlideDuration = (r) + } + } + if c.mode == EmptyFields && v.WindowDuration == "" && Forced(v.ForceSendFields, "WindowDuration") { + ch := n.Child("window_duration") + c.fn(&ch, "") + } + if s := string(v.WindowDuration); c.match(s) { + ch := n.Child("window_duration") + if r, ok := c.fn(&ch, s); ok { + v.WindowDuration = (r) + } + } +} + +func stringsMlTumblingWindow(c *stringsWalk, n *Node, v *ml.TumblingWindow) { + if n.isRef() { + return + } + if s := string(v.WindowDuration); c.match(s) { + ch := n.Child("window_duration") + if r, ok := c.fn(&ch, s); ok { + v.WindowDuration = (r) + } + } +} + +func stringsMlUcTraceLocation(c *stringsWalk, n *Node, v *ml.UcTraceLocation) { + if n.isRef() { + return + } + if s := string(v.Catalog); c.match(s) { + ch := n.Child("catalog") + if r, ok := c.fn(&ch, s); ok { + v.Catalog = (r) + } + } + if c.mode == EmptyFields && v.EffectiveTablePrefix == "" && Forced(v.ForceSendFields, "EffectiveTablePrefix") { + ch := n.Child("effective_table_prefix") + c.fn(&ch, "") + } + if s := string(v.EffectiveTablePrefix); c.match(s) { + ch := n.Child("effective_table_prefix") + if r, ok := c.fn(&ch, s); ok { + v.EffectiveTablePrefix = (r) + } + } + if s := string(v.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.Schema = (r) + } + } + if c.mode == EmptyFields && v.TablePrefix == "" && Forced(v.ForceSendFields, "TablePrefix") { + ch := n.Child("table_prefix") + c.fn(&ch, "") + } + if s := string(v.TablePrefix); c.match(s) { + ch := n.Child("table_prefix") + if r, ok := c.fn(&ch, s); ok { + v.TablePrefix = (r) + } + } +} + +func stringsServingAiGatewayGuardrails(c *stringsWalk, n *Node, v *serving.AiGatewayGuardrails) { + if n.isRef() { + return + } + if !n.IsRef("input") { + if x := v.Input; x != nil { + ch := n.Child("input") + stringsServingAiGatewayGuardrailParameters(c, &ch, x) + } + } + if !n.IsRef("output") { + if x := v.Output; x != nil { + ch := n.Child("output") + stringsServingAiGatewayGuardrailParameters(c, &ch, x) + } + } +} + +func stringsServingAiGatewayInferenceTableConfig(c *stringsWalk, n *Node, v *serving.AiGatewayInferenceTableConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CatalogName == "" && Forced(v.ForceSendFields, "CatalogName") { + ch := n.Child("catalog_name") + c.fn(&ch, "") + } + if s := string(v.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.SchemaName == "" && Forced(v.ForceSendFields, "SchemaName") { + ch := n.Child("schema_name") + c.fn(&ch, "") + } + if s := string(v.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.SchemaName = (r) + } + } + if c.mode == EmptyFields && v.TableNamePrefix == "" && Forced(v.ForceSendFields, "TableNamePrefix") { + ch := n.Child("table_name_prefix") + c.fn(&ch, "") + } + if s := string(v.TableNamePrefix); c.match(s) { + ch := n.Child("table_name_prefix") + if r, ok := c.fn(&ch, s); ok { + v.TableNamePrefix = (r) + } + } +} + +func stringsServingAiGatewayRateLimit(c *stringsWalk, n *Node, v *serving.AiGatewayRateLimit) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = serving.AiGatewayRateLimitKey(r) + } + } + if c.mode == EmptyFields && v.Principal == "" && Forced(v.ForceSendFields, "Principal") { + ch := n.Child("principal") + c.fn(&ch, "") + } + if s := string(v.Principal); c.match(s) { + ch := n.Child("principal") + if r, ok := c.fn(&ch, s); ok { + v.Principal = (r) + } + } + if s := string(v.RenewalPeriod); c.match(s) { + ch := n.Child("renewal_period") + if r, ok := c.fn(&ch, s); ok { + v.RenewalPeriod = serving.AiGatewayRateLimitRenewalPeriod(r) + } + } +} + +func stringsServingAutoCaptureConfigInput(c *stringsWalk, n *Node, v *serving.AutoCaptureConfigInput) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CatalogName == "" && Forced(v.ForceSendFields, "CatalogName") { + ch := n.Child("catalog_name") + c.fn(&ch, "") + } + if s := string(v.CatalogName); c.match(s) { + ch := n.Child("catalog_name") + if r, ok := c.fn(&ch, s); ok { + v.CatalogName = (r) + } + } + if c.mode == EmptyFields && v.SchemaName == "" && Forced(v.ForceSendFields, "SchemaName") { + ch := n.Child("schema_name") + c.fn(&ch, "") + } + if s := string(v.SchemaName); c.match(s) { + ch := n.Child("schema_name") + if r, ok := c.fn(&ch, s); ok { + v.SchemaName = (r) + } + } + if c.mode == EmptyFields && v.TableNamePrefix == "" && Forced(v.ForceSendFields, "TableNamePrefix") { + ch := n.Child("table_name_prefix") + c.fn(&ch, "") + } + if s := string(v.TableNamePrefix); c.match(s) { + ch := n.Child("table_name_prefix") + if r, ok := c.fn(&ch, s); ok { + v.TableNamePrefix = (r) + } + } +} + +func stringsServingServedEntityInput(c *stringsWalk, n *Node, v *serving.ServedEntityInput) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.EntityName == "" && Forced(v.ForceSendFields, "EntityName") { + ch := n.Child("entity_name") + c.fn(&ch, "") + } + if s := string(v.EntityName); c.match(s) { + ch := n.Child("entity_name") + if r, ok := c.fn(&ch, s); ok { + v.EntityName = (r) + } + } + if c.mode == EmptyFields && v.EntityVersion == "" && Forced(v.ForceSendFields, "EntityVersion") { + ch := n.Child("entity_version") + c.fn(&ch, "") + } + if s := string(v.EntityVersion); c.match(s) { + ch := n.Child("entity_version") + if r, ok := c.fn(&ch, s); ok { + v.EntityVersion = (r) + } + } + if !n.IsRef("environment_vars") { + if len(v.EnvironmentVars) > 0 { + cn := n.Child("environment_vars") + for _, k := range sortedKeys(v.EnvironmentVars) { + if s := string(v.EnvironmentVars[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentVars[k] = (r) + } + } + } + } + } + if !n.IsRef("external_model") { + if x := v.ExternalModel; x != nil { + ch := n.Child("external_model") + stringsServingExternalModel(c, &ch, x) + } + } + if c.mode == EmptyFields && v.InstanceProfileArn == "" && Forced(v.ForceSendFields, "InstanceProfileArn") { + ch := n.Child("instance_profile_arn") + c.fn(&ch, "") + } + if s := string(v.InstanceProfileArn); c.match(s) { + ch := n.Child("instance_profile_arn") + if r, ok := c.fn(&ch, s); ok { + v.InstanceProfileArn = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.WorkloadSize == "" && Forced(v.ForceSendFields, "WorkloadSize") { + ch := n.Child("workload_size") + c.fn(&ch, "") + } + if s := string(v.WorkloadSize); c.match(s) { + ch := n.Child("workload_size") + if r, ok := c.fn(&ch, s); ok { + v.WorkloadSize = (r) + } + } + if c.mode == EmptyFields && v.WorkloadType == "" && Forced(v.ForceSendFields, "WorkloadType") { + ch := n.Child("workload_type") + c.fn(&ch, "") + } + if s := string(v.WorkloadType); c.match(s) { + ch := n.Child("workload_type") + if r, ok := c.fn(&ch, s); ok { + v.WorkloadType = serving.ServingModelWorkloadType(r) + } + } +} + +func stringsServingServedModelInput(c *stringsWalk, n *Node, v *serving.ServedModelInput) { + if n.isRef() { + return + } + if !n.IsRef("environment_vars") { + if len(v.EnvironmentVars) > 0 { + cn := n.Child("environment_vars") + for _, k := range sortedKeys(v.EnvironmentVars) { + if s := string(v.EnvironmentVars[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.EnvironmentVars[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.InstanceProfileArn == "" && Forced(v.ForceSendFields, "InstanceProfileArn") { + ch := n.Child("instance_profile_arn") + c.fn(&ch, "") + } + if s := string(v.InstanceProfileArn); c.match(s) { + ch := n.Child("instance_profile_arn") + if r, ok := c.fn(&ch, s); ok { + v.InstanceProfileArn = (r) + } + } + if s := string(v.ModelName); c.match(s) { + ch := n.Child("model_name") + if r, ok := c.fn(&ch, s); ok { + v.ModelName = (r) + } + } + if s := string(v.ModelVersion); c.match(s) { + ch := n.Child("model_version") + if r, ok := c.fn(&ch, s); ok { + v.ModelVersion = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.WorkloadSize == "" && Forced(v.ForceSendFields, "WorkloadSize") { + ch := n.Child("workload_size") + c.fn(&ch, "") + } + if s := string(v.WorkloadSize); c.match(s) { + ch := n.Child("workload_size") + if r, ok := c.fn(&ch, s); ok { + v.WorkloadSize = (r) + } + } + if c.mode == EmptyFields && v.WorkloadType == "" && Forced(v.ForceSendFields, "WorkloadType") { + ch := n.Child("workload_type") + c.fn(&ch, "") + } + if s := string(v.WorkloadType); c.match(s) { + ch := n.Child("workload_type") + if r, ok := c.fn(&ch, s); ok { + v.WorkloadType = serving.ServedModelInputWorkloadType(r) + } + } +} + +func stringsServingTrafficConfig(c *stringsWalk, n *Node, v *serving.TrafficConfig) { + if n.isRef() { + return + } + if !n.IsRef("routes") { + if len(v.Routes) > 0 { + cn := n.Child("routes") + for i := range v.Routes { + { + ch := cn.Elem(i) + stringsServingRoute(c, &ch, &v.Routes[i]) + } + } + } + } +} + +func stringsServingTelemetryInferenceTableConfig(c *stringsWalk, n *Node, v *serving.TelemetryInferenceTableConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsServingUnityCatalogTableNames(c *stringsWalk, n *Node, v *serving.UnityCatalogTableNames) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AnnotationsTable == "" && Forced(v.ForceSendFields, "AnnotationsTable") { + ch := n.Child("annotations_table") + c.fn(&ch, "") + } + if s := string(v.AnnotationsTable); c.match(s) { + ch := n.Child("annotations_table") + if r, ok := c.fn(&ch, s); ok { + v.AnnotationsTable = (r) + } + } + if c.mode == EmptyFields && v.LogsTable == "" && Forced(v.ForceSendFields, "LogsTable") { + ch := n.Child("logs_table") + c.fn(&ch, "") + } + if s := string(v.LogsTable); c.match(s) { + ch := n.Child("logs_table") + if r, ok := c.fn(&ch, s); ok { + v.LogsTable = (r) + } + } + if c.mode == EmptyFields && v.MetricsTable == "" && Forced(v.ForceSendFields, "MetricsTable") { + ch := n.Child("metrics_table") + c.fn(&ch, "") + } + if s := string(v.MetricsTable); c.match(s) { + ch := n.Child("metrics_table") + if r, ok := c.fn(&ch, s); ok { + v.MetricsTable = (r) + } + } + if c.mode == EmptyFields && v.TracesTable == "" && Forced(v.ForceSendFields, "TracesTable") { + ch := n.Child("traces_table") + c.fn(&ch, "") + } + if s := string(v.TracesTable); c.match(s) { + ch := n.Child("traces_table") + if r, ok := c.fn(&ch, s); ok { + v.TracesTable = (r) + } + } +} + +func stringsCatalogInferenceTableConfig(c *stringsWalk, n *Node, v *catalog.InferenceTableConfig) { + if n.isRef() { + return + } + if s := string(v.Parent); c.match(s) { + ch := n.Child("parent") + if r, ok := c.fn(&ch, s); ok { + v.Parent = (r) + } + } + if c.mode == EmptyFields && v.Table == "" && Forced(v.ForceSendFields, "Table") { + ch := n.Child("table") + c.fn(&ch, "") + } + if s := string(v.Table); c.match(s) { + ch := n.Child("table") + if r, ok := c.fn(&ch, s); ok { + v.Table = (r) + } + } + if c.mode == EmptyFields && v.TableNamePrefix == "" && Forced(v.ForceSendFields, "TableNamePrefix") { + ch := n.Child("table_name_prefix") + c.fn(&ch, "") + } + if s := string(v.TableNamePrefix); c.match(s) { + ch := n.Child("table_name_prefix") + if r, ok := c.fn(&ch, s); ok { + v.TableNamePrefix = (r) + } + } +} + +func stringsCatalogRateLimit(c *stringsWalk, n *Node, v *catalog.RateLimit) { + if n.isRef() { + return + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = catalog.RateLimitRateLimitKey(r) + } + } + if c.mode == EmptyFields && v.Principal == "" && Forced(v.ForceSendFields, "Principal") { + ch := n.Child("principal") + c.fn(&ch, "") + } + if s := string(v.Principal); c.match(s) { + ch := n.Child("principal") + if r, ok := c.fn(&ch, s); ok { + v.Principal = (r) + } + } + if s := string(v.RenewalPeriod); c.match(s) { + ch := n.Child("renewal_period") + if r, ok := c.fn(&ch, s); ok { + v.RenewalPeriod = catalog.RateLimitRateLimitRenewalPeriod(r) + } + } +} + +func stringsCatalogModelServiceConfigRoutingConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfigRoutingConfig) { + if n.isRef() { + return + } + if !n.IsRef("destinations") { + if len(v.Destinations) > 0 { + cn := n.Child("destinations") + for i := range v.Destinations { + { + ch := cn.Elem(i) + stringsCatalogModelServiceConfigDestinationConfig(c, &ch, &v.Destinations[i]) + } + } + } + } + if !n.IsRef("fallback") { + if x := v.Fallback; x != nil { + ch := n.Child("fallback") + stringsCatalogModelServiceConfigFallbackConfig(c, &ch, x) + } + } +} + +func stringsCatalogMcpServiceConfigSourceConnection(c *stringsWalk, n *Node, v *catalog.McpServiceConfigSourceConnection) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if !n.IsRef("options") { + if len(v.Options) > 0 { + cn := n.Child("options") + for _, k := range sortedKeys(v.Options) { + if s := string(v.Options[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Options[k] = (r) + } + } + } + } + } +} + +func stringsCatalogModelProviderServiceConfigAmazonBedrockProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAmazonBedrockProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigAnthropicProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAnthropicProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigAzureOpenAiProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAzureOpenAiProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigCustomProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigCustomProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigCustomProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigGeminiEnterpriseProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigMicrosoftFoundryProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigOpenAiProviderConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigOpenAiProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("direct") { + if x := v.Direct; x != nil { + ch := n.Child("direct") + stringsCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigModelTargetConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigModelTargetConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.EndpointRoute == "" && Forced(v.ForceSendFields, "EndpointRoute") { + ch := n.Child("endpoint_route") + c.fn(&ch, "") + } + if s := string(v.EndpointRoute); c.match(s) { + ch := n.Child("endpoint_route") + if r, ok := c.fn(&ch, s); ok { + v.EndpointRoute = (r) + } + } + if s := string(v.Model); c.match(s) { + ch := n.Child("model") + if r, ok := c.fn(&ch, s); ok { + v.Model = (r) + } + } + if !n.IsRef("native_api_types") { + if len(v.NativeApiTypes) > 0 { + cn := n.Child("native_api_types") + for i := range v.NativeApiTypes { + if s := string(v.NativeApiTypes[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.NativeApiTypes[i] = (r) + } + } + } + } + } +} + +func stringsCatalogMonitorDestination(c *stringsWalk, n *Node, v *catalog.MonitorDestination) { + if n.isRef() { + return + } + if !n.IsRef("email_addresses") { + if len(v.EmailAddresses) > 0 { + cn := n.Child("email_addresses") + for i := range v.EmailAddresses { + if s := string(v.EmailAddresses[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.EmailAddresses[i] = (r) + } + } + } + } + } +} + +func stringsCatalogAzureEncryptionSettings(c *stringsWalk, n *Node, v *catalog.AzureEncryptionSettings) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AzureCmkAccessConnectorId == "" && Forced(v.ForceSendFields, "AzureCmkAccessConnectorId") { + ch := n.Child("azure_cmk_access_connector_id") + c.fn(&ch, "") + } + if s := string(v.AzureCmkAccessConnectorId); c.match(s) { + ch := n.Child("azure_cmk_access_connector_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureCmkAccessConnectorId = (r) + } + } + if c.mode == EmptyFields && v.AzureCmkManagedIdentityId == "" && Forced(v.ForceSendFields, "AzureCmkManagedIdentityId") { + ch := n.Child("azure_cmk_managed_identity_id") + c.fn(&ch, "") + } + if s := string(v.AzureCmkManagedIdentityId); c.match(s) { + ch := n.Child("azure_cmk_managed_identity_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureCmkManagedIdentityId = (r) + } + } + if s := string(v.AzureTenantId); c.match(s) { + ch := n.Child("azure_tenant_id") + if r, ok := c.fn(&ch, s); ok { + v.AzureTenantId = (r) + } + } +} + +func stringsCatalogAzureQueueStorage(c *stringsWalk, n *Node, v *catalog.AzureQueueStorage) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ManagedResourceId == "" && Forced(v.ForceSendFields, "ManagedResourceId") { + ch := n.Child("managed_resource_id") + c.fn(&ch, "") + } + if s := string(v.ManagedResourceId); c.match(s) { + ch := n.Child("managed_resource_id") + if r, ok := c.fn(&ch, s); ok { + v.ManagedResourceId = (r) + } + } + if c.mode == EmptyFields && v.QueueUrl == "" && Forced(v.ForceSendFields, "QueueUrl") { + ch := n.Child("queue_url") + c.fn(&ch, "") + } + if s := string(v.QueueUrl); c.match(s) { + ch := n.Child("queue_url") + if r, ok := c.fn(&ch, s); ok { + v.QueueUrl = (r) + } + } + if c.mode == EmptyFields && v.ResourceGroup == "" && Forced(v.ForceSendFields, "ResourceGroup") { + ch := n.Child("resource_group") + c.fn(&ch, "") + } + if s := string(v.ResourceGroup); c.match(s) { + ch := n.Child("resource_group") + if r, ok := c.fn(&ch, s); ok { + v.ResourceGroup = (r) + } + } + if c.mode == EmptyFields && v.SubscriptionId == "" && Forced(v.ForceSendFields, "SubscriptionId") { + ch := n.Child("subscription_id") + c.fn(&ch, "") + } + if s := string(v.SubscriptionId); c.match(s) { + ch := n.Child("subscription_id") + if r, ok := c.fn(&ch, s); ok { + v.SubscriptionId = (r) + } + } +} + +func stringsCatalogGcpPubsub(c *stringsWalk, n *Node, v *catalog.GcpPubsub) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ManagedResourceId == "" && Forced(v.ForceSendFields, "ManagedResourceId") { + ch := n.Child("managed_resource_id") + c.fn(&ch, "") + } + if s := string(v.ManagedResourceId); c.match(s) { + ch := n.Child("managed_resource_id") + if r, ok := c.fn(&ch, s); ok { + v.ManagedResourceId = (r) + } + } + if c.mode == EmptyFields && v.SubscriptionName == "" && Forced(v.ForceSendFields, "SubscriptionName") { + ch := n.Child("subscription_name") + c.fn(&ch, "") + } + if s := string(v.SubscriptionName); c.match(s) { + ch := n.Child("subscription_name") + if r, ok := c.fn(&ch, s); ok { + v.SubscriptionName = (r) + } + } +} + +func stringsCatalogAwsSqsQueue(c *stringsWalk, n *Node, v *catalog.AwsSqsQueue) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ManagedResourceId == "" && Forced(v.ForceSendFields, "ManagedResourceId") { + ch := n.Child("managed_resource_id") + c.fn(&ch, "") + } + if s := string(v.ManagedResourceId); c.match(s) { + ch := n.Child("managed_resource_id") + if r, ok := c.fn(&ch, s); ok { + v.ManagedResourceId = (r) + } + } + if c.mode == EmptyFields && v.QueueUrl == "" && Forced(v.ForceSendFields, "QueueUrl") { + ch := n.Child("queue_url") + c.fn(&ch, "") + } + if s := string(v.QueueUrl); c.match(s) { + ch := n.Child("queue_url") + if r, ok := c.fn(&ch, s); ok { + v.QueueUrl = (r) + } + } +} + +func stringsCatalogSseEncryptionDetails(c *stringsWalk, n *Node, v *catalog.SseEncryptionDetails) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Algorithm == "" && Forced(v.ForceSendFields, "Algorithm") { + ch := n.Child("algorithm") + c.fn(&ch, "") + } + if s := string(v.Algorithm); c.match(s) { + ch := n.Child("algorithm") + if r, ok := c.fn(&ch, s); ok { + v.Algorithm = catalog.SseEncryptionDetailsAlgorithm(r) + } + } + if c.mode == EmptyFields && v.AwsKmsKeyArn == "" && Forced(v.ForceSendFields, "AwsKmsKeyArn") { + ch := n.Child("aws_kms_key_arn") + c.fn(&ch, "") + } + if s := string(v.AwsKmsKeyArn); c.match(s) { + ch := n.Child("aws_kms_key_arn") + if r, ok := c.fn(&ch, s); ok { + v.AwsKmsKeyArn = (r) + } + } +} + +func stringsComputeRCranLibrary(c *stringsWalk, n *Node, v *compute.RCranLibrary) { + if n.isRef() { + return + } + if s := string(v.Package); c.match(s) { + ch := n.Child("package") + if r, ok := c.fn(&ch, s); ok { + v.Package = (r) + } + } + if c.mode == EmptyFields && v.Repo == "" && Forced(v.ForceSendFields, "Repo") { + ch := n.Child("repo") + c.fn(&ch, "") + } + if s := string(v.Repo); c.match(s) { + ch := n.Child("repo") + if r, ok := c.fn(&ch, s); ok { + v.Repo = (r) + } + } +} + +func stringsComputePythonPyPiLibrary(c *stringsWalk, n *Node, v *compute.PythonPyPiLibrary) { + if n.isRef() { + return + } + if s := string(v.Package); c.match(s) { + ch := n.Child("package") + if r, ok := c.fn(&ch, s); ok { + v.Package = (r) + } + } + if c.mode == EmptyFields && v.Repo == "" && Forced(v.ForceSendFields, "Repo") { + ch := n.Child("repo") + c.fn(&ch, "") + } + if s := string(v.Repo); c.match(s) { + ch := n.Child("repo") + if r, ok := c.fn(&ch, s); ok { + v.Repo = (r) + } + } +} + +func stringsComputeLogAnalyticsInfo(c *stringsWalk, n *Node, v *compute.LogAnalyticsInfo) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.LogAnalyticsPrimaryKey == "" && Forced(v.ForceSendFields, "LogAnalyticsPrimaryKey") { + ch := n.Child("log_analytics_primary_key") + c.fn(&ch, "") + } + if s := string(v.LogAnalyticsPrimaryKey); c.match(s) { + ch := n.Child("log_analytics_primary_key") + if r, ok := c.fn(&ch, s); ok { + v.LogAnalyticsPrimaryKey = (r) + } + } + if c.mode == EmptyFields && v.LogAnalyticsWorkspaceId == "" && Forced(v.ForceSendFields, "LogAnalyticsWorkspaceId") { + ch := n.Child("log_analytics_workspace_id") + c.fn(&ch, "") + } + if s := string(v.LogAnalyticsWorkspaceId); c.match(s) { + ch := n.Child("log_analytics_workspace_id") + if r, ok := c.fn(&ch, s); ok { + v.LogAnalyticsWorkspaceId = (r) + } + } +} + +func stringsComputeDbfsStorageInfo(c *stringsWalk, n *Node, v *compute.DbfsStorageInfo) { + if n.isRef() { + return + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } +} + +func stringsComputeS3StorageInfo(c *stringsWalk, n *Node, v *compute.S3StorageInfo) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CannedAcl == "" && Forced(v.ForceSendFields, "CannedAcl") { + ch := n.Child("canned_acl") + c.fn(&ch, "") + } + if s := string(v.CannedAcl); c.match(s) { + ch := n.Child("canned_acl") + if r, ok := c.fn(&ch, s); ok { + v.CannedAcl = (r) + } + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } + if c.mode == EmptyFields && v.EncryptionType == "" && Forced(v.ForceSendFields, "EncryptionType") { + ch := n.Child("encryption_type") + c.fn(&ch, "") + } + if s := string(v.EncryptionType); c.match(s) { + ch := n.Child("encryption_type") + if r, ok := c.fn(&ch, s); ok { + v.EncryptionType = (r) + } + } + if c.mode == EmptyFields && v.Endpoint == "" && Forced(v.ForceSendFields, "Endpoint") { + ch := n.Child("endpoint") + c.fn(&ch, "") + } + if s := string(v.Endpoint); c.match(s) { + ch := n.Child("endpoint") + if r, ok := c.fn(&ch, s); ok { + v.Endpoint = (r) + } + } + if c.mode == EmptyFields && v.KmsKey == "" && Forced(v.ForceSendFields, "KmsKey") { + ch := n.Child("kms_key") + c.fn(&ch, "") + } + if s := string(v.KmsKey); c.match(s) { + ch := n.Child("kms_key") + if r, ok := c.fn(&ch, s); ok { + v.KmsKey = (r) + } + } + if c.mode == EmptyFields && v.Region == "" && Forced(v.ForceSendFields, "Region") { + ch := n.Child("region") + c.fn(&ch, "") + } + if s := string(v.Region); c.match(s) { + ch := n.Child("region") + if r, ok := c.fn(&ch, s); ok { + v.Region = (r) + } + } +} + +func stringsComputeVolumesStorageInfo(c *stringsWalk, n *Node, v *compute.VolumesStorageInfo) { + if n.isRef() { + return + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } +} + +func stringsComputeDockerBasicAuth(c *stringsWalk, n *Node, v *compute.DockerBasicAuth) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Password == "" && Forced(v.ForceSendFields, "Password") { + ch := n.Child("password") + c.fn(&ch, "") + } + if s := string(v.Password); c.match(s) { + ch := n.Child("password") + if r, ok := c.fn(&ch, s); ok { + v.Password = (r) + } + } + if c.mode == EmptyFields && v.Username == "" && Forced(v.ForceSendFields, "Username") { + ch := n.Child("username") + c.fn(&ch, "") + } + if s := string(v.Username); c.match(s) { + ch := n.Child("username") + if r, ok := c.fn(&ch, s); ok { + v.Username = (r) + } + } +} + +func stringsComputeAdlsgen2Info(c *stringsWalk, n *Node, v *compute.Adlsgen2Info) { + if n.isRef() { + return + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } +} + +func stringsComputeLocalFileInfo(c *stringsWalk, n *Node, v *compute.LocalFileInfo) { + if n.isRef() { + return + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } +} + +func stringsComputeGcsStorageInfo(c *stringsWalk, n *Node, v *compute.GcsStorageInfo) { + if n.isRef() { + return + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } +} + +func stringsComputeWorkspaceStorageInfo(c *stringsWalk, n *Node, v *compute.WorkspaceStorageInfo) { + if n.isRef() { + return + } + if s := string(v.Destination); c.match(s) { + ch := n.Child("destination") + if r, ok := c.fn(&ch, s); ok { + v.Destination = (r) + } + } +} + +func stringsResourcesAppEnvVar(c *stringsWalk, n *Node, v *resources.AppEnvVar) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } + if s := string(v.ValueFrom); c.match(s) { + ch := n.Child("value_from") + if r, ok := c.fn(&ch, s); ok { + v.ValueFrom = (r) + } + } +} + +func stringsAppsAppDeploymentArtifacts(c *stringsWalk, n *Node, v *apps.AppDeploymentArtifacts) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.SourceCodePath == "" && Forced(v.ForceSendFields, "SourceCodePath") { + ch := n.Child("source_code_path") + c.fn(&ch, "") + } + if s := string(v.SourceCodePath); c.match(s) { + ch := n.Child("source_code_path") + if r, ok := c.fn(&ch, s); ok { + v.SourceCodePath = (r) + } + } +} + +func stringsAppsEnvVar(c *stringsWalk, n *Node, v *apps.EnvVar) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } + if c.mode == EmptyFields && v.ValueFrom == "" && Forced(v.ForceSendFields, "ValueFrom") { + ch := n.Child("value_from") + c.fn(&ch, "") + } + if s := string(v.ValueFrom); c.match(s) { + ch := n.Child("value_from") + if r, ok := c.fn(&ch, s); ok { + v.ValueFrom = (r) + } + } +} + +func stringsAppsAppHealthCheck(c *stringsWalk, n *Node, v *apps.AppHealthCheck) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Path == "" && Forced(v.ForceSendFields, "Path") { + ch := n.Child("path") + c.fn(&ch, "") + } + if s := string(v.Path); c.match(s) { + ch := n.Child("path") + if r, ok := c.fn(&ch, s); ok { + v.Path = (r) + } + } +} + +func stringsAppsAppDeploymentStatus(c *stringsWalk, n *Node, v *apps.AppDeploymentStatus) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Message == "" && Forced(v.ForceSendFields, "Message") { + ch := n.Child("message") + c.fn(&ch, "") + } + if s := string(v.Message); c.match(s) { + ch := n.Child("message") + if r, ok := c.fn(&ch, s); ok { + v.Message = (r) + } + } + if c.mode == EmptyFields && v.State == "" && Forced(v.ForceSendFields, "State") { + ch := n.Child("state") + c.fn(&ch, "") + } + if s := string(v.State); c.match(s) { + ch := n.Child("state") + if r, ok := c.fn(&ch, s); ok { + v.State = apps.AppDeploymentState(r) + } + } +} + +func stringsAppsAppResourceApp(c *stringsWalk, n *Node, v *apps.AppResourceApp) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Permission == "" && Forced(v.ForceSendFields, "Permission") { + ch := n.Child("permission") + c.fn(&ch, "") + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceAppAppPermission(r) + } + } +} + +func stringsAppsAppResourceDatabase(c *stringsWalk, n *Node, v *apps.AppResourceDatabase) { + if n.isRef() { + return + } + if s := string(v.DatabaseName); c.match(s) { + ch := n.Child("database_name") + if r, ok := c.fn(&ch, s); ok { + v.DatabaseName = (r) + } + } + if s := string(v.InstanceName); c.match(s) { + ch := n.Child("instance_name") + if r, ok := c.fn(&ch, s); ok { + v.InstanceName = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceDatabaseDatabasePermission(r) + } + } +} + +func stringsAppsAppResourceExperiment(c *stringsWalk, n *Node, v *apps.AppResourceExperiment) { + if n.isRef() { + return + } + if s := string(v.ExperimentId); c.match(s) { + ch := n.Child("experiment_id") + if r, ok := c.fn(&ch, s); ok { + v.ExperimentId = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceExperimentExperimentPermission(r) + } + } +} + +func stringsAppsAppResourceGenieSpace(c *stringsWalk, n *Node, v *apps.AppResourceGenieSpace) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceGenieSpaceGenieSpacePermission(r) + } + } + if s := string(v.SpaceId); c.match(s) { + ch := n.Child("space_id") + if r, ok := c.fn(&ch, s); ok { + v.SpaceId = (r) + } + } +} + +func stringsAppsAppResourceJob(c *stringsWalk, n *Node, v *apps.AppResourceJob) { + if n.isRef() { + return + } + if s := string(v.Id); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.Id = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceJobJobPermission(r) + } + } +} + +func stringsAppsAppResourcePostgres(c *stringsWalk, n *Node, v *apps.AppResourcePostgres) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Branch == "" && Forced(v.ForceSendFields, "Branch") { + ch := n.Child("branch") + c.fn(&ch, "") + } + if s := string(v.Branch); c.match(s) { + ch := n.Child("branch") + if r, ok := c.fn(&ch, s); ok { + v.Branch = (r) + } + } + if c.mode == EmptyFields && v.Database == "" && Forced(v.ForceSendFields, "Database") { + ch := n.Child("database") + c.fn(&ch, "") + } + if s := string(v.Database); c.match(s) { + ch := n.Child("database") + if r, ok := c.fn(&ch, s); ok { + v.Database = (r) + } + } + if c.mode == EmptyFields && v.Permission == "" && Forced(v.ForceSendFields, "Permission") { + ch := n.Child("permission") + c.fn(&ch, "") + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourcePostgresPostgresPermission(r) + } + } +} + +func stringsAppsAppResourceSecret(c *stringsWalk, n *Node, v *apps.AppResourceSecret) { + if n.isRef() { + return + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceSecretSecretPermission(r) + } + } + if s := string(v.Scope); c.match(s) { + ch := n.Child("scope") + if r, ok := c.fn(&ch, s); ok { + v.Scope = (r) + } + } +} + +func stringsAppsAppResourceServingEndpoint(c *stringsWalk, n *Node, v *apps.AppResourceServingEndpoint) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceServingEndpointServingEndpointPermission(r) + } + } +} + +func stringsAppsAppResourceSqlWarehouse(c *stringsWalk, n *Node, v *apps.AppResourceSqlWarehouse) { + if n.isRef() { + return + } + if s := string(v.Id); c.match(s) { + ch := n.Child("id") + if r, ok := c.fn(&ch, s); ok { + v.Id = (r) + } + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceSqlWarehouseSqlWarehousePermission(r) + } + } +} + +func stringsAppsAppResourceUcSecurable(c *stringsWalk, n *Node, v *apps.AppResourceUcSecurable) { + if n.isRef() { + return + } + if s := string(v.Permission); c.match(s) { + ch := n.Child("permission") + if r, ok := c.fn(&ch, s); ok { + v.Permission = apps.AppResourceUcSecurableUcSecurablePermission(r) + } + } + if s := string(v.SecurableFullName); c.match(s) { + ch := n.Child("securable_full_name") + if r, ok := c.fn(&ch, s); ok { + v.SecurableFullName = (r) + } + } + if c.mode == EmptyFields && v.SecurableKind == "" && Forced(v.ForceSendFields, "SecurableKind") { + ch := n.Child("securable_kind") + c.fn(&ch, "") + } + if s := string(v.SecurableKind); c.match(s) { + ch := n.Child("securable_kind") + if r, ok := c.fn(&ch, s); ok { + v.SecurableKind = (r) + } + } + if s := string(v.SecurableType); c.match(s) { + ch := n.Child("securable_type") + if r, ok := c.fn(&ch, s); ok { + v.SecurableType = apps.AppResourceUcSecurableUcSecurableType(r) + } + } +} + +func stringsAppsUnityCatalog(c *stringsWalk, n *Node, v *apps.UnityCatalog) { + if n.isRef() { + return + } + if s := string(v.LogsTable); c.match(s) { + ch := n.Child("logs_table") + if r, ok := c.fn(&ch, s); ok { + v.LogsTable = (r) + } + } + if s := string(v.MetricsTable); c.match(s) { + ch := n.Child("metrics_table") + if r, ok := c.fn(&ch, s); ok { + v.MetricsTable = (r) + } + } + if s := string(v.TracesTable); c.match(s) { + ch := n.Child("traces_table") + if r, ok := c.fn(&ch, s); ok { + v.TracesTable = (r) + } + } +} + +func stringsSqlAlertV2Notification(c *stringsWalk, n *Node, v *sql.AlertV2Notification) { + if n.isRef() { + return + } + if !n.IsRef("subscriptions") { + if len(v.Subscriptions) > 0 { + cn := n.Child("subscriptions") + for i := range v.Subscriptions { + { + ch := cn.Elem(i) + stringsSqlAlertV2Subscription(c, &ch, &v.Subscriptions[i]) + } + } + } + } +} + +func stringsSqlAlertV2OperandColumn(c *stringsWalk, n *Node, v *sql.AlertV2OperandColumn) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Aggregation == "" && Forced(v.ForceSendFields, "Aggregation") { + ch := n.Child("aggregation") + c.fn(&ch, "") + } + if s := string(v.Aggregation); c.match(s) { + ch := n.Child("aggregation") + if r, ok := c.fn(&ch, s); ok { + v.Aggregation = sql.Aggregation(r) + } + } + if c.mode == EmptyFields && v.Display == "" && Forced(v.ForceSendFields, "Display") { + ch := n.Child("display") + c.fn(&ch, "") + } + if s := string(v.Display); c.match(s) { + ch := n.Child("display") + if r, ok := c.fn(&ch, s); ok { + v.Display = (r) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsSqlAlertV2Operand(c *stringsWalk, n *Node, v *sql.AlertV2Operand) { + if n.isRef() { + return + } + if !n.IsRef("column") { + if x := v.Column; x != nil { + ch := n.Child("column") + stringsSqlAlertV2OperandColumn(c, &ch, x) + } + } + if !n.IsRef("value") { + if x := v.Value; x != nil { + ch := n.Child("value") + stringsSqlAlertV2OperandValue(c, &ch, x) + } + } +} + +func stringsSqlEndpointTagPair(c *stringsWalk, n *Node, v *sql.EndpointTagPair) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsDatabaseSyncedTableContinuousUpdateStatus(c *stringsWalk, n *Node, v *database.SyncedTableContinuousUpdateStatus) { + if n.isRef() { + return + } + if !n.IsRef("initial_pipeline_sync_progress") { + if x := v.InitialPipelineSyncProgress; x != nil { + ch := n.Child("initial_pipeline_sync_progress") + stringsDatabaseSyncedTablePipelineProgress(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Timestamp == "" && Forced(v.ForceSendFields, "Timestamp") { + ch := n.Child("timestamp") + c.fn(&ch, "") + } + if s := string(v.Timestamp); c.match(s) { + ch := n.Child("timestamp") + if r, ok := c.fn(&ch, s); ok { + v.Timestamp = (r) + } + } +} + +func stringsDatabaseSyncedTableFailedStatus(c *stringsWalk, n *Node, v *database.SyncedTableFailedStatus) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Timestamp == "" && Forced(v.ForceSendFields, "Timestamp") { + ch := n.Child("timestamp") + c.fn(&ch, "") + } + if s := string(v.Timestamp); c.match(s) { + ch := n.Child("timestamp") + if r, ok := c.fn(&ch, s); ok { + v.Timestamp = (r) + } + } +} + +func stringsDatabaseSyncedTablePosition(c *stringsWalk, n *Node, v *database.SyncedTablePosition) { + if n.isRef() { + return + } + if !n.IsRef("delta_table_sync_info") { + if x := v.DeltaTableSyncInfo; x != nil { + ch := n.Child("delta_table_sync_info") + stringsDatabaseDeltaTableSyncInfo(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SyncEndTimestamp == "" && Forced(v.ForceSendFields, "SyncEndTimestamp") { + ch := n.Child("sync_end_timestamp") + c.fn(&ch, "") + } + if s := string(v.SyncEndTimestamp); c.match(s) { + ch := n.Child("sync_end_timestamp") + if r, ok := c.fn(&ch, s); ok { + v.SyncEndTimestamp = (r) + } + } + if c.mode == EmptyFields && v.SyncStartTimestamp == "" && Forced(v.ForceSendFields, "SyncStartTimestamp") { + ch := n.Child("sync_start_timestamp") + c.fn(&ch, "") + } + if s := string(v.SyncStartTimestamp); c.match(s) { + ch := n.Child("sync_start_timestamp") + if r, ok := c.fn(&ch, s); ok { + v.SyncStartTimestamp = (r) + } + } +} + +func stringsDatabaseSyncedTableProvisioningStatus(c *stringsWalk, n *Node, v *database.SyncedTableProvisioningStatus) { + if n.isRef() { + return + } + if !n.IsRef("initial_pipeline_sync_progress") { + if x := v.InitialPipelineSyncProgress; x != nil { + ch := n.Child("initial_pipeline_sync_progress") + stringsDatabaseSyncedTablePipelineProgress(c, &ch, x) + } + } +} + +func stringsDatabaseSyncedTableTriggeredUpdateStatus(c *stringsWalk, n *Node, v *database.SyncedTableTriggeredUpdateStatus) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Timestamp == "" && Forced(v.ForceSendFields, "Timestamp") { + ch := n.Child("timestamp") + c.fn(&ch, "") + } + if s := string(v.Timestamp); c.match(s) { + ch := n.Child("timestamp") + if r, ok := c.fn(&ch, s); ok { + v.Timestamp = (r) + } + } + if !n.IsRef("triggered_update_progress") { + if x := v.TriggeredUpdateProgress; x != nil { + ch := n.Child("triggered_update_progress") + stringsDatabaseSyncedTablePipelineProgress(c, &ch, x) + } + } +} + +func stringsDatabaseNewPipelineSpec(c *stringsWalk, n *Node, v *database.NewPipelineSpec) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.BudgetPolicyId == "" && Forced(v.ForceSendFields, "BudgetPolicyId") { + ch := n.Child("budget_policy_id") + c.fn(&ch, "") + } + if s := string(v.BudgetPolicyId); c.match(s) { + ch := n.Child("budget_policy_id") + if r, ok := c.fn(&ch, s); ok { + v.BudgetPolicyId = (r) + } + } + if c.mode == EmptyFields && v.StorageCatalog == "" && Forced(v.ForceSendFields, "StorageCatalog") { + ch := n.Child("storage_catalog") + c.fn(&ch, "") + } + if s := string(v.StorageCatalog); c.match(s) { + ch := n.Child("storage_catalog") + if r, ok := c.fn(&ch, s); ok { + v.StorageCatalog = (r) + } + } + if c.mode == EmptyFields && v.StorageSchema == "" && Forced(v.ForceSendFields, "StorageSchema") { + ch := n.Child("storage_schema") + c.fn(&ch, "") + } + if s := string(v.StorageSchema); c.match(s) { + ch := n.Child("storage_schema") + if r, ok := c.fn(&ch, s); ok { + v.StorageSchema = (r) + } + } +} + +func stringsDatabaseSyncedTableSpecTypeOverride(c *stringsWalk, n *Node, v *database.SyncedTableSpecTypeOverride) { + if n.isRef() { + return + } + if s := string(v.ColumnName); c.match(s) { + ch := n.Child("column_name") + if r, ok := c.fn(&ch, s); ok { + v.ColumnName = (r) + } + } + if s := string(v.PgType); c.match(s) { + ch := n.Child("pg_type") + if r, ok := c.fn(&ch, s); ok { + v.PgType = database.SyncedTableSpecPgSpecificType(r) + } + } +} + +func stringsPostgresWeeklySchedule(c *stringsWalk, n *Node, v *postgres.WeeklySchedule) { + if n.isRef() { + return + } + if s := string(v.DayOfWeek); c.match(s) { + ch := n.Child("day_of_week") + if r, ok := c.fn(&ch, s); ok { + v.DayOfWeek = postgres.DayOfWeek(r) + } + } +} + +func stringsVectorsearchEmbeddingSourceColumn(c *stringsWalk, n *Node, v *vectorsearch.EmbeddingSourceColumn) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.EmbeddingModelEndpointName == "" && Forced(v.ForceSendFields, "EmbeddingModelEndpointName") { + ch := n.Child("embedding_model_endpoint_name") + c.fn(&ch, "") + } + if s := string(v.EmbeddingModelEndpointName); c.match(s) { + ch := n.Child("embedding_model_endpoint_name") + if r, ok := c.fn(&ch, s); ok { + v.EmbeddingModelEndpointName = (r) + } + } + if c.mode == EmptyFields && v.ModelEndpointNameForQuery == "" && Forced(v.ForceSendFields, "ModelEndpointNameForQuery") { + ch := n.Child("model_endpoint_name_for_query") + c.fn(&ch, "") + } + if s := string(v.ModelEndpointNameForQuery); c.match(s) { + ch := n.Child("model_endpoint_name_for_query") + if r, ok := c.fn(&ch, s); ok { + v.ModelEndpointNameForQuery = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsVectorsearchEmbeddingVectorColumn(c *stringsWalk, n *Node, v *vectorsearch.EmbeddingVectorColumn) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsComputeDiskType(c *stringsWalk, n *Node, v *compute.DiskType) { + if n.isRef() { + return + } + if s := string(v.AzureDiskVolumeType); c.match(s) { + ch := n.Child("azure_disk_volume_type") + if r, ok := c.fn(&ch, s); ok { + v.AzureDiskVolumeType = compute.DiskTypeAzureDiskVolumeType(r) + } + } + if s := string(v.EbsVolumeType); c.match(s) { + ch := n.Child("ebs_volume_type") + if r, ok := c.fn(&ch, s); ok { + v.EbsVolumeType = compute.DiskTypeEbsVolumeType(r) + } + } +} + +func stringsJobsDeploymentSpec(c *stringsWalk, n *Node, v *jobs.DeploymentSpec) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CommandPath == "" && Forced(v.ForceSendFields, "CommandPath") { + ch := n.Child("command_path") + c.fn(&ch, "") + } + if s := string(v.CommandPath); c.match(s) { + ch := n.Child("command_path") + if r, ok := c.fn(&ch, s); ok { + v.CommandPath = (r) + } + } + if !n.IsRef("compute") { + { + ch := n.Child("compute") + stringsJobsComputeSpec(c, &ch, &v.Compute) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsJobsAlertTaskSubscriber(c *stringsWalk, n *Node, v *jobs.AlertTaskSubscriber) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DestinationId == "" && Forced(v.ForceSendFields, "DestinationId") { + ch := n.Child("destination_id") + c.fn(&ch, "") + } + if s := string(v.DestinationId); c.match(s) { + ch := n.Child("destination_id") + if r, ok := c.fn(&ch, s); ok { + v.DestinationId = (r) + } + } + if c.mode == EmptyFields && v.UserName == "" && Forced(v.ForceSendFields, "UserName") { + ch := n.Child("user_name") + c.fn(&ch, "") + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } +} + +func stringsJobsSubscription(c *stringsWalk, n *Node, v *jobs.Subscription) { + if n.isRef() { + return + } + if !n.IsRef("attachment") { + if x := v.Attachment; x != nil { + ch := n.Child("attachment") + stringsJobsSubscriptionAttachment(c, &ch, x) + } + } + if c.mode == EmptyFields && v.CustomSubject == "" && Forced(v.ForceSendFields, "CustomSubject") { + ch := n.Child("custom_subject") + c.fn(&ch, "") + } + if s := string(v.CustomSubject); c.match(s) { + ch := n.Child("custom_subject") + if r, ok := c.fn(&ch, s); ok { + v.CustomSubject = (r) + } + } + if !n.IsRef("subscribers") { + if len(v.Subscribers) > 0 { + cn := n.Child("subscribers") + for i := range v.Subscribers { + { + ch := cn.Elem(i) + stringsJobsSubscriptionSubscriber(c, &ch, &v.Subscribers[i]) + } + } + } + } +} + +func stringsJobsComputeConfig(c *stringsWalk, n *Node, v *jobs.ComputeConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.GpuNodePoolId == "" && Forced(v.ForceSendFields, "GpuNodePoolId") { + ch := n.Child("gpu_node_pool_id") + c.fn(&ch, "") + } + if s := string(v.GpuNodePoolId); c.match(s) { + ch := n.Child("gpu_node_pool_id") + if r, ok := c.fn(&ch, s); ok { + v.GpuNodePoolId = (r) + } + } + if c.mode == EmptyFields && v.GpuType == "" && Forced(v.ForceSendFields, "GpuType") { + ch := n.Child("gpu_type") + c.fn(&ch, "") + } + if s := string(v.GpuType); c.match(s) { + ch := n.Child("gpu_type") + if r, ok := c.fn(&ch, s); ok { + v.GpuType = (r) + } + } +} + +func stringsJobsPowerBiModel(c *stringsWalk, n *Node, v *jobs.PowerBiModel) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AuthenticationMethod == "" && Forced(v.ForceSendFields, "AuthenticationMethod") { + ch := n.Child("authentication_method") + c.fn(&ch, "") + } + if s := string(v.AuthenticationMethod); c.match(s) { + ch := n.Child("authentication_method") + if r, ok := c.fn(&ch, s); ok { + v.AuthenticationMethod = jobs.AuthenticationMethod(r) + } + } + if c.mode == EmptyFields && v.ModelName == "" && Forced(v.ForceSendFields, "ModelName") { + ch := n.Child("model_name") + c.fn(&ch, "") + } + if s := string(v.ModelName); c.match(s) { + ch := n.Child("model_name") + if r, ok := c.fn(&ch, s); ok { + v.ModelName = (r) + } + } + if c.mode == EmptyFields && v.StorageMode == "" && Forced(v.ForceSendFields, "StorageMode") { + ch := n.Child("storage_mode") + c.fn(&ch, "") + } + if s := string(v.StorageMode); c.match(s) { + ch := n.Child("storage_mode") + if r, ok := c.fn(&ch, s); ok { + v.StorageMode = jobs.StorageMode(r) + } + } + if c.mode == EmptyFields && v.WorkspaceName == "" && Forced(v.ForceSendFields, "WorkspaceName") { + ch := n.Child("workspace_name") + c.fn(&ch, "") + } + if s := string(v.WorkspaceName); c.match(s) { + ch := n.Child("workspace_name") + if r, ok := c.fn(&ch, s); ok { + v.WorkspaceName = (r) + } + } +} + +func stringsJobsPowerBiTable(c *stringsWalk, n *Node, v *jobs.PowerBiTable) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Catalog == "" && Forced(v.ForceSendFields, "Catalog") { + ch := n.Child("catalog") + c.fn(&ch, "") + } + if s := string(v.Catalog); c.match(s) { + ch := n.Child("catalog") + if r, ok := c.fn(&ch, s); ok { + v.Catalog = (r) + } + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Schema == "" && Forced(v.ForceSendFields, "Schema") { + ch := n.Child("schema") + c.fn(&ch, "") + } + if s := string(v.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.Schema = (r) + } + } + if c.mode == EmptyFields && v.StorageMode == "" && Forced(v.ForceSendFields, "StorageMode") { + ch := n.Child("storage_mode") + c.fn(&ch, "") + } + if s := string(v.StorageMode); c.match(s) { + ch := n.Child("storage_mode") + if r, ok := c.fn(&ch, s); ok { + v.StorageMode = jobs.StorageMode(r) + } + } +} + +func stringsJobsPythonOperatorTaskParameter(c *stringsWalk, n *Node, v *jobs.PythonOperatorTaskParameter) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Name == "" && Forced(v.ForceSendFields, "Name") { + ch := n.Child("name") + c.fn(&ch, "") + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsJobsSqlTaskAlert(c *stringsWalk, n *Node, v *jobs.SqlTaskAlert) { + if n.isRef() { + return + } + if s := string(v.AlertId); c.match(s) { + ch := n.Child("alert_id") + if r, ok := c.fn(&ch, s); ok { + v.AlertId = (r) + } + } + if !n.IsRef("subscriptions") { + if len(v.Subscriptions) > 0 { + cn := n.Child("subscriptions") + for i := range v.Subscriptions { + { + ch := cn.Elem(i) + stringsJobsSqlTaskSubscription(c, &ch, &v.Subscriptions[i]) + } + } + } + } +} + +func stringsJobsSqlTaskDashboard(c *stringsWalk, n *Node, v *jobs.SqlTaskDashboard) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CustomSubject == "" && Forced(v.ForceSendFields, "CustomSubject") { + ch := n.Child("custom_subject") + c.fn(&ch, "") + } + if s := string(v.CustomSubject); c.match(s) { + ch := n.Child("custom_subject") + if r, ok := c.fn(&ch, s); ok { + v.CustomSubject = (r) + } + } + if s := string(v.DashboardId); c.match(s) { + ch := n.Child("dashboard_id") + if r, ok := c.fn(&ch, s); ok { + v.DashboardId = (r) + } + } + if !n.IsRef("subscriptions") { + if len(v.Subscriptions) > 0 { + cn := n.Child("subscriptions") + for i := range v.Subscriptions { + { + ch := cn.Elem(i) + stringsJobsSqlTaskSubscription(c, &ch, &v.Subscriptions[i]) + } + } + } + } +} + +func stringsJobsSqlTaskFile(c *stringsWalk, n *Node, v *jobs.SqlTaskFile) { + if n.isRef() { + return + } + if s := string(v.Path); c.match(s) { + ch := n.Child("path") + if r, ok := c.fn(&ch, s); ok { + v.Path = (r) + } + } + if s := string(v.Source); c.match(s) { + ch := n.Child("source") + if r, ok := c.fn(&ch, s); ok { + v.Source = jobs.Source(r) + } + } +} + +func stringsJobsSqlTaskQuery(c *stringsWalk, n *Node, v *jobs.SqlTaskQuery) { + if n.isRef() { + return + } + if s := string(v.QueryId); c.match(s) { + ch := n.Child("query_id") + if r, ok := c.fn(&ch, s); ok { + v.QueryId = (r) + } + } +} + +func stringsPipelinesReportSpec(c *stringsWalk, n *Node, v *pipelines.ReportSpec) { + if n.isRef() { + return + } + if s := string(v.DestinationCatalog); c.match(s) { + ch := n.Child("destination_catalog") + if r, ok := c.fn(&ch, s); ok { + v.DestinationCatalog = (r) + } + } + if s := string(v.DestinationSchema); c.match(s) { + ch := n.Child("destination_schema") + if r, ok := c.fn(&ch, s); ok { + v.DestinationSchema = (r) + } + } + if c.mode == EmptyFields && v.DestinationTable == "" && Forced(v.ForceSendFields, "DestinationTable") { + ch := n.Child("destination_table") + c.fn(&ch, "") + } + if s := string(v.DestinationTable); c.match(s) { + ch := n.Child("destination_table") + if r, ok := c.fn(&ch, s); ok { + v.DestinationTable = (r) + } + } + if s := string(v.SourceUrl); c.match(s) { + ch := n.Child("source_url") + if r, ok := c.fn(&ch, s); ok { + v.SourceUrl = (r) + } + } + if !n.IsRef("table_configuration") { + if x := v.TableConfiguration; x != nil { + ch := n.Child("table_configuration") + stringsPipelinesTableSpecificConfig(c, &ch, x) + } + } +} + +func stringsPipelinesSchemaSpec(c *stringsWalk, n *Node, v *pipelines.SchemaSpec) { + if n.isRef() { + return + } + if !n.IsRef("connector_options") { + if x := v.ConnectorOptions; x != nil { + ch := n.Child("connector_options") + stringsPipelinesConnectorOptions(c, &ch, x) + } + } + if s := string(v.DestinationCatalog); c.match(s) { + ch := n.Child("destination_catalog") + if r, ok := c.fn(&ch, s); ok { + v.DestinationCatalog = (r) + } + } + if s := string(v.DestinationSchema); c.match(s) { + ch := n.Child("destination_schema") + if r, ok := c.fn(&ch, s); ok { + v.DestinationSchema = (r) + } + } + if !n.IsRef("fanout_options") { + if x := v.FanoutOptions; x != nil { + ch := n.Child("fanout_options") + stringsPipelinesIngestionPipelineDefinitionFanoutOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SourceCatalog == "" && Forced(v.ForceSendFields, "SourceCatalog") { + ch := n.Child("source_catalog") + c.fn(&ch, "") + } + if s := string(v.SourceCatalog); c.match(s) { + ch := n.Child("source_catalog") + if r, ok := c.fn(&ch, s); ok { + v.SourceCatalog = (r) + } + } + if c.mode == EmptyFields && v.SourceSchema == "" && Forced(v.ForceSendFields, "SourceSchema") { + ch := n.Child("source_schema") + c.fn(&ch, "") + } + if s := string(v.SourceSchema); c.match(s) { + ch := n.Child("source_schema") + if r, ok := c.fn(&ch, s); ok { + v.SourceSchema = (r) + } + } + if !n.IsRef("table_configuration") { + if x := v.TableConfiguration; x != nil { + ch := n.Child("table_configuration") + stringsPipelinesTableSpecificConfig(c, &ch, x) + } + } +} + +func stringsPipelinesTableSpec(c *stringsWalk, n *Node, v *pipelines.TableSpec) { + if n.isRef() { + return + } + if !n.IsRef("connector_options") { + if x := v.ConnectorOptions; x != nil { + ch := n.Child("connector_options") + stringsPipelinesConnectorOptions(c, &ch, x) + } + } + if s := string(v.DestinationCatalog); c.match(s) { + ch := n.Child("destination_catalog") + if r, ok := c.fn(&ch, s); ok { + v.DestinationCatalog = (r) + } + } + if s := string(v.DestinationSchema); c.match(s) { + ch := n.Child("destination_schema") + if r, ok := c.fn(&ch, s); ok { + v.DestinationSchema = (r) + } + } + if c.mode == EmptyFields && v.DestinationTable == "" && Forced(v.ForceSendFields, "DestinationTable") { + ch := n.Child("destination_table") + c.fn(&ch, "") + } + if s := string(v.DestinationTable); c.match(s) { + ch := n.Child("destination_table") + if r, ok := c.fn(&ch, s); ok { + v.DestinationTable = (r) + } + } + if c.mode == EmptyFields && v.SourceCatalog == "" && Forced(v.ForceSendFields, "SourceCatalog") { + ch := n.Child("source_catalog") + c.fn(&ch, "") + } + if s := string(v.SourceCatalog); c.match(s) { + ch := n.Child("source_catalog") + if r, ok := c.fn(&ch, s); ok { + v.SourceCatalog = (r) + } + } + if c.mode == EmptyFields && v.SourceSchema == "" && Forced(v.ForceSendFields, "SourceSchema") { + ch := n.Child("source_schema") + c.fn(&ch, "") + } + if s := string(v.SourceSchema); c.match(s) { + ch := n.Child("source_schema") + if r, ok := c.fn(&ch, s); ok { + v.SourceSchema = (r) + } + } + if c.mode == EmptyFields && v.SourceTable == "" && Forced(v.ForceSendFields, "SourceTable") { + ch := n.Child("source_table") + c.fn(&ch, "") + } + if s := string(v.SourceTable); c.match(s) { + ch := n.Child("source_table") + if r, ok := c.fn(&ch, s); ok { + v.SourceTable = (r) + } + } + if !n.IsRef("table_configuration") { + if x := v.TableConfiguration; x != nil { + ch := n.Child("table_configuration") + stringsPipelinesTableSpecificConfig(c, &ch, x) + } + } +} + +func stringsPipelinesApiSourceConnectorConfig(c *stringsWalk, n *Node, v *pipelines.ApiSourceConnectorConfig) { + if n.isRef() { + return + } + if !n.IsRef("configs") { + if len(v.Configs) > 0 { + cn := n.Child("configs") + for _, k := range sortedKeys(v.Configs) { + if s := string(v.Configs[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Configs[k] = (r) + } + } + } + } + } +} + +func stringsPipelinesSourceCatalogConfig(c *stringsWalk, n *Node, v *pipelines.SourceCatalogConfig) { + if n.isRef() { + return + } + if !n.IsRef("postgres") { + if x := v.Postgres; x != nil { + ch := n.Child("postgres") + stringsPipelinesPostgresCatalogConfig(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SourceCatalog == "" && Forced(v.ForceSendFields, "SourceCatalog") { + ch := n.Child("source_catalog") + c.fn(&ch, "") + } + if s := string(v.SourceCatalog); c.match(s) { + ch := n.Child("source_catalog") + if r, ok := c.fn(&ch, s); ok { + v.SourceCatalog = (r) + } + } +} + +func stringsPipelinesGoogleAdsConfig(c *stringsWalk, n *Node, v *pipelines.GoogleAdsConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ManagerAccountId == "" && Forced(v.ForceSendFields, "ManagerAccountId") { + ch := n.Child("manager_account_id") + c.fn(&ch, "") + } + if s := string(v.ManagerAccountId); c.match(s) { + ch := n.Child("manager_account_id") + if r, ok := c.fn(&ch, s); ok { + v.ManagerAccountId = (r) + } + } +} + +func stringsPipelinesIngestionPipelineDefinitionTableSpecificConfigQueryBasedConnectorConfig(c *stringsWalk, n *Node, v *pipelines.IngestionPipelineDefinitionTableSpecificConfigQueryBasedConnectorConfig) { + if n.isRef() { + return + } + if !n.IsRef("cursor_columns") { + if len(v.CursorColumns) > 0 { + cn := n.Child("cursor_columns") + for i := range v.CursorColumns { + if s := string(v.CursorColumns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.CursorColumns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.DeletionCondition == "" && Forced(v.ForceSendFields, "DeletionCondition") { + ch := n.Child("deletion_condition") + c.fn(&ch, "") + } + if s := string(v.DeletionCondition); c.match(s) { + ch := n.Child("deletion_condition") + if r, ok := c.fn(&ch, s); ok { + v.DeletionCondition = (r) + } + } +} + +func stringsPipelinesIngestionPipelineDefinitionWorkdayReportParameters(c *stringsWalk, n *Node, v *pipelines.IngestionPipelineDefinitionWorkdayReportParameters) { + if n.isRef() { + return + } + if !n.IsRef("parameters") { + if len(v.Parameters) > 0 { + cn := n.Child("parameters") + for _, k := range sortedKeys(v.Parameters) { + if s := string(v.Parameters[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Parameters[k] = (r) + } + } + } + } + } + if !n.IsRef("report_parameters") { + if len(v.ReportParameters) > 0 { + cn := n.Child("report_parameters") + for i := range v.ReportParameters { + { + ch := cn.Elem(i) + stringsPipelinesIngestionPipelineDefinitionWorkdayReportParametersQueryKeyValue(c, &ch, &v.ReportParameters[i]) + } + } + } + } +} + +func stringsMlApproxCountDistinctFunction(c *stringsWalk, n *Node, v *ml.ApproxCountDistinctFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlApproxPercentileFunction(c *stringsWalk, n *Node, v *ml.ApproxPercentileFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlAvgFunction(c *stringsWalk, n *Node, v *ml.AvgFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlCountFunction(c *stringsWalk, n *Node, v *ml.CountFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlFirstFunction(c *stringsWalk, n *Node, v *ml.FirstFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlFirstDistinctFunction(c *stringsWalk, n *Node, v *ml.FirstDistinctFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlFirstNFunction(c *stringsWalk, n *Node, v *ml.FirstNFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlLastFunction(c *stringsWalk, n *Node, v *ml.LastFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlLastDistinctFunction(c *stringsWalk, n *Node, v *ml.LastDistinctFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlLastNFunction(c *stringsWalk, n *Node, v *ml.LastNFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlMaxFunction(c *stringsWalk, n *Node, v *ml.MaxFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlMinFunction(c *stringsWalk, n *Node, v *ml.MinFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlStddevPopFunction(c *stringsWalk, n *Node, v *ml.StddevPopFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlStddevSampFunction(c *stringsWalk, n *Node, v *ml.StddevSampFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlSumFunction(c *stringsWalk, n *Node, v *ml.SumFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlVarPopFunction(c *stringsWalk, n *Node, v *ml.VarPopFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlVarSampFunction(c *stringsWalk, n *Node, v *ml.VarSampFunction) { + if n.isRef() { + return + } + if s := string(v.Input); c.match(s) { + ch := n.Child("input") + if r, ok := c.fn(&ch, s); ok { + v.Input = (r) + } + } +} + +func stringsMlInputBinding(c *stringsWalk, n *Node, v *ml.InputBinding) { + if n.isRef() { + return + } + if s := string(v.Column); c.match(s) { + ch := n.Child("column") + if r, ok := c.fn(&ch, s); ok { + v.Column = (r) + } + } + if s := string(v.Parameter); c.match(s) { + ch := n.Child("parameter") + if r, ok := c.fn(&ch, s); ok { + v.Parameter = (r) + } + } +} + +func stringsMlFeatureReference(c *stringsWalk, n *Node, v *ml.FeatureReference) { + if n.isRef() { + return + } + if s := string(v.Feature); c.match(s) { + ch := n.Child("feature") + if r, ok := c.fn(&ch, s); ok { + v.Feature = (r) + } + } +} + +func stringsMlColumnIdentifier(c *stringsWalk, n *Node, v *ml.ColumnIdentifier) { + if n.isRef() { + return + } + if s := string(v.VariantExprPath); c.match(s) { + ch := n.Child("variant_expr_path") + if r, ok := c.fn(&ch, s); ok { + v.VariantExprPath = (r) + } + } +} + +func stringsMlFlatSchema(c *stringsWalk, n *Node, v *ml.FlatSchema) { + if n.isRef() { + return + } + if !n.IsRef("fields") { + if len(v.Fields) > 0 { + cn := n.Child("fields") + for i := range v.Fields { + { + ch := cn.Elem(i) + stringsMlFieldDefinition(c, &ch, &v.Fields[i]) + } + } + } + } +} + +func stringsServingAiGatewayGuardrailParameters(c *stringsWalk, n *Node, v *serving.AiGatewayGuardrailParameters) { + if n.isRef() { + return + } + if !n.IsRef("invalid_keywords") { + if len(v.InvalidKeywords) > 0 { + cn := n.Child("invalid_keywords") + for i := range v.InvalidKeywords { + if s := string(v.InvalidKeywords[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.InvalidKeywords[i] = (r) + } + } + } + } + } + if !n.IsRef("pii") { + if x := v.Pii; x != nil { + ch := n.Child("pii") + stringsServingAiGatewayGuardrailPiiBehavior(c, &ch, x) + } + } + if !n.IsRef("valid_topics") { + if len(v.ValidTopics) > 0 { + cn := n.Child("valid_topics") + for i := range v.ValidTopics { + if s := string(v.ValidTopics[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ValidTopics[i] = (r) + } + } + } + } + } +} + +func stringsServingExternalModel(c *stringsWalk, n *Node, v *serving.ExternalModel) { + if n.isRef() { + return + } + if !n.IsRef("ai21labs_config") { + if x := v.Ai21labsConfig; x != nil { + ch := n.Child("ai21labs_config") + stringsServingAi21LabsConfig(c, &ch, x) + } + } + if !n.IsRef("amazon_bedrock_config") { + if x := v.AmazonBedrockConfig; x != nil { + ch := n.Child("amazon_bedrock_config") + stringsServingAmazonBedrockConfig(c, &ch, x) + } + } + if !n.IsRef("anthropic_config") { + if x := v.AnthropicConfig; x != nil { + ch := n.Child("anthropic_config") + stringsServingAnthropicConfig(c, &ch, x) + } + } + if !n.IsRef("cohere_config") { + if x := v.CohereConfig; x != nil { + ch := n.Child("cohere_config") + stringsServingCohereConfig(c, &ch, x) + } + } + if !n.IsRef("custom_provider_config") { + if x := v.CustomProviderConfig; x != nil { + ch := n.Child("custom_provider_config") + stringsServingCustomProviderConfig(c, &ch, x) + } + } + if !n.IsRef("databricks_model_serving_config") { + if x := v.DatabricksModelServingConfig; x != nil { + ch := n.Child("databricks_model_serving_config") + stringsServingDatabricksModelServingConfig(c, &ch, x) + } + } + if !n.IsRef("google_cloud_vertex_ai_config") { + if x := v.GoogleCloudVertexAiConfig; x != nil { + ch := n.Child("google_cloud_vertex_ai_config") + stringsServingGoogleCloudVertexAiConfig(c, &ch, x) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if !n.IsRef("openai_config") { + if x := v.OpenaiConfig; x != nil { + ch := n.Child("openai_config") + stringsServingOpenAiConfig(c, &ch, x) + } + } + if !n.IsRef("palm_config") { + if x := v.PalmConfig; x != nil { + ch := n.Child("palm_config") + stringsServingPaLmConfig(c, &ch, x) + } + } + if s := string(v.Provider); c.match(s) { + ch := n.Child("provider") + if r, ok := c.fn(&ch, s); ok { + v.Provider = serving.ExternalModelProvider(r) + } + } + if s := string(v.Task); c.match(s) { + ch := n.Child("task") + if r, ok := c.fn(&ch, s); ok { + v.Task = (r) + } + } +} + +func stringsServingRoute(c *stringsWalk, n *Node, v *serving.Route) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ServedEntityName == "" && Forced(v.ForceSendFields, "ServedEntityName") { + ch := n.Child("served_entity_name") + c.fn(&ch, "") + } + if s := string(v.ServedEntityName); c.match(s) { + ch := n.Child("served_entity_name") + if r, ok := c.fn(&ch, s); ok { + v.ServedEntityName = (r) + } + } + if c.mode == EmptyFields && v.ServedModelName == "" && Forced(v.ForceSendFields, "ServedModelName") { + ch := n.Child("served_model_name") + c.fn(&ch, "") + } + if s := string(v.ServedModelName); c.match(s) { + ch := n.Child("served_model_name") + if r, ok := c.fn(&ch, s); ok { + v.ServedModelName = (r) + } + } +} + +func stringsCatalogModelServiceConfigDestinationConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfigDestinationConfig) { + if n.isRef() { + return + } + if s := string(v.DestinationType); c.match(s) { + ch := n.Child("destination_type") + if r, ok := c.fn(&ch, s); ok { + v.DestinationType = catalog.ModelServiceConfigDestinationConfigDestinationType(r) + } + } + if !n.IsRef("external_model_config") { + if x := v.ExternalModelConfig; x != nil { + ch := n.Child("external_model_config") + stringsCatalogModelServiceConfigExternalModelConfig(c, &ch, x) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } + if !n.IsRef("pay_per_token_config") { + if x := v.PayPerTokenConfig; x != nil { + ch := n.Child("pay_per_token_config") + stringsCatalogModelServiceConfigPayPerTokenConfig(c, &ch, x) + } + } + if !n.IsRef("provisioned_throughput_config") { + if x := v.ProvisionedThroughputConfig; x != nil { + ch := n.Child("provisioned_throughput_config") + stringsCatalogModelServiceConfigProvisionedThroughputConfig(c, &ch, x) + } + } +} + +func stringsCatalogModelServiceConfigFallbackConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfigFallbackConfig) { + if n.isRef() { + return + } + if !n.IsRef("destinations") { + if len(v.Destinations) > 0 { + cn := n.Child("destinations") + for i := range v.Destinations { + { + ch := cn.Elem(i) + stringsCatalogModelServiceConfigDestinationConfig(c, &ch, &v.Destinations[i]) + } + } + } + } +} + +func stringsCatalogModelProviderServiceConfigAmazonBedrockProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAmazonBedrockProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("aws_access_key") { + if x := v.AwsAccessKey; x != nil { + ch := n.Child("aws_access_key") + stringsCatalogModelProviderServiceConfigAwsAccessKey(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Region == "" && Forced(v.ForceSendFields, "Region") { + ch := n.Child("region") + c.fn(&ch, "") + } + if s := string(v.Region); c.match(s) { + ch := n.Child("region") + if r, ok := c.fn(&ch, s); ok { + v.Region = (r) + } + } + if !n.IsRef("service_credential") { + if x := v.ServiceCredential; x != nil { + ch := n.Child("service_credential") + stringsCatalogModelProviderServiceConfigServiceCredential(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigAnthropicProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAnthropicProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key") { + if x := v.ApiKey; x != nil { + ch := n.Child("api_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigAzureOpenAiProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAzureOpenAiProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key") { + if x := v.ApiKey; x != nil { + ch := n.Child("api_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } + if c.mode == EmptyFields && v.BaseUrl == "" && Forced(v.ForceSendFields, "BaseUrl") { + ch := n.Child("base_url") + c.fn(&ch, "") + } + if s := string(v.BaseUrl); c.match(s) { + ch := n.Child("base_url") + if r, ok := c.fn(&ch, s); ok { + v.BaseUrl = (r) + } + } + if !n.IsRef("entra_service_principal") { + if x := v.EntraServicePrincipal; x != nil { + ch := n.Child("entra_service_principal") + stringsCatalogModelProviderServiceConfigEntraServicePrincipal(c, &ch, x) + } + } + if !n.IsRef("service_credential") { + if x := v.ServiceCredential; x != nil { + ch := n.Child("service_credential") + stringsCatalogModelProviderServiceConfigServiceCredential(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigCustomProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigCustomProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key") { + if x := v.ApiKey; x != nil { + ch := n.Child("api_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } + if c.mode == EmptyFields && v.BaseUrl == "" && Forced(v.ForceSendFields, "BaseUrl") { + ch := n.Child("base_url") + c.fn(&ch, "") + } + if s := string(v.BaseUrl); c.match(s) { + ch := n.Child("base_url") + if r, ok := c.fn(&ch, s); ok { + v.BaseUrl = (r) + } + } + if !n.IsRef("header_auth") { + if x := v.HeaderAuth; x != nil { + ch := n.Child("header_auth") + stringsCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigGeminiEnterpriseProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key") { + if x := v.ApiKey; x != nil { + ch := n.Child("api_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ProjectId == "" && Forced(v.ForceSendFields, "ProjectId") { + ch := n.Child("project_id") + c.fn(&ch, "") + } + if s := string(v.ProjectId); c.match(s) { + ch := n.Child("project_id") + if r, ok := c.fn(&ch, s); ok { + v.ProjectId = (r) + } + } + if c.mode == EmptyFields && v.Region == "" && Forced(v.ForceSendFields, "Region") { + ch := n.Child("region") + c.fn(&ch, "") + } + if s := string(v.Region); c.match(s) { + ch := n.Child("region") + if r, ok := c.fn(&ch, s); ok { + v.Region = (r) + } + } + if !n.IsRef("service_credential") { + if x := v.ServiceCredential; x != nil { + ch := n.Child("service_credential") + stringsCatalogModelProviderServiceConfigServiceCredential(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigMicrosoftFoundryProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key") { + if x := v.ApiKey; x != nil { + ch := n.Child("api_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } + if c.mode == EmptyFields && v.BaseUrl == "" && Forced(v.ForceSendFields, "BaseUrl") { + ch := n.Child("base_url") + c.fn(&ch, "") + } + if s := string(v.BaseUrl); c.match(s) { + ch := n.Child("base_url") + if r, ok := c.fn(&ch, s); ok { + v.BaseUrl = (r) + } + } + if !n.IsRef("entra_service_principal") { + if x := v.EntraServicePrincipal; x != nil { + ch := n.Child("entra_service_principal") + stringsCatalogModelProviderServiceConfigEntraServicePrincipal(c, &ch, x) + } + } + if !n.IsRef("service_credential") { + if x := v.ServiceCredential; x != nil { + ch := n.Child("service_credential") + stringsCatalogModelProviderServiceConfigServiceCredential(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigOpenAiProviderDirectConfig(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigOpenAiProviderDirectConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key") { + if x := v.ApiKey; x != nil { + ch := n.Child("api_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } + if c.mode == EmptyFields && v.BaseUrl == "" && Forced(v.ForceSendFields, "BaseUrl") { + ch := n.Child("base_url") + c.fn(&ch, "") + } + if s := string(v.BaseUrl); c.match(s) { + ch := n.Child("base_url") + if r, ok := c.fn(&ch, s); ok { + v.BaseUrl = (r) + } + } + if c.mode == EmptyFields && v.Organization == "" && Forced(v.ForceSendFields, "Organization") { + ch := n.Child("organization") + c.fn(&ch, "") + } + if s := string(v.Organization); c.match(s) { + ch := n.Child("organization") + if r, ok := c.fn(&ch, s); ok { + v.Organization = (r) + } + } +} + +func stringsSqlAlertV2Subscription(c *stringsWalk, n *Node, v *sql.AlertV2Subscription) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DestinationId == "" && Forced(v.ForceSendFields, "DestinationId") { + ch := n.Child("destination_id") + c.fn(&ch, "") + } + if s := string(v.DestinationId); c.match(s) { + ch := n.Child("destination_id") + if r, ok := c.fn(&ch, s); ok { + v.DestinationId = (r) + } + } + if c.mode == EmptyFields && v.UserEmail == "" && Forced(v.ForceSendFields, "UserEmail") { + ch := n.Child("user_email") + c.fn(&ch, "") + } + if s := string(v.UserEmail); c.match(s) { + ch := n.Child("user_email") + if r, ok := c.fn(&ch, s); ok { + v.UserEmail = (r) + } + } +} + +func stringsSqlAlertV2OperandValue(c *stringsWalk, n *Node, v *sql.AlertV2OperandValue) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.StringValue == "" && Forced(v.ForceSendFields, "StringValue") { + ch := n.Child("string_value") + c.fn(&ch, "") + } + if s := string(v.StringValue); c.match(s) { + ch := n.Child("string_value") + if r, ok := c.fn(&ch, s); ok { + v.StringValue = (r) + } + } +} + +func stringsDatabaseSyncedTablePipelineProgress(c *stringsWalk, n *Node, v *database.SyncedTablePipelineProgress) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ProvisioningPhase == "" && Forced(v.ForceSendFields, "ProvisioningPhase") { + ch := n.Child("provisioning_phase") + c.fn(&ch, "") + } + if s := string(v.ProvisioningPhase); c.match(s) { + ch := n.Child("provisioning_phase") + if r, ok := c.fn(&ch, s); ok { + v.ProvisioningPhase = database.ProvisioningPhase(r) + } + } +} + +func stringsDatabaseDeltaTableSyncInfo(c *stringsWalk, n *Node, v *database.DeltaTableSyncInfo) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DeltaCommitTimestamp == "" && Forced(v.ForceSendFields, "DeltaCommitTimestamp") { + ch := n.Child("delta_commit_timestamp") + c.fn(&ch, "") + } + if s := string(v.DeltaCommitTimestamp); c.match(s) { + ch := n.Child("delta_commit_timestamp") + if r, ok := c.fn(&ch, s); ok { + v.DeltaCommitTimestamp = (r) + } + } +} + +func stringsJobsComputeSpec(c *stringsWalk, n *Node, v *jobs.ComputeSpec) { + if n.isRef() { + return + } + if s := string(v.AcceleratorType); c.match(s) { + ch := n.Child("accelerator_type") + if r, ok := c.fn(&ch, s); ok { + v.AcceleratorType = jobs.ComputeSpecAcceleratorType(r) + } + } + if c.mode == EmptyFields && v.ProvisionedCapacityId == "" && Forced(v.ForceSendFields, "ProvisionedCapacityId") { + ch := n.Child("provisioned_capacity_id") + c.fn(&ch, "") + } + if s := string(v.ProvisionedCapacityId); c.match(s) { + ch := n.Child("provisioned_capacity_id") + if r, ok := c.fn(&ch, s); ok { + v.ProvisionedCapacityId = (r) + } + } +} + +func stringsJobsSubscriptionAttachment(c *stringsWalk, n *Node, v *jobs.SubscriptionAttachment) { + if n.isRef() { + return + } + if !n.IsRef("data_attachment") { + if x := v.DataAttachment; x != nil { + ch := n.Child("data_attachment") + stringsJobsSubscriptionAttachmentDataAttachment(c, &ch, x) + } + } + if !n.IsRef("page_attachment") { + if x := v.PageAttachment; x != nil { + ch := n.Child("page_attachment") + stringsJobsSubscriptionAttachmentPageAttachment(c, &ch, x) + } + } +} + +func stringsJobsSubscriptionSubscriber(c *stringsWalk, n *Node, v *jobs.SubscriptionSubscriber) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DestinationId == "" && Forced(v.ForceSendFields, "DestinationId") { + ch := n.Child("destination_id") + c.fn(&ch, "") + } + if s := string(v.DestinationId); c.match(s) { + ch := n.Child("destination_id") + if r, ok := c.fn(&ch, s); ok { + v.DestinationId = (r) + } + } + if c.mode == EmptyFields && v.UserName == "" && Forced(v.ForceSendFields, "UserName") { + ch := n.Child("user_name") + c.fn(&ch, "") + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } +} + +func stringsJobsSqlTaskSubscription(c *stringsWalk, n *Node, v *jobs.SqlTaskSubscription) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DestinationId == "" && Forced(v.ForceSendFields, "DestinationId") { + ch := n.Child("destination_id") + c.fn(&ch, "") + } + if s := string(v.DestinationId); c.match(s) { + ch := n.Child("destination_id") + if r, ok := c.fn(&ch, s); ok { + v.DestinationId = (r) + } + } + if c.mode == EmptyFields && v.UserName == "" && Forced(v.ForceSendFields, "UserName") { + ch := n.Child("user_name") + c.fn(&ch, "") + } + if s := string(v.UserName); c.match(s) { + ch := n.Child("user_name") + if r, ok := c.fn(&ch, s); ok { + v.UserName = (r) + } + } +} + +func stringsPipelinesConnectorOptions(c *stringsWalk, n *Node, v *pipelines.ConnectorOptions) { + if n.isRef() { + return + } + if !n.IsRef("api_source_connector_options") { + if x := v.ApiSourceConnectorOptions; x != nil { + ch := n.Child("api_source_connector_options") + stringsPipelinesApiSourceConnectorOptions(c, &ch, x) + } + } + if !n.IsRef("confluence_options") { + if x := v.ConfluenceOptions; x != nil { + ch := n.Child("confluence_options") + stringsPipelinesConfluenceConnectorOptions(c, &ch, x) + } + } + if !n.IsRef("gdrive_options") { + if x := v.GdriveOptions; x != nil { + ch := n.Child("gdrive_options") + stringsPipelinesGoogleDriveOptions(c, &ch, x) + } + } + if !n.IsRef("google_ads_options") { + if x := v.GoogleAdsOptions; x != nil { + ch := n.Child("google_ads_options") + stringsPipelinesGoogleAdsOptions(c, &ch, x) + } + } + if !n.IsRef("jira_options") { + if x := v.JiraOptions; x != nil { + ch := n.Child("jira_options") + stringsPipelinesJiraConnectorOptions(c, &ch, x) + } + } + if !n.IsRef("kafka_options") { + if x := v.KafkaOptions; x != nil { + ch := n.Child("kafka_options") + stringsPipelinesKafkaOptions(c, &ch, x) + } + } + if !n.IsRef("linkedin_ads_options") { + if x := v.LinkedinAdsOptions; x != nil { + ch := n.Child("linkedin_ads_options") + stringsPipelinesLinkedInAdsOptions(c, &ch, x) + } + } + if !n.IsRef("marketo_options") { + if x := v.MarketoOptions; x != nil { + ch := n.Child("marketo_options") + stringsPipelinesMarketoOptions(c, &ch, x) + } + } + if !n.IsRef("meta_ads_options") { + if x := v.MetaAdsOptions; x != nil { + ch := n.Child("meta_ads_options") + stringsPipelinesMetaMarketingOptions(c, &ch, x) + } + } + if !n.IsRef("outlook_options") { + if x := v.OutlookOptions; x != nil { + ch := n.Child("outlook_options") + stringsPipelinesOutlookOptions(c, &ch, x) + } + } + if !n.IsRef("rabbitmq_options") { + if x := v.RabbitmqOptions; x != nil { + ch := n.Child("rabbitmq_options") + stringsPipelinesRabbitmqOptions(c, &ch, x) + } + } + if !n.IsRef("reddit_ads_options") { + if x := v.RedditAdsOptions; x != nil { + ch := n.Child("reddit_ads_options") + stringsPipelinesRedditAdsOptions(c, &ch, x) + } + } + if !n.IsRef("sharepoint_options") { + if x := v.SharepointOptions; x != nil { + ch := n.Child("sharepoint_options") + stringsPipelinesSharepointOptions(c, &ch, x) + } + } + if !n.IsRef("tiktok_ads_options") { + if x := v.TiktokAdsOptions; x != nil { + ch := n.Child("tiktok_ads_options") + stringsPipelinesTikTokAdsOptions(c, &ch, x) + } + } + if !n.IsRef("zendesk_support_options") { + if x := v.ZendeskSupportOptions; x != nil { + ch := n.Child("zendesk_support_options") + stringsPipelinesZendeskSupportOptions(c, &ch, x) + } + } +} + +func stringsPipelinesIngestionPipelineDefinitionFanoutOptions(c *stringsWalk, n *Node, v *pipelines.IngestionPipelineDefinitionFanoutOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.FanoutBy == "" && Forced(v.ForceSendFields, "FanoutBy") { + ch := n.Child("fanout_by") + c.fn(&ch, "") + } + if s := string(v.FanoutBy); c.match(s) { + ch := n.Child("fanout_by") + if r, ok := c.fn(&ch, s); ok { + v.FanoutBy = (r) + } + } + if !n.IsRef("transforms") { + if len(v.Transforms) > 0 { + cn := n.Child("transforms") + for i := range v.Transforms { + { + ch := cn.Elem(i) + stringsPipelinesTransformer(c, &ch, &v.Transforms[i]) + } + } + } + } +} + +func stringsPipelinesPostgresCatalogConfig(c *stringsWalk, n *Node, v *pipelines.PostgresCatalogConfig) { + if n.isRef() { + return + } + if !n.IsRef("slot_config") { + if x := v.SlotConfig; x != nil { + ch := n.Child("slot_config") + stringsPipelinesPostgresSlotConfig(c, &ch, x) + } + } +} + +func stringsPipelinesIngestionPipelineDefinitionWorkdayReportParametersQueryKeyValue(c *stringsWalk, n *Node, v *pipelines.IngestionPipelineDefinitionWorkdayReportParametersQueryKeyValue) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Key == "" && Forced(v.ForceSendFields, "Key") { + ch := n.Child("key") + c.fn(&ch, "") + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } +} + +func stringsMlFieldDefinition(c *stringsWalk, n *Node, v *ml.FieldDefinition) { + if n.isRef() { + return + } + if s := string(v.DataType); c.match(s) { + ch := n.Child("data_type") + if r, ok := c.fn(&ch, s); ok { + v.DataType = ml.ScalarDataType(r) + } + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsServingAiGatewayGuardrailPiiBehavior(c *stringsWalk, n *Node, v *serving.AiGatewayGuardrailPiiBehavior) { + if n.isRef() { + return + } + if s := string(v.Behavior); c.match(s) { + ch := n.Child("behavior") + if r, ok := c.fn(&ch, s); ok { + v.Behavior = serving.AiGatewayGuardrailPiiBehaviorBehavior(r) + } + } +} + +func stringsServingAi21LabsConfig(c *stringsWalk, n *Node, v *serving.Ai21LabsConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Ai21labsApiKey == "" && Forced(v.ForceSendFields, "Ai21labsApiKey") { + ch := n.Child("ai21labs_api_key") + c.fn(&ch, "") + } + if s := string(v.Ai21labsApiKey); c.match(s) { + ch := n.Child("ai21labs_api_key") + if r, ok := c.fn(&ch, s); ok { + v.Ai21labsApiKey = (r) + } + } + if c.mode == EmptyFields && v.Ai21labsApiKeyPlaintext == "" && Forced(v.ForceSendFields, "Ai21labsApiKeyPlaintext") { + ch := n.Child("ai21labs_api_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.Ai21labsApiKeyPlaintext); c.match(s) { + ch := n.Child("ai21labs_api_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.Ai21labsApiKeyPlaintext = (r) + } + } +} + +func stringsServingAmazonBedrockConfig(c *stringsWalk, n *Node, v *serving.AmazonBedrockConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AwsAccessKeyId == "" && Forced(v.ForceSendFields, "AwsAccessKeyId") { + ch := n.Child("aws_access_key_id") + c.fn(&ch, "") + } + if s := string(v.AwsAccessKeyId); c.match(s) { + ch := n.Child("aws_access_key_id") + if r, ok := c.fn(&ch, s); ok { + v.AwsAccessKeyId = (r) + } + } + if c.mode == EmptyFields && v.AwsAccessKeyIdPlaintext == "" && Forced(v.ForceSendFields, "AwsAccessKeyIdPlaintext") { + ch := n.Child("aws_access_key_id_plaintext") + c.fn(&ch, "") + } + if s := string(v.AwsAccessKeyIdPlaintext); c.match(s) { + ch := n.Child("aws_access_key_id_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.AwsAccessKeyIdPlaintext = (r) + } + } + if s := string(v.AwsRegion); c.match(s) { + ch := n.Child("aws_region") + if r, ok := c.fn(&ch, s); ok { + v.AwsRegion = (r) + } + } + if c.mode == EmptyFields && v.AwsSecretAccessKey == "" && Forced(v.ForceSendFields, "AwsSecretAccessKey") { + ch := n.Child("aws_secret_access_key") + c.fn(&ch, "") + } + if s := string(v.AwsSecretAccessKey); c.match(s) { + ch := n.Child("aws_secret_access_key") + if r, ok := c.fn(&ch, s); ok { + v.AwsSecretAccessKey = (r) + } + } + if c.mode == EmptyFields && v.AwsSecretAccessKeyPlaintext == "" && Forced(v.ForceSendFields, "AwsSecretAccessKeyPlaintext") { + ch := n.Child("aws_secret_access_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.AwsSecretAccessKeyPlaintext); c.match(s) { + ch := n.Child("aws_secret_access_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.AwsSecretAccessKeyPlaintext = (r) + } + } + if s := string(v.BedrockProvider); c.match(s) { + ch := n.Child("bedrock_provider") + if r, ok := c.fn(&ch, s); ok { + v.BedrockProvider = serving.AmazonBedrockConfigBedrockProvider(r) + } + } + if c.mode == EmptyFields && v.InstanceProfileArn == "" && Forced(v.ForceSendFields, "InstanceProfileArn") { + ch := n.Child("instance_profile_arn") + c.fn(&ch, "") + } + if s := string(v.InstanceProfileArn); c.match(s) { + ch := n.Child("instance_profile_arn") + if r, ok := c.fn(&ch, s); ok { + v.InstanceProfileArn = (r) + } + } + if c.mode == EmptyFields && v.UcServiceCredentialName == "" && Forced(v.ForceSendFields, "UcServiceCredentialName") { + ch := n.Child("uc_service_credential_name") + c.fn(&ch, "") + } + if s := string(v.UcServiceCredentialName); c.match(s) { + ch := n.Child("uc_service_credential_name") + if r, ok := c.fn(&ch, s); ok { + v.UcServiceCredentialName = (r) + } + } +} + +func stringsServingAnthropicConfig(c *stringsWalk, n *Node, v *serving.AnthropicConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AnthropicApiKey == "" && Forced(v.ForceSendFields, "AnthropicApiKey") { + ch := n.Child("anthropic_api_key") + c.fn(&ch, "") + } + if s := string(v.AnthropicApiKey); c.match(s) { + ch := n.Child("anthropic_api_key") + if r, ok := c.fn(&ch, s); ok { + v.AnthropicApiKey = (r) + } + } + if c.mode == EmptyFields && v.AnthropicApiKeyPlaintext == "" && Forced(v.ForceSendFields, "AnthropicApiKeyPlaintext") { + ch := n.Child("anthropic_api_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.AnthropicApiKeyPlaintext); c.match(s) { + ch := n.Child("anthropic_api_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.AnthropicApiKeyPlaintext = (r) + } + } +} + +func stringsServingCohereConfig(c *stringsWalk, n *Node, v *serving.CohereConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CohereApiBase == "" && Forced(v.ForceSendFields, "CohereApiBase") { + ch := n.Child("cohere_api_base") + c.fn(&ch, "") + } + if s := string(v.CohereApiBase); c.match(s) { + ch := n.Child("cohere_api_base") + if r, ok := c.fn(&ch, s); ok { + v.CohereApiBase = (r) + } + } + if c.mode == EmptyFields && v.CohereApiKey == "" && Forced(v.ForceSendFields, "CohereApiKey") { + ch := n.Child("cohere_api_key") + c.fn(&ch, "") + } + if s := string(v.CohereApiKey); c.match(s) { + ch := n.Child("cohere_api_key") + if r, ok := c.fn(&ch, s); ok { + v.CohereApiKey = (r) + } + } + if c.mode == EmptyFields && v.CohereApiKeyPlaintext == "" && Forced(v.ForceSendFields, "CohereApiKeyPlaintext") { + ch := n.Child("cohere_api_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.CohereApiKeyPlaintext); c.match(s) { + ch := n.Child("cohere_api_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.CohereApiKeyPlaintext = (r) + } + } +} + +func stringsServingCustomProviderConfig(c *stringsWalk, n *Node, v *serving.CustomProviderConfig) { + if n.isRef() { + return + } + if !n.IsRef("api_key_auth") { + if x := v.ApiKeyAuth; x != nil { + ch := n.Child("api_key_auth") + stringsServingApiKeyAuth(c, &ch, x) + } + } + if !n.IsRef("bearer_token_auth") { + if x := v.BearerTokenAuth; x != nil { + ch := n.Child("bearer_token_auth") + stringsServingBearerTokenAuth(c, &ch, x) + } + } + if s := string(v.CustomProviderUrl); c.match(s) { + ch := n.Child("custom_provider_url") + if r, ok := c.fn(&ch, s); ok { + v.CustomProviderUrl = (r) + } + } +} + +func stringsServingDatabricksModelServingConfig(c *stringsWalk, n *Node, v *serving.DatabricksModelServingConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DatabricksApiToken == "" && Forced(v.ForceSendFields, "DatabricksApiToken") { + ch := n.Child("databricks_api_token") + c.fn(&ch, "") + } + if s := string(v.DatabricksApiToken); c.match(s) { + ch := n.Child("databricks_api_token") + if r, ok := c.fn(&ch, s); ok { + v.DatabricksApiToken = (r) + } + } + if c.mode == EmptyFields && v.DatabricksApiTokenPlaintext == "" && Forced(v.ForceSendFields, "DatabricksApiTokenPlaintext") { + ch := n.Child("databricks_api_token_plaintext") + c.fn(&ch, "") + } + if s := string(v.DatabricksApiTokenPlaintext); c.match(s) { + ch := n.Child("databricks_api_token_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.DatabricksApiTokenPlaintext = (r) + } + } + if s := string(v.DatabricksWorkspaceUrl); c.match(s) { + ch := n.Child("databricks_workspace_url") + if r, ok := c.fn(&ch, s); ok { + v.DatabricksWorkspaceUrl = (r) + } + } +} + +func stringsServingGoogleCloudVertexAiConfig(c *stringsWalk, n *Node, v *serving.GoogleCloudVertexAiConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.PrivateKey == "" && Forced(v.ForceSendFields, "PrivateKey") { + ch := n.Child("private_key") + c.fn(&ch, "") + } + if s := string(v.PrivateKey); c.match(s) { + ch := n.Child("private_key") + if r, ok := c.fn(&ch, s); ok { + v.PrivateKey = (r) + } + } + if c.mode == EmptyFields && v.PrivateKeyPlaintext == "" && Forced(v.ForceSendFields, "PrivateKeyPlaintext") { + ch := n.Child("private_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.PrivateKeyPlaintext); c.match(s) { + ch := n.Child("private_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.PrivateKeyPlaintext = (r) + } + } + if s := string(v.ProjectId); c.match(s) { + ch := n.Child("project_id") + if r, ok := c.fn(&ch, s); ok { + v.ProjectId = (r) + } + } + if s := string(v.Region); c.match(s) { + ch := n.Child("region") + if r, ok := c.fn(&ch, s); ok { + v.Region = (r) + } + } +} + +func stringsServingOpenAiConfig(c *stringsWalk, n *Node, v *serving.OpenAiConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.MicrosoftEntraClientId == "" && Forced(v.ForceSendFields, "MicrosoftEntraClientId") { + ch := n.Child("microsoft_entra_client_id") + c.fn(&ch, "") + } + if s := string(v.MicrosoftEntraClientId); c.match(s) { + ch := n.Child("microsoft_entra_client_id") + if r, ok := c.fn(&ch, s); ok { + v.MicrosoftEntraClientId = (r) + } + } + if c.mode == EmptyFields && v.MicrosoftEntraClientSecret == "" && Forced(v.ForceSendFields, "MicrosoftEntraClientSecret") { + ch := n.Child("microsoft_entra_client_secret") + c.fn(&ch, "") + } + if s := string(v.MicrosoftEntraClientSecret); c.match(s) { + ch := n.Child("microsoft_entra_client_secret") + if r, ok := c.fn(&ch, s); ok { + v.MicrosoftEntraClientSecret = (r) + } + } + if c.mode == EmptyFields && v.MicrosoftEntraClientSecretPlaintext == "" && Forced(v.ForceSendFields, "MicrosoftEntraClientSecretPlaintext") { + ch := n.Child("microsoft_entra_client_secret_plaintext") + c.fn(&ch, "") + } + if s := string(v.MicrosoftEntraClientSecretPlaintext); c.match(s) { + ch := n.Child("microsoft_entra_client_secret_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.MicrosoftEntraClientSecretPlaintext = (r) + } + } + if c.mode == EmptyFields && v.MicrosoftEntraTenantId == "" && Forced(v.ForceSendFields, "MicrosoftEntraTenantId") { + ch := n.Child("microsoft_entra_tenant_id") + c.fn(&ch, "") + } + if s := string(v.MicrosoftEntraTenantId); c.match(s) { + ch := n.Child("microsoft_entra_tenant_id") + if r, ok := c.fn(&ch, s); ok { + v.MicrosoftEntraTenantId = (r) + } + } + if c.mode == EmptyFields && v.OpenaiApiBase == "" && Forced(v.ForceSendFields, "OpenaiApiBase") { + ch := n.Child("openai_api_base") + c.fn(&ch, "") + } + if s := string(v.OpenaiApiBase); c.match(s) { + ch := n.Child("openai_api_base") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiApiBase = (r) + } + } + if c.mode == EmptyFields && v.OpenaiApiKey == "" && Forced(v.ForceSendFields, "OpenaiApiKey") { + ch := n.Child("openai_api_key") + c.fn(&ch, "") + } + if s := string(v.OpenaiApiKey); c.match(s) { + ch := n.Child("openai_api_key") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiApiKey = (r) + } + } + if c.mode == EmptyFields && v.OpenaiApiKeyPlaintext == "" && Forced(v.ForceSendFields, "OpenaiApiKeyPlaintext") { + ch := n.Child("openai_api_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.OpenaiApiKeyPlaintext); c.match(s) { + ch := n.Child("openai_api_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiApiKeyPlaintext = (r) + } + } + if c.mode == EmptyFields && v.OpenaiApiType == "" && Forced(v.ForceSendFields, "OpenaiApiType") { + ch := n.Child("openai_api_type") + c.fn(&ch, "") + } + if s := string(v.OpenaiApiType); c.match(s) { + ch := n.Child("openai_api_type") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiApiType = (r) + } + } + if c.mode == EmptyFields && v.OpenaiApiVersion == "" && Forced(v.ForceSendFields, "OpenaiApiVersion") { + ch := n.Child("openai_api_version") + c.fn(&ch, "") + } + if s := string(v.OpenaiApiVersion); c.match(s) { + ch := n.Child("openai_api_version") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiApiVersion = (r) + } + } + if c.mode == EmptyFields && v.OpenaiDeploymentName == "" && Forced(v.ForceSendFields, "OpenaiDeploymentName") { + ch := n.Child("openai_deployment_name") + c.fn(&ch, "") + } + if s := string(v.OpenaiDeploymentName); c.match(s) { + ch := n.Child("openai_deployment_name") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiDeploymentName = (r) + } + } + if c.mode == EmptyFields && v.OpenaiOrganization == "" && Forced(v.ForceSendFields, "OpenaiOrganization") { + ch := n.Child("openai_organization") + c.fn(&ch, "") + } + if s := string(v.OpenaiOrganization); c.match(s) { + ch := n.Child("openai_organization") + if r, ok := c.fn(&ch, s); ok { + v.OpenaiOrganization = (r) + } + } +} + +func stringsServingPaLmConfig(c *stringsWalk, n *Node, v *serving.PaLmConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.PalmApiKey == "" && Forced(v.ForceSendFields, "PalmApiKey") { + ch := n.Child("palm_api_key") + c.fn(&ch, "") + } + if s := string(v.PalmApiKey); c.match(s) { + ch := n.Child("palm_api_key") + if r, ok := c.fn(&ch, s); ok { + v.PalmApiKey = (r) + } + } + if c.mode == EmptyFields && v.PalmApiKeyPlaintext == "" && Forced(v.ForceSendFields, "PalmApiKeyPlaintext") { + ch := n.Child("palm_api_key_plaintext") + c.fn(&ch, "") + } + if s := string(v.PalmApiKeyPlaintext); c.match(s) { + ch := n.Child("palm_api_key_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.PalmApiKeyPlaintext = (r) + } + } +} + +func stringsCatalogModelServiceConfigExternalModelConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfigExternalModelConfig) { + if n.isRef() { + return + } + if s := string(v.ModelProviderService); c.match(s) { + ch := n.Child("model_provider_service") + if r, ok := c.fn(&ch, s); ok { + v.ModelProviderService = (r) + } + } + if !n.IsRef("target") { + { + ch := n.Child("target") + stringsCatalogModelProviderServiceConfigModelTargetConfig(c, &ch, &v.Target) + } + } +} + +func stringsCatalogModelServiceConfigPayPerTokenConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfigPayPerTokenConfig) { + if n.isRef() { + return + } + if s := string(v.Model); c.match(s) { + ch := n.Child("model") + if r, ok := c.fn(&ch, s); ok { + v.Model = (r) + } + } +} + +func stringsCatalogModelServiceConfigProvisionedThroughputConfig(c *stringsWalk, n *Node, v *catalog.ModelServiceConfigProvisionedThroughputConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Model == "" && Forced(v.ForceSendFields, "Model") { + ch := n.Child("model") + c.fn(&ch, "") + } + if s := string(v.Model); c.match(s) { + ch := n.Child("model") + if r, ok := c.fn(&ch, s); ok { + v.Model = (r) + } + } + if s := string(v.ModelServingEndpoint); c.match(s) { + ch := n.Child("model_serving_endpoint") + if r, ok := c.fn(&ch, s); ok { + v.ModelServingEndpoint = (r) + } + } +} + +func stringsCatalogModelProviderServiceConfigAwsAccessKey(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigAwsAccessKey) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AccessKeyId == "" && Forced(v.ForceSendFields, "AccessKeyId") { + ch := n.Child("access_key_id") + c.fn(&ch, "") + } + if s := string(v.AccessKeyId); c.match(s) { + ch := n.Child("access_key_id") + if r, ok := c.fn(&ch, s); ok { + v.AccessKeyId = (r) + } + } + if !n.IsRef("secret_access_key") { + if x := v.SecretAccessKey; x != nil { + ch := n.Child("secret_access_key") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigServiceCredential(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigServiceCredential) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsCatalogModelProviderServiceConfigProviderSecret(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigProviderSecret) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Plaintext == "" && Forced(v.ForceSendFields, "Plaintext") { + ch := n.Child("plaintext") + c.fn(&ch, "") + } + if s := string(v.Plaintext); c.match(s) { + ch := n.Child("plaintext") + if r, ok := c.fn(&ch, s); ok { + v.Plaintext = (r) + } + } + if !n.IsRef("secret_reference") { + if x := v.SecretReference; x != nil { + ch := n.Child("secret_reference") + stringsCatalogModelProviderServiceConfigSecretReference(c, &ch, x) + } + } +} + +func stringsCatalogModelProviderServiceConfigEntraServicePrincipal(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigEntraServicePrincipal) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ClientId == "" && Forced(v.ForceSendFields, "ClientId") { + ch := n.Child("client_id") + c.fn(&ch, "") + } + if s := string(v.ClientId); c.match(s) { + ch := n.Child("client_id") + if r, ok := c.fn(&ch, s); ok { + v.ClientId = (r) + } + } + if !n.IsRef("client_secret") { + if x := v.ClientSecret; x != nil { + ch := n.Child("client_secret") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } + if c.mode == EmptyFields && v.TenantId == "" && Forced(v.ForceSendFields, "TenantId") { + ch := n.Child("tenant_id") + c.fn(&ch, "") + } + if s := string(v.TenantId); c.match(s) { + ch := n.Child("tenant_id") + if r, ok := c.fn(&ch, s); ok { + v.TenantId = (r) + } + } +} + +func stringsCatalogModelProviderServiceConfigCustomProviderApiKeyHeaderAuth(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigCustomProviderApiKeyHeaderAuth) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ApiKeyName == "" && Forced(v.ForceSendFields, "ApiKeyName") { + ch := n.Child("api_key_name") + c.fn(&ch, "") + } + if s := string(v.ApiKeyName); c.match(s) { + ch := n.Child("api_key_name") + if r, ok := c.fn(&ch, s); ok { + v.ApiKeyName = (r) + } + } + if !n.IsRef("api_key_value") { + if x := v.ApiKeyValue; x != nil { + ch := n.Child("api_key_value") + stringsCatalogModelProviderServiceConfigProviderSecret(c, &ch, x) + } + } +} + +func stringsJobsSubscriptionAttachmentDataAttachment(c *stringsWalk, n *Node, v *jobs.SubscriptionAttachmentDataAttachment) { + if n.isRef() { + return + } + if s := string(v.FileFormat); c.match(s) { + ch := n.Child("file_format") + if r, ok := c.fn(&ch, s); ok { + v.FileFormat = jobs.SubscriptionAttachmentDataAttachmentFileFormat(r) + } + } + if !n.IsRef("widgets") { + if len(v.Widgets) > 0 { + cn := n.Child("widgets") + for i := range v.Widgets { + { + ch := cn.Elem(i) + stringsJobsSubscriptionAttachmentDataAttachmentWidget(c, &ch, &v.Widgets[i]) + } + } + } + } +} + +func stringsJobsSubscriptionAttachmentPageAttachment(c *stringsWalk, n *Node, v *jobs.SubscriptionAttachmentPageAttachment) { + if n.isRef() { + return + } + if !n.IsRef("page_ids") { + if len(v.PageIds) > 0 { + cn := n.Child("page_ids") + for i := range v.PageIds { + if s := string(v.PageIds[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.PageIds[i] = (r) + } + } + } + } + } + if s := string(v.PageSelection); c.match(s) { + ch := n.Child("page_selection") + if r, ok := c.fn(&ch, s); ok { + v.PageSelection = jobs.SubscriptionAttachmentPageAttachmentPageSelection(r) + } + } +} + +func stringsPipelinesApiSourceConnectorOptions(c *stringsWalk, n *Node, v *pipelines.ApiSourceConnectorOptions) { + if n.isRef() { + return + } + if !n.IsRef("options") { + if len(v.Options) > 0 { + cn := n.Child("options") + for _, k := range sortedKeys(v.Options) { + if s := string(v.Options[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.Options[k] = (r) + } + } + } + } + } +} + +func stringsPipelinesConfluenceConnectorOptions(c *stringsWalk, n *Node, v *pipelines.ConfluenceConnectorOptions) { + if n.isRef() { + return + } + if !n.IsRef("include_confluence_spaces") { + if len(v.IncludeConfluenceSpaces) > 0 { + cn := n.Child("include_confluence_spaces") + for i := range v.IncludeConfluenceSpaces { + if s := string(v.IncludeConfluenceSpaces[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeConfluenceSpaces[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesGoogleDriveOptions(c *stringsWalk, n *Node, v *pipelines.GoogleDriveOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.EntityType == "" && Forced(v.ForceSendFields, "EntityType") { + ch := n.Child("entity_type") + c.fn(&ch, "") + } + if s := string(v.EntityType); c.match(s) { + ch := n.Child("entity_type") + if r, ok := c.fn(&ch, s); ok { + v.EntityType = pipelines.GoogleDriveOptionsGoogleDriveEntityType(r) + } + } + if !n.IsRef("file_ingestion_options") { + if x := v.FileIngestionOptions; x != nil { + ch := n.Child("file_ingestion_options") + stringsPipelinesFileIngestionOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Url == "" && Forced(v.ForceSendFields, "Url") { + ch := n.Child("url") + c.fn(&ch, "") + } + if s := string(v.Url); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.Url = (r) + } + } +} + +func stringsPipelinesGoogleAdsOptions(c *stringsWalk, n *Node, v *pipelines.GoogleAdsOptions) { + if n.isRef() { + return + } + if !n.IsRef("custom_report_options") { + if x := v.CustomReportOptions; x != nil { + ch := n.Child("custom_report_options") + stringsPipelinesGoogleAdsCustomReportOptions(c, &ch, x) + } + } + if s := string(v.ManagerAccountId); c.match(s) { + ch := n.Child("manager_account_id") + if r, ok := c.fn(&ch, s); ok { + v.ManagerAccountId = (r) + } + } + if c.mode == EmptyFields && v.SyncStartDate == "" && Forced(v.ForceSendFields, "SyncStartDate") { + ch := n.Child("sync_start_date") + c.fn(&ch, "") + } + if s := string(v.SyncStartDate); c.match(s) { + ch := n.Child("sync_start_date") + if r, ok := c.fn(&ch, s); ok { + v.SyncStartDate = (r) + } + } +} + +func stringsPipelinesJiraConnectorOptions(c *stringsWalk, n *Node, v *pipelines.JiraConnectorOptions) { + if n.isRef() { + return + } + if !n.IsRef("include_jira_spaces") { + if len(v.IncludeJiraSpaces) > 0 { + cn := n.Child("include_jira_spaces") + for i := range v.IncludeJiraSpaces { + if s := string(v.IncludeJiraSpaces[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeJiraSpaces[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesKafkaOptions(c *stringsWalk, n *Node, v *pipelines.KafkaOptions) { + if n.isRef() { + return + } + if !n.IsRef("client_config") { + if len(v.ClientConfig) > 0 { + cn := n.Child("client_config") + for _, k := range sortedKeys(v.ClientConfig) { + if s := string(v.ClientConfig[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.ClientConfig[k] = (r) + } + } + } + } + } + if !n.IsRef("key_transformer") { + if x := v.KeyTransformer; x != nil { + ch := n.Child("key_transformer") + stringsPipelinesTransformer(c, &ch, x) + } + } + if c.mode == EmptyFields && v.StartingOffset == "" && Forced(v.ForceSendFields, "StartingOffset") { + ch := n.Child("starting_offset") + c.fn(&ch, "") + } + if s := string(v.StartingOffset); c.match(s) { + ch := n.Child("starting_offset") + if r, ok := c.fn(&ch, s); ok { + v.StartingOffset = (r) + } + } + if c.mode == EmptyFields && v.TopicPattern == "" && Forced(v.ForceSendFields, "TopicPattern") { + ch := n.Child("topic_pattern") + c.fn(&ch, "") + } + if s := string(v.TopicPattern); c.match(s) { + ch := n.Child("topic_pattern") + if r, ok := c.fn(&ch, s); ok { + v.TopicPattern = (r) + } + } + if !n.IsRef("topics") { + if len(v.Topics) > 0 { + cn := n.Child("topics") + for i := range v.Topics { + if s := string(v.Topics[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Topics[i] = (r) + } + } + } + } + } + if !n.IsRef("value_transformer") { + if x := v.ValueTransformer; x != nil { + ch := n.Child("value_transformer") + stringsPipelinesTransformer(c, &ch, x) + } + } +} + +func stringsPipelinesLinkedInAdsOptions(c *stringsWalk, n *Node, v *pipelines.LinkedInAdsOptions) { + if n.isRef() { + return + } + if !n.IsRef("custom_report_options") { + if x := v.CustomReportOptions; x != nil { + ch := n.Child("custom_report_options") + stringsPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SyncStartDate == "" && Forced(v.ForceSendFields, "SyncStartDate") { + ch := n.Child("sync_start_date") + c.fn(&ch, "") + } + if s := string(v.SyncStartDate); c.match(s) { + ch := n.Child("sync_start_date") + if r, ok := c.fn(&ch, s); ok { + v.SyncStartDate = (r) + } + } +} + +func stringsPipelinesMarketoOptions(c *stringsWalk, n *Node, v *pipelines.MarketoOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.SyncStartDate == "" && Forced(v.ForceSendFields, "SyncStartDate") { + ch := n.Child("sync_start_date") + c.fn(&ch, "") + } + if s := string(v.SyncStartDate); c.match(s) { + ch := n.Child("sync_start_date") + if r, ok := c.fn(&ch, s); ok { + v.SyncStartDate = (r) + } + } +} + +func stringsPipelinesMetaMarketingOptions(c *stringsWalk, n *Node, v *pipelines.MetaMarketingOptions) { + if n.isRef() { + return + } + if !n.IsRef("action_attribution_windows") { + if len(v.ActionAttributionWindows) > 0 { + cn := n.Child("action_attribution_windows") + for i := range v.ActionAttributionWindows { + if s := string(v.ActionAttributionWindows[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ActionAttributionWindows[i] = (r) + } + } + } + } + } + if !n.IsRef("action_breakdowns") { + if len(v.ActionBreakdowns) > 0 { + cn := n.Child("action_breakdowns") + for i := range v.ActionBreakdowns { + if s := string(v.ActionBreakdowns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ActionBreakdowns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ActionReportTime == "" && Forced(v.ForceSendFields, "ActionReportTime") { + ch := n.Child("action_report_time") + c.fn(&ch, "") + } + if s := string(v.ActionReportTime); c.match(s) { + ch := n.Child("action_report_time") + if r, ok := c.fn(&ch, s); ok { + v.ActionReportTime = (r) + } + } + if !n.IsRef("breakdowns") { + if len(v.Breakdowns) > 0 { + cn := n.Child("breakdowns") + for i := range v.Breakdowns { + if s := string(v.Breakdowns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Breakdowns[i] = (r) + } + } + } + } + } + if !n.IsRef("custom_report_options") { + if x := v.CustomReportOptions; x != nil { + ch := n.Child("custom_report_options") + stringsPipelinesMetaMarketingOptionsMetaMarketingCustomReportOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Level == "" && Forced(v.ForceSendFields, "Level") { + ch := n.Child("level") + c.fn(&ch, "") + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = (r) + } + } + if c.mode == EmptyFields && v.StartDate == "" && Forced(v.ForceSendFields, "StartDate") { + ch := n.Child("start_date") + c.fn(&ch, "") + } + if s := string(v.StartDate); c.match(s) { + ch := n.Child("start_date") + if r, ok := c.fn(&ch, s); ok { + v.StartDate = (r) + } + } + if c.mode == EmptyFields && v.TimeIncrement == "" && Forced(v.ForceSendFields, "TimeIncrement") { + ch := n.Child("time_increment") + c.fn(&ch, "") + } + if s := string(v.TimeIncrement); c.match(s) { + ch := n.Child("time_increment") + if r, ok := c.fn(&ch, s); ok { + v.TimeIncrement = (r) + } + } +} + +func stringsPipelinesOutlookOptions(c *stringsWalk, n *Node, v *pipelines.OutlookOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.AttachmentMode == "" && Forced(v.ForceSendFields, "AttachmentMode") { + ch := n.Child("attachment_mode") + c.fn(&ch, "") + } + if s := string(v.AttachmentMode); c.match(s) { + ch := n.Child("attachment_mode") + if r, ok := c.fn(&ch, s); ok { + v.AttachmentMode = pipelines.OutlookAttachmentMode(r) + } + } + if c.mode == EmptyFields && v.BodyFormat == "" && Forced(v.ForceSendFields, "BodyFormat") { + ch := n.Child("body_format") + c.fn(&ch, "") + } + if s := string(v.BodyFormat); c.match(s) { + ch := n.Child("body_format") + if r, ok := c.fn(&ch, s); ok { + v.BodyFormat = pipelines.OutlookBodyFormat(r) + } + } + if !n.IsRef("folder_filter") { + if len(v.FolderFilter) > 0 { + cn := n.Child("folder_filter") + for i := range v.FolderFilter { + if s := string(v.FolderFilter[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.FolderFilter[i] = (r) + } + } + } + } + } + if !n.IsRef("include_folders") { + if len(v.IncludeFolders) > 0 { + cn := n.Child("include_folders") + for i := range v.IncludeFolders { + if s := string(v.IncludeFolders[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeFolders[i] = (r) + } + } + } + } + } + if !n.IsRef("include_mailboxes") { + if len(v.IncludeMailboxes) > 0 { + cn := n.Child("include_mailboxes") + for i := range v.IncludeMailboxes { + if s := string(v.IncludeMailboxes[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeMailboxes[i] = (r) + } + } + } + } + } + if !n.IsRef("include_senders") { + if len(v.IncludeSenders) > 0 { + cn := n.Child("include_senders") + for i := range v.IncludeSenders { + if s := string(v.IncludeSenders[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeSenders[i] = (r) + } + } + } + } + } + if !n.IsRef("include_subjects") { + if len(v.IncludeSubjects) > 0 { + cn := n.Child("include_subjects") + for i := range v.IncludeSubjects { + if s := string(v.IncludeSubjects[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.IncludeSubjects[i] = (r) + } + } + } + } + } + if !n.IsRef("sender_filter") { + if len(v.SenderFilter) > 0 { + cn := n.Child("sender_filter") + for i := range v.SenderFilter { + if s := string(v.SenderFilter[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.SenderFilter[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.StartDate == "" && Forced(v.ForceSendFields, "StartDate") { + ch := n.Child("start_date") + c.fn(&ch, "") + } + if s := string(v.StartDate); c.match(s) { + ch := n.Child("start_date") + if r, ok := c.fn(&ch, s); ok { + v.StartDate = (r) + } + } + if !n.IsRef("subject_filter") { + if len(v.SubjectFilter) > 0 { + cn := n.Child("subject_filter") + for i := range v.SubjectFilter { + if s := string(v.SubjectFilter[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.SubjectFilter[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesRabbitmqOptions(c *stringsWalk, n *Node, v *pipelines.RabbitmqOptions) { + if n.isRef() { + return + } + if s := string(v.Queue); c.match(s) { + ch := n.Child("queue") + if r, ok := c.fn(&ch, s); ok { + v.Queue = (r) + } + } +} + +func stringsPipelinesRedditAdsOptions(c *stringsWalk, n *Node, v *pipelines.RedditAdsOptions) { + if n.isRef() { + return + } + if !n.IsRef("custom_report_options") { + if x := v.CustomReportOptions; x != nil { + ch := n.Child("custom_report_options") + stringsPipelinesRedditAdsOptionsRedditAdsCustomReportOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.SyncStartDate == "" && Forced(v.ForceSendFields, "SyncStartDate") { + ch := n.Child("sync_start_date") + c.fn(&ch, "") + } + if s := string(v.SyncStartDate); c.match(s) { + ch := n.Child("sync_start_date") + if r, ok := c.fn(&ch, s); ok { + v.SyncStartDate = (r) + } + } +} + +func stringsPipelinesSharepointOptions(c *stringsWalk, n *Node, v *pipelines.SharepointOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.EntityType == "" && Forced(v.ForceSendFields, "EntityType") { + ch := n.Child("entity_type") + c.fn(&ch, "") + } + if s := string(v.EntityType); c.match(s) { + ch := n.Child("entity_type") + if r, ok := c.fn(&ch, s); ok { + v.EntityType = pipelines.SharepointOptionsSharepointEntityType(r) + } + } + if !n.IsRef("file_ingestion_options") { + if x := v.FileIngestionOptions; x != nil { + ch := n.Child("file_ingestion_options") + stringsPipelinesFileIngestionOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Url == "" && Forced(v.ForceSendFields, "Url") { + ch := n.Child("url") + c.fn(&ch, "") + } + if s := string(v.Url); c.match(s) { + ch := n.Child("url") + if r, ok := c.fn(&ch, s); ok { + v.Url = (r) + } + } +} + +func stringsPipelinesTikTokAdsOptions(c *stringsWalk, n *Node, v *pipelines.TikTokAdsOptions) { + if n.isRef() { + return + } + if !n.IsRef("custom_report_options") { + if x := v.CustomReportOptions; x != nil { + ch := n.Child("custom_report_options") + stringsPipelinesTikTokAdsOptionsTikTokAdsCustomReportOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.DataLevel == "" && Forced(v.ForceSendFields, "DataLevel") { + ch := n.Child("data_level") + c.fn(&ch, "") + } + if s := string(v.DataLevel); c.match(s) { + ch := n.Child("data_level") + if r, ok := c.fn(&ch, s); ok { + v.DataLevel = pipelines.TikTokAdsOptionsTikTokDataLevel(r) + } + } + if !n.IsRef("dimensions") { + if len(v.Dimensions) > 0 { + cn := n.Child("dimensions") + for i := range v.Dimensions { + if s := string(v.Dimensions[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Dimensions[i] = (r) + } + } + } + } + } + if !n.IsRef("metrics") { + if len(v.Metrics) > 0 { + cn := n.Child("metrics") + for i := range v.Metrics { + if s := string(v.Metrics[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Metrics[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ReportType == "" && Forced(v.ForceSendFields, "ReportType") { + ch := n.Child("report_type") + c.fn(&ch, "") + } + if s := string(v.ReportType); c.match(s) { + ch := n.Child("report_type") + if r, ok := c.fn(&ch, s); ok { + v.ReportType = pipelines.TikTokAdsOptionsTikTokReportType(r) + } + } + if c.mode == EmptyFields && v.SyncStartDate == "" && Forced(v.ForceSendFields, "SyncStartDate") { + ch := n.Child("sync_start_date") + c.fn(&ch, "") + } + if s := string(v.SyncStartDate); c.match(s) { + ch := n.Child("sync_start_date") + if r, ok := c.fn(&ch, s); ok { + v.SyncStartDate = (r) + } + } +} + +func stringsPipelinesZendeskSupportOptions(c *stringsWalk, n *Node, v *pipelines.ZendeskSupportOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.StartDate == "" && Forced(v.ForceSendFields, "StartDate") { + ch := n.Child("start_date") + c.fn(&ch, "") + } + if s := string(v.StartDate); c.match(s) { + ch := n.Child("start_date") + if r, ok := c.fn(&ch, s); ok { + v.StartDate = (r) + } + } +} + +func stringsPipelinesTransformer(c *stringsWalk, n *Node, v *pipelines.Transformer) { + if n.isRef() { + return + } + if !n.IsRef("avro_options") { + if x := v.AvroOptions; x != nil { + ch := n.Child("avro_options") + stringsPipelinesAvroTransformerOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.Format == "" && Forced(v.ForceSendFields, "Format") { + ch := n.Child("format") + c.fn(&ch, "") + } + if s := string(v.Format); c.match(s) { + ch := n.Child("format") + if r, ok := c.fn(&ch, s); ok { + v.Format = pipelines.TransformerFormat(r) + } + } + if c.mode == EmptyFields && v.InputColumn == "" && Forced(v.ForceSendFields, "InputColumn") { + ch := n.Child("input_column") + c.fn(&ch, "") + } + if s := string(v.InputColumn); c.match(s) { + ch := n.Child("input_column") + if r, ok := c.fn(&ch, s); ok { + v.InputColumn = (r) + } + } + if !n.IsRef("json_options") { + if x := v.JsonOptions; x != nil { + ch := n.Child("json_options") + stringsPipelinesJsonTransformerOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.OutputColumn == "" && Forced(v.ForceSendFields, "OutputColumn") { + ch := n.Child("output_column") + c.fn(&ch, "") + } + if s := string(v.OutputColumn); c.match(s) { + ch := n.Child("output_column") + if r, ok := c.fn(&ch, s); ok { + v.OutputColumn = (r) + } + } + if !n.IsRef("protobuf_options") { + if x := v.ProtobufOptions; x != nil { + ch := n.Child("protobuf_options") + stringsPipelinesProtobufTransformerOptions(c, &ch, x) + } + } +} + +func stringsPipelinesPostgresSlotConfig(c *stringsWalk, n *Node, v *pipelines.PostgresSlotConfig) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.PublicationName == "" && Forced(v.ForceSendFields, "PublicationName") { + ch := n.Child("publication_name") + c.fn(&ch, "") + } + if s := string(v.PublicationName); c.match(s) { + ch := n.Child("publication_name") + if r, ok := c.fn(&ch, s); ok { + v.PublicationName = (r) + } + } + if c.mode == EmptyFields && v.SlotName == "" && Forced(v.ForceSendFields, "SlotName") { + ch := n.Child("slot_name") + c.fn(&ch, "") + } + if s := string(v.SlotName); c.match(s) { + ch := n.Child("slot_name") + if r, ok := c.fn(&ch, s); ok { + v.SlotName = (r) + } + } +} + +func stringsServingApiKeyAuth(c *stringsWalk, n *Node, v *serving.ApiKeyAuth) { + if n.isRef() { + return + } + if s := string(v.Key); c.match(s) { + ch := n.Child("key") + if r, ok := c.fn(&ch, s); ok { + v.Key = (r) + } + } + if c.mode == EmptyFields && v.Value == "" && Forced(v.ForceSendFields, "Value") { + ch := n.Child("value") + c.fn(&ch, "") + } + if s := string(v.Value); c.match(s) { + ch := n.Child("value") + if r, ok := c.fn(&ch, s); ok { + v.Value = (r) + } + } + if c.mode == EmptyFields && v.ValuePlaintext == "" && Forced(v.ForceSendFields, "ValuePlaintext") { + ch := n.Child("value_plaintext") + c.fn(&ch, "") + } + if s := string(v.ValuePlaintext); c.match(s) { + ch := n.Child("value_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.ValuePlaintext = (r) + } + } +} + +func stringsServingBearerTokenAuth(c *stringsWalk, n *Node, v *serving.BearerTokenAuth) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Token == "" && Forced(v.ForceSendFields, "Token") { + ch := n.Child("token") + c.fn(&ch, "") + } + if s := string(v.Token); c.match(s) { + ch := n.Child("token") + if r, ok := c.fn(&ch, s); ok { + v.Token = (r) + } + } + if c.mode == EmptyFields && v.TokenPlaintext == "" && Forced(v.ForceSendFields, "TokenPlaintext") { + ch := n.Child("token_plaintext") + c.fn(&ch, "") + } + if s := string(v.TokenPlaintext); c.match(s) { + ch := n.Child("token_plaintext") + if r, ok := c.fn(&ch, s); ok { + v.TokenPlaintext = (r) + } + } +} + +func stringsCatalogModelProviderServiceConfigSecretReference(c *stringsWalk, n *Node, v *catalog.ModelProviderServiceConfigSecretReference) { + if n.isRef() { + return + } + if s := string(v.Name); c.match(s) { + ch := n.Child("name") + if r, ok := c.fn(&ch, s); ok { + v.Name = (r) + } + } +} + +func stringsJobsSubscriptionAttachmentDataAttachmentWidget(c *stringsWalk, n *Node, v *jobs.SubscriptionAttachmentDataAttachmentWidget) { + if n.isRef() { + return + } + if s := string(v.PageId); c.match(s) { + ch := n.Child("page_id") + if r, ok := c.fn(&ch, s); ok { + v.PageId = (r) + } + } + if s := string(v.WidgetId); c.match(s) { + ch := n.Child("widget_id") + if r, ok := c.fn(&ch, s); ok { + v.WidgetId = (r) + } + } +} + +func stringsPipelinesFileIngestionOptions(c *stringsWalk, n *Node, v *pipelines.FileIngestionOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.CorruptRecordColumn == "" && Forced(v.ForceSendFields, "CorruptRecordColumn") { + ch := n.Child("corrupt_record_column") + c.fn(&ch, "") + } + if s := string(v.CorruptRecordColumn); c.match(s) { + ch := n.Child("corrupt_record_column") + if r, ok := c.fn(&ch, s); ok { + v.CorruptRecordColumn = (r) + } + } + if !n.IsRef("file_filters") { + if len(v.FileFilters) > 0 { + cn := n.Child("file_filters") + for i := range v.FileFilters { + { + ch := cn.Elem(i) + stringsPipelinesFileFilter(c, &ch, &v.FileFilters[i]) + } + } + } + } + if c.mode == EmptyFields && v.Format == "" && Forced(v.ForceSendFields, "Format") { + ch := n.Child("format") + c.fn(&ch, "") + } + if s := string(v.Format); c.match(s) { + ch := n.Child("format") + if r, ok := c.fn(&ch, s); ok { + v.Format = pipelines.FileIngestionOptionsFileFormat(r) + } + } + if !n.IsRef("format_options") { + if len(v.FormatOptions) > 0 { + cn := n.Child("format_options") + for _, k := range sortedKeys(v.FormatOptions) { + if s := string(v.FormatOptions[k]); c.match(s) { + ch := cn.Child(string(k)) + if r, ok := c.fn(&ch, s); ok { + v.FormatOptions[k] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.RescuedDataColumn == "" && Forced(v.ForceSendFields, "RescuedDataColumn") { + ch := n.Child("rescued_data_column") + c.fn(&ch, "") + } + if s := string(v.RescuedDataColumn); c.match(s) { + ch := n.Child("rescued_data_column") + if r, ok := c.fn(&ch, s); ok { + v.RescuedDataColumn = (r) + } + } + if c.mode == EmptyFields && v.SchemaEvolutionMode == "" && Forced(v.ForceSendFields, "SchemaEvolutionMode") { + ch := n.Child("schema_evolution_mode") + c.fn(&ch, "") + } + if s := string(v.SchemaEvolutionMode); c.match(s) { + ch := n.Child("schema_evolution_mode") + if r, ok := c.fn(&ch, s); ok { + v.SchemaEvolutionMode = pipelines.FileIngestionOptionsSchemaEvolutionMode(r) + } + } + if c.mode == EmptyFields && v.SchemaHints == "" && Forced(v.ForceSendFields, "SchemaHints") { + ch := n.Child("schema_hints") + c.fn(&ch, "") + } + if s := string(v.SchemaHints); c.match(s) { + ch := n.Child("schema_hints") + if r, ok := c.fn(&ch, s); ok { + v.SchemaHints = (r) + } + } + if c.mode == EmptyFields && v.SingleVariantColumn == "" && Forced(v.ForceSendFields, "SingleVariantColumn") { + ch := n.Child("single_variant_column") + c.fn(&ch, "") + } + if s := string(v.SingleVariantColumn); c.match(s) { + ch := n.Child("single_variant_column") + if r, ok := c.fn(&ch, s); ok { + v.SingleVariantColumn = (r) + } + } +} + +func stringsPipelinesGoogleAdsCustomReportOptions(c *stringsWalk, n *Node, v *pipelines.GoogleAdsCustomReportOptions) { + if n.isRef() { + return + } + if !n.IsRef("metrics") { + if len(v.Metrics) > 0 { + cn := n.Child("metrics") + for i := range v.Metrics { + if s := string(v.Metrics[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Metrics[i] = (r) + } + } + } + } + } + if s := string(v.Resource); c.match(s) { + ch := n.Child("resource") + if r, ok := c.fn(&ch, s); ok { + v.Resource = (r) + } + } + if !n.IsRef("resource_fields") { + if len(v.ResourceFields) > 0 { + cn := n.Child("resource_fields") + for i := range v.ResourceFields { + if s := string(v.ResourceFields[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ResourceFields[i] = (r) + } + } + } + } + } + if !n.IsRef("segments") { + if len(v.Segments) > 0 { + cn := n.Child("segments") + for i := range v.Segments { + if s := string(v.Segments[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Segments[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesLinkedInAdsOptionsLinkedInAdsCustomReportOptions(c *stringsWalk, n *Node, v *pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptions) { + if n.isRef() { + return + } + if !n.IsRef("entity_granularity") { + if len(v.EntityGranularity) > 0 { + cn := n.Child("entity_granularity") + for i := range v.EntityGranularity { + if s := string(v.EntityGranularity[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.EntityGranularity[i] = pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsEntityGranularity(r) + } + } + } + } + } + if s := string(v.Finder); c.match(s) { + ch := n.Child("finder") + if r, ok := c.fn(&ch, s); ok { + v.Finder = pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsFinder(r) + } + } + if !n.IsRef("metrics") { + if len(v.Metrics) > 0 { + cn := n.Child("metrics") + for i := range v.Metrics { + if s := string(v.Metrics[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Metrics[i] = (r) + } + } + } + } + } + if s := string(v.TimeGranularity); c.match(s) { + ch := n.Child("time_granularity") + if r, ok := c.fn(&ch, s); ok { + v.TimeGranularity = pipelines.LinkedInAdsOptionsLinkedInAdsCustomReportOptionsLinkedInAdsTimeGranularity(r) + } + } +} + +func stringsPipelinesMetaMarketingOptionsMetaMarketingCustomReportOptions(c *stringsWalk, n *Node, v *pipelines.MetaMarketingOptionsMetaMarketingCustomReportOptions) { + if n.isRef() { + return + } + if !n.IsRef("action_attribution_windows") { + if len(v.ActionAttributionWindows) > 0 { + cn := n.Child("action_attribution_windows") + for i := range v.ActionAttributionWindows { + if s := string(v.ActionAttributionWindows[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ActionAttributionWindows[i] = (r) + } + } + } + } + } + if !n.IsRef("action_breakdowns") { + if len(v.ActionBreakdowns) > 0 { + cn := n.Child("action_breakdowns") + for i := range v.ActionBreakdowns { + if s := string(v.ActionBreakdowns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.ActionBreakdowns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ActionReportTime == "" && Forced(v.ForceSendFields, "ActionReportTime") { + ch := n.Child("action_report_time") + c.fn(&ch, "") + } + if s := string(v.ActionReportTime); c.match(s) { + ch := n.Child("action_report_time") + if r, ok := c.fn(&ch, s); ok { + v.ActionReportTime = (r) + } + } + if !n.IsRef("breakdowns") { + if len(v.Breakdowns) > 0 { + cn := n.Child("breakdowns") + for i := range v.Breakdowns { + if s := string(v.Breakdowns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Breakdowns[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.Level == "" && Forced(v.ForceSendFields, "Level") { + ch := n.Child("level") + c.fn(&ch, "") + } + if s := string(v.Level); c.match(s) { + ch := n.Child("level") + if r, ok := c.fn(&ch, s); ok { + v.Level = (r) + } + } + if c.mode == EmptyFields && v.TimeIncrement == "" && Forced(v.ForceSendFields, "TimeIncrement") { + ch := n.Child("time_increment") + c.fn(&ch, "") + } + if s := string(v.TimeIncrement); c.match(s) { + ch := n.Child("time_increment") + if r, ok := c.fn(&ch, s); ok { + v.TimeIncrement = (r) + } + } +} + +func stringsPipelinesRedditAdsOptionsRedditAdsCustomReportOptions(c *stringsWalk, n *Node, v *pipelines.RedditAdsOptionsRedditAdsCustomReportOptions) { + if n.isRef() { + return + } + if !n.IsRef("breakdowns") { + if len(v.Breakdowns) > 0 { + cn := n.Child("breakdowns") + for i := range v.Breakdowns { + if s := string(v.Breakdowns[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Breakdowns[i] = (r) + } + } + } + } + } + if !n.IsRef("fields") { + if len(v.Fields) > 0 { + cn := n.Child("fields") + for i := range v.Fields { + if s := string(v.Fields[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Fields[i] = (r) + } + } + } + } + } +} + +func stringsPipelinesTikTokAdsOptionsTikTokAdsCustomReportOptions(c *stringsWalk, n *Node, v *pipelines.TikTokAdsOptionsTikTokAdsCustomReportOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DataLevel == "" && Forced(v.ForceSendFields, "DataLevel") { + ch := n.Child("data_level") + c.fn(&ch, "") + } + if s := string(v.DataLevel); c.match(s) { + ch := n.Child("data_level") + if r, ok := c.fn(&ch, s); ok { + v.DataLevel = pipelines.TikTokAdsOptionsTikTokDataLevel(r) + } + } + if !n.IsRef("dimensions") { + if len(v.Dimensions) > 0 { + cn := n.Child("dimensions") + for i := range v.Dimensions { + if s := string(v.Dimensions[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Dimensions[i] = (r) + } + } + } + } + } + if !n.IsRef("metrics") { + if len(v.Metrics) > 0 { + cn := n.Child("metrics") + for i := range v.Metrics { + if s := string(v.Metrics[i]); c.match(s) { + ch := cn.Elem(i) + if r, ok := c.fn(&ch, s); ok { + v.Metrics[i] = (r) + } + } + } + } + } + if c.mode == EmptyFields && v.ReportType == "" && Forced(v.ForceSendFields, "ReportType") { + ch := n.Child("report_type") + c.fn(&ch, "") + } + if s := string(v.ReportType); c.match(s) { + ch := n.Child("report_type") + if r, ok := c.fn(&ch, s); ok { + v.ReportType = pipelines.TikTokAdsOptionsTikTokReportType(r) + } + } +} + +func stringsPipelinesAvroTransformerOptions(c *stringsWalk, n *Node, v *pipelines.AvroTransformerOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ParseMode == "" && Forced(v.ForceSendFields, "ParseMode") { + ch := n.Child("parse_mode") + c.fn(&ch, "") + } + if s := string(v.ParseMode); c.match(s) { + ch := n.Child("parse_mode") + if r, ok := c.fn(&ch, s); ok { + v.ParseMode = pipelines.ParseMode(r) + } + } + if c.mode == EmptyFields && v.Schema == "" && Forced(v.ForceSendFields, "Schema") { + ch := n.Child("schema") + c.fn(&ch, "") + } + if s := string(v.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.Schema = (r) + } + } + if c.mode == EmptyFields && v.SchemaFilePath == "" && Forced(v.ForceSendFields, "SchemaFilePath") { + ch := n.Child("schema_file_path") + c.fn(&ch, "") + } + if s := string(v.SchemaFilePath); c.match(s) { + ch := n.Child("schema_file_path") + if r, ok := c.fn(&ch, s); ok { + v.SchemaFilePath = (r) + } + } + if !n.IsRef("schema_registry") { + if x := v.SchemaRegistry; x != nil { + ch := n.Child("schema_registry") + stringsPipelinesSchemaRegistryConfig(c, &ch, x) + } + } +} + +func stringsPipelinesJsonTransformerOptions(c *stringsWalk, n *Node, v *pipelines.JsonTransformerOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Schema == "" && Forced(v.ForceSendFields, "Schema") { + ch := n.Child("schema") + c.fn(&ch, "") + } + if s := string(v.Schema); c.match(s) { + ch := n.Child("schema") + if r, ok := c.fn(&ch, s); ok { + v.Schema = (r) + } + } + if c.mode == EmptyFields && v.SchemaEvolutionMode == "" && Forced(v.ForceSendFields, "SchemaEvolutionMode") { + ch := n.Child("schema_evolution_mode") + c.fn(&ch, "") + } + if s := string(v.SchemaEvolutionMode); c.match(s) { + ch := n.Child("schema_evolution_mode") + if r, ok := c.fn(&ch, s); ok { + v.SchemaEvolutionMode = pipelines.FileIngestionOptionsSchemaEvolutionMode(r) + } + } + if c.mode == EmptyFields && v.SchemaFilePath == "" && Forced(v.ForceSendFields, "SchemaFilePath") { + ch := n.Child("schema_file_path") + c.fn(&ch, "") + } + if s := string(v.SchemaFilePath); c.match(s) { + ch := n.Child("schema_file_path") + if r, ok := c.fn(&ch, s); ok { + v.SchemaFilePath = (r) + } + } + if c.mode == EmptyFields && v.SchemaHints == "" && Forced(v.ForceSendFields, "SchemaHints") { + ch := n.Child("schema_hints") + c.fn(&ch, "") + } + if s := string(v.SchemaHints); c.match(s) { + ch := n.Child("schema_hints") + if r, ok := c.fn(&ch, s); ok { + v.SchemaHints = (r) + } + } +} + +func stringsPipelinesProtobufTransformerOptions(c *stringsWalk, n *Node, v *pipelines.ProtobufTransformerOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.DescFilePath == "" && Forced(v.ForceSendFields, "DescFilePath") { + ch := n.Child("desc_file_path") + c.fn(&ch, "") + } + if s := string(v.DescFilePath); c.match(s) { + ch := n.Child("desc_file_path") + if r, ok := c.fn(&ch, s); ok { + v.DescFilePath = (r) + } + } + if c.mode == EmptyFields && v.MessageName == "" && Forced(v.ForceSendFields, "MessageName") { + ch := n.Child("message_name") + c.fn(&ch, "") + } + if s := string(v.MessageName); c.match(s) { + ch := n.Child("message_name") + if r, ok := c.fn(&ch, s); ok { + v.MessageName = (r) + } + } + if c.mode == EmptyFields && v.ParseMode == "" && Forced(v.ForceSendFields, "ParseMode") { + ch := n.Child("parse_mode") + c.fn(&ch, "") + } + if s := string(v.ParseMode); c.match(s) { + ch := n.Child("parse_mode") + if r, ok := c.fn(&ch, s); ok { + v.ParseMode = pipelines.ParseMode(r) + } + } + if !n.IsRef("schema_registry") { + if x := v.SchemaRegistry; x != nil { + ch := n.Child("schema_registry") + stringsPipelinesSchemaRegistryConfig(c, &ch, x) + } + } +} + +func stringsPipelinesFileFilter(c *stringsWalk, n *Node, v *pipelines.FileFilter) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.ModifiedAfter == "" && Forced(v.ForceSendFields, "ModifiedAfter") { + ch := n.Child("modified_after") + c.fn(&ch, "") + } + if s := string(v.ModifiedAfter); c.match(s) { + ch := n.Child("modified_after") + if r, ok := c.fn(&ch, s); ok { + v.ModifiedAfter = (r) + } + } + if c.mode == EmptyFields && v.ModifiedBefore == "" && Forced(v.ForceSendFields, "ModifiedBefore") { + ch := n.Child("modified_before") + c.fn(&ch, "") + } + if s := string(v.ModifiedBefore); c.match(s) { + ch := n.Child("modified_before") + if r, ok := c.fn(&ch, s); ok { + v.ModifiedBefore = (r) + } + } + if c.mode == EmptyFields && v.PathFilter == "" && Forced(v.ForceSendFields, "PathFilter") { + ch := n.Child("path_filter") + c.fn(&ch, "") + } + if s := string(v.PathFilter); c.match(s) { + ch := n.Child("path_filter") + if r, ok := c.fn(&ch, s); ok { + v.PathFilter = (r) + } + } +} + +func stringsPipelinesSchemaRegistryConfig(c *stringsWalk, n *Node, v *pipelines.SchemaRegistryConfig) { + if n.isRef() { + return + } + if !n.IsRef("confluent_options") { + if x := v.ConfluentOptions; x != nil { + ch := n.Child("confluent_options") + stringsPipelinesConfluentSchemaRegistryOptions(c, &ch, x) + } + } + if c.mode == EmptyFields && v.ConnectionName == "" && Forced(v.ForceSendFields, "ConnectionName") { + ch := n.Child("connection_name") + c.fn(&ch, "") + } + if s := string(v.ConnectionName); c.match(s) { + ch := n.Child("connection_name") + if r, ok := c.fn(&ch, s); ok { + v.ConnectionName = (r) + } + } + if c.mode == EmptyFields && v.ProtobufMessageName == "" && Forced(v.ForceSendFields, "ProtobufMessageName") { + ch := n.Child("protobuf_message_name") + c.fn(&ch, "") + } + if s := string(v.ProtobufMessageName); c.match(s) { + ch := n.Child("protobuf_message_name") + if r, ok := c.fn(&ch, s); ok { + v.ProtobufMessageName = (r) + } + } +} + +func stringsPipelinesConfluentSchemaRegistryOptions(c *stringsWalk, n *Node, v *pipelines.ConfluentSchemaRegistryOptions) { + if n.isRef() { + return + } + if c.mode == EmptyFields && v.Subject == "" && Forced(v.ForceSendFields, "Subject") { + ch := n.Child("subject") + c.fn(&ch, "") + } + if s := string(v.Subject); c.match(s) { + ch := n.Child("subject") + if r, ok := c.fn(&ch, s); ok { + v.Subject = (r) + } + } +} + +func stringsResource(c *stringsWalk, n *Node, v *config.Resources, typ, name string) { + switch typ { + case "jobs": + if x := v.Jobs[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesJob(c, &rn, x) + } + case "job_runs": + if x := v.JobRuns[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesJobRun(c, &rn, x) + } + case "pipelines": + if x := v.Pipelines[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPipeline(c, &rn, x) + } + case "features": + if x := v.Features[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesFeature(c, &rn, x) + } + case "models": + if x := v.Models[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesMlflowModel(c, &rn, x) + } + case "experiments": + if x := v.Experiments[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesMlflowExperiment(c, &rn, x) + } + case "model_serving_endpoints": + if x := v.ModelServingEndpoints[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesModelServingEndpoint(c, &rn, x) + } + case "model_services": + if x := v.ModelServices[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesModelService(c, &rn, x) + } + case "mcp_services": + if x := v.McpServices[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesMcpService(c, &rn, x) + } + case "model_provider_services": + if x := v.ModelProviderServices[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesModelProviderService(c, &rn, x) + } + case "registered_models": + if x := v.RegisteredModels[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesRegisteredModel(c, &rn, x) + } + case "quality_monitors": + if x := v.QualityMonitors[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesQualityMonitor(c, &rn, x) + } + case "catalogs": + if x := v.Catalogs[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesCatalog(c, &rn, x) + } + case "schemas": + if x := v.Schemas[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesSchema(c, &rn, x) + } + case "volumes": + if x := v.Volumes[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesVolume(c, &rn, x) + } + case "external_locations": + if x := v.ExternalLocations[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesExternalLocation(c, &rn, x) + } + case "clusters": + if x := v.Clusters[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesCluster(c, &rn, x) + } + case "dashboards": + if x := v.Dashboards[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesDashboard(c, &rn, x) + } + case "genie_spaces": + if x := v.GenieSpaces[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesGenieSpace(c, &rn, x) + } + case "apps": + if x := v.Apps[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesApp(c, &rn, x) + } + case "secret_scopes": + if x := v.SecretScopes[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesSecretScope(c, &rn, x) + } + case "alerts": + if x := v.Alerts[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesAlert(c, &rn, x) + } + case "sql_warehouses": + if x := v.SqlWarehouses[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesSqlWarehouse(c, &rn, x) + } + case "database_instances": + if x := v.DatabaseInstances[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesDatabaseInstance(c, &rn, x) + } + case "database_catalogs": + if x := v.DatabaseCatalogs[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesDatabaseCatalog(c, &rn, x) + } + case "synced_database_tables": + if x := v.SyncedDatabaseTables[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesSyncedDatabaseTable(c, &rn, x) + } + case "postgres_projects": + if x := v.PostgresProjects[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresProject(c, &rn, x) + } + case "postgres_branches": + if x := v.PostgresBranches[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresBranch(c, &rn, x) + } + case "postgres_endpoints": + if x := v.PostgresEndpoints[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresEndpoint(c, &rn, x) + } + case "postgres_catalogs": + if x := v.PostgresCatalogs[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresCatalog(c, &rn, x) + } + case "postgres_databases": + if x := v.PostgresDatabases[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresDatabase(c, &rn, x) + } + case "postgres_roles": + if x := v.PostgresRoles[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresRole(c, &rn, x) + } + case "postgres_synced_tables": + if x := v.PostgresSyncedTables[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresSyncedTable(c, &rn, x) + } + case "postgres_snapshot_schedules": + if x := v.PostgresSnapshotSchedules[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesPostgresSnapshotSchedule(c, &rn, x) + } + case "vector_search_endpoints": + if x := v.VectorSearchEndpoints[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesVectorSearchEndpoint(c, &rn, x) + } + case "vector_search_indexes": + if x := v.VectorSearchIndexes[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesVectorSearchIndex(c, &rn, x) + } + case "instance_pools": + if x := v.InstancePools[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesInstancePool(c, &rn, x) + } + case "secrets": + if x := v.Secrets[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesSecret(c, &rn, x) + } + case "cluster_policies": + if x := v.ClusterPolicies[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesClusterPolicy(c, &rn, x) + } + case "internal_immutable_snapshots": + if x := v.Snapshots[name]; x != nil { + ch := n.Child(typ) + rn := ch.Child(name) + stringsResourcesSnapshot(c, &rn, x) + } + } +} diff --git a/bundle/config/workspace.go b/bundle/config/workspace.go index e1448dc7212..682b54fd6f6 100644 --- a/bundle/config/workspace.go +++ b/bundle/config/workspace.go @@ -203,8 +203,11 @@ func (w *Workspace) NormalizeHostURL() { func (w *Workspace) Client(ctx context.Context) (*databricks.WorkspaceClient, error) { // Extract query parameters (?o=, ?a=) from the host URL before building // the SDK config. This ensures workspace_id and account_id are available - // for profile resolution during EnsureResolved(). - w.NormalizeHostURL() + // for profile resolution during EnsureResolved(). A copy is normalized: the + // configuration keeps the host as it is written. + normalized := *w + normalized.NormalizeHostURL() + w = &normalized cfg := w.Config(ctx) diff --git a/bundle/configsync/diff.go b/bundle/configsync/diff.go index e42f0a1d83a..aeb593afe50 100644 --- a/bundle/configsync/diff.go +++ b/bundle/configsync/diff.go @@ -8,10 +8,9 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/deployplan" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structdiff" + "github.com/databricks/cli/libs/structs/structvar" ) type OperationType string @@ -39,13 +38,8 @@ type ResourceChanges map[string]*ConfigChangeDesc type Changes map[string]ResourceChanges -func normalizeValue(v any) (any, error) { - dynValue, err := convert.FromTyped(v, dyn.NilValue) - if err != nil { - return nil, fmt.Errorf("failed to convert value of type %T: %w", v, err) - } - - return dynValue.AsAny(), nil +func normalizeValue(v any) any { + return structvar.NewView(v, nil, nil).AsAny() } func filterEntityDefaults(basePath string, value any) any { @@ -92,10 +86,7 @@ func convertChangeDesc(path string, cd *deployplan.ChangeDesc) (*ConfigChangeDes // cd.Old in this check would classify the change as Replace and fail later in // resolveSelectors because the old key no longer exists in the YAML. hasConfigValue := cd.New != nil - normalizedValue, err := normalizeValue(cd.Remote) - if err != nil { - return nil, fmt.Errorf("failed to normalize remote value: %w", err) - } + normalizedValue := normalizeValue(cd.Remote) if shouldSkipField(path, normalizedValue, hasConfigValue) { return &ConfigChangeDesc{ @@ -129,11 +120,7 @@ func convertChangeDesc(path string, cd *deployplan.ChangeDesc) (*ConfigChangeDes // which keys on the path component (index 3), so a resource literally named // "permissions" ("resources.jobs.permissions") is not misclassified. func isPermissionsOrGrantsSubResource(resourceKey string) bool { - path, err := dyn.NewPathFromString(resourceKey) - if err != nil { - return false - } - _, nodeType := config.GetNodeAndType(path) + nodeType := config.GetResourceTypeFromKey(resourceKey) return strings.HasSuffix(nodeType, ".permissions") || strings.HasSuffix(nodeType, ".grants") } diff --git a/bundle/configsync/patch_test.go b/bundle/configsync/patch_test.go index ef0030f95f8..998acc0877f 100644 --- a/bundle/configsync/patch_test.go +++ b/bundle/configsync/patch_test.go @@ -40,6 +40,7 @@ resources: require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) changes := Changes{ "resources.jobs.test_job": ResourceChanges{ diff --git a/bundle/configsync/resolve.go b/bundle/configsync/resolve.go index cdad5182336..e97c0d5e98c 100644 --- a/bundle/configsync/resolve.go +++ b/bundle/configsync/resolve.go @@ -12,10 +12,10 @@ import ( "strings" "github.com/databricks/cli/bundle" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/notebook" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) type FieldChange struct { @@ -42,7 +42,7 @@ type resolvedChange struct { path *structpath.PatternNode steps []sequenceStep // leaf is the merged value the change addresses, invalid for a new field. - leaf dyn.Value + leaf structvar.View // operation decides how many destinations the change needs: a removal has to // reach every definition, an edit only the one that wins the merge. operation OperationType @@ -62,28 +62,23 @@ func resolveSelectors(pathStr string, b *bundle.Bundle, operation OperationType) nodes := node.AsSlice() var result *structpath.PatternNode var steps []sequenceStep - var currentPath dyn.Path - currentValue := b.Config.Value() + var currentPath *structpath.PathNode + currentValue := b.Config.View() for component, n := range nodes { if key, ok := n.StringKey(); ok { result = structpath.NewPatternStringKey(result, key) - currentPath = append(currentPath, dyn.Key(key)) - if currentValue.IsValid() { - currentValue, _ = dyn.GetByPath(currentValue, dyn.Path{dyn.Key(key)}) - } + currentPath = structpath.NewStringKey(currentPath, key) + currentValue = currentValue.Get(key) continue } if idx, ok := n.Index(); ok { - sequencePath := slices.Clone(currentPath) + sequencePath := currentPath result = structpath.NewPatternIndex(result, idx) - currentPath = append(currentPath, dyn.Index(idx)) - var element dyn.Value - if currentValue.IsValid() { - element, _ = dyn.GetByPath(currentValue, dyn.Path{dyn.Index(idx)}) - currentValue = element - } + currentPath = structpath.NewIndex(currentPath, idx) + element := currentValue.Index(idx) + currentValue = element steps = append(steps, sequenceStep{ component: component, sequencePath: sequencePath, @@ -98,26 +93,22 @@ func resolveSelectors(pathStr string, b *bundle.Bundle, operation OperationType) // Check for key-value selector: [key='value'] if key, value, ok := n.KeyValue(); ok { - if !currentValue.IsValid() || currentValue.Kind() != dyn.KindSequence { + if !currentValue.IsValid() || currentValue.Kind() != structvar.KindSequence { return resolvedChange{}, fmt.Errorf("cannot apply [%s='%s'] selector to non-array value in path %s", key, value, pathStr) } - seq, _ := currentValue.AsSequence() foundIndex := -1 + var foundElement structvar.View - for i, elem := range seq { - keyValue, err := dyn.GetByPath(elem, dyn.Path{dyn.Key(key)}) - if err != nil { - continue - } - - if keyValue.Kind() == dyn.KindString && keyValue.MustString() == value { + for i, elem := range currentValue.Sequence() { + if keyValue, ok := elem.Get(key).AsString(); ok && keyValue == value { foundIndex = i + foundElement = elem break } } - sequencePath := slices.Clone(currentPath) + sequencePath := currentPath if foundIndex == -1 { if operation == OperationAdd { @@ -128,20 +119,20 @@ func resolveSelectors(pathStr string, b *bundle.Bundle, operation OperationType) newElement: true, }) // Can't navigate further into non-existent element - currentValue = dyn.Value{} + currentValue = structvar.View{} continue } return resolvedChange{}, fmt.Errorf("no array element found with %s='%s' in path %s", key, value, pathStr) } result = structpath.NewPatternIndex(result, foundIndex) - currentPath = append(currentPath, dyn.Index(foundIndex)) + currentPath = structpath.NewIndex(currentPath, foundIndex) steps = append(steps, sequenceStep{ component: component, sequencePath: sequencePath, - element: seq[foundIndex], + element: foundElement, }) - currentValue = seq[foundIndex] + currentValue = foundElement continue } } @@ -200,7 +191,7 @@ func adjustArrayIndex(path *structpath.PatternNode, scope string, operations map // disappears looks identical to one that was written. preResolved is the merged // config with ${...} references still literal, used to detect variable-reference // parents. -func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes, preResolved dyn.Value) ([]FieldChange, int, error) { +func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes, preResolved structvar.View) ([]FieldChange, int, error) { var result []FieldChange skipped := 0 targetName := b.Config.Bundle.Target @@ -360,8 +351,11 @@ func ResolveChanges(ctx context.Context, b *bundle.Bundle, configChanges Changes // on it belongs in that same block. filePath = block.file } else { - resourceLocation := b.Config.GetLocation(resourceKey) - filePath = resourceLocation.File + resourcePath, err := structpath.ParsePath(resourceKey) + if err != nil { + return nil, 0, fmt.Errorf("failed to parse resource key %s: %w", resourceKey, err) + } + filePath = b.Config.GetLocationOfExact(resourcePath).File } if filePath == "" { return nil, 0, fmt.Errorf("failed to find location for resource %s for a field %s", resourceKey, fieldPath) diff --git a/bundle/configsync/resolve_test.go b/bundle/configsync/resolve_test.go index 9d85fc51119..bd458f3347a 100644 --- a/bundle/configsync/resolve_test.go +++ b/bundle/configsync/resolve_test.go @@ -30,6 +30,7 @@ func TestResolveSelectors_NoSelectors(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) result, err := resolveSelectors("resources.jobs.test_job.name", b, OperationReplace) require.NoError(t, err) @@ -55,6 +56,7 @@ func TestResolveSelectors_NumericIndices(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) result, err := resolveSelectors("resources.jobs.test_job.tasks[0].task_key", b, OperationReplace) require.NoError(t, err) @@ -88,6 +90,7 @@ func TestResolveSelectors_KeyValueSelector(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) result, err := resolveSelectors("resources.jobs.test_job.tasks[task_key='main'].notebook_task.notebook_path", b, OperationReplace) require.NoError(t, err) @@ -118,6 +121,7 @@ func TestResolveSelectors_SelectorNotFound(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) _, err = resolveSelectors("resources.jobs.test_job.tasks[task_key='nonexistent'].notebook_task.notebook_path", b, OperationReplace) require.Error(t, err) @@ -141,6 +145,7 @@ func TestResolveSelectors_SelectorOnNonArray(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) _, err = resolveSelectors("resources.jobs.test_job[task_key='main'].name", b, OperationReplace) require.Error(t, err) @@ -172,6 +177,7 @@ func TestResolveSelectors_NestedSelectors(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) result, err := resolveSelectors("resources.jobs.test_job.tasks[task_key='main'].libraries[0].pypi.package", b, OperationReplace) require.NoError(t, err) @@ -198,6 +204,7 @@ func TestResolveSelectors_WildcardNotSupported(t *testing.T) { require.NoError(t, err) mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) _, err = resolveSelectors("resources.jobs.test_job.tasks.*.task_key", b, OperationReplace) require.Error(t, err) diff --git a/bundle/configsync/sequences.go b/bundle/configsync/sequences.go index 4ce0274fdb3..3a9bd84dd1e 100644 --- a/bundle/configsync/sequences.go +++ b/bundle/configsync/sequences.go @@ -12,9 +12,9 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) // Write-back has to turn a position in the merged configuration into a position in a @@ -56,13 +56,13 @@ func (b sourceBlock) scopeKey() string { } // blockResolver answers which physical block a merged value came from, by location: -// merging accumulates them (libs/dyn/merge), so a value assembled from two blocks +// merging accumulates them, so a value assembled from two blocks // reports a location in each. Selecting a target folds its overrides into resources and // drops the targets subtree, so the blocks are recovered by parsing the files again. type blockResolver struct { // blocks holds one parsed file per contributing file, keyed by block, so a // path relative to a block can be looked up inside it. - blocks map[sourceBlock]dyn.Value + blocks map[sourceBlock]structvar.View target string byLocation map[diag.Location]sourceBlock } @@ -74,13 +74,10 @@ type blockResolver struct { // The contributing files are parsed directly rather than reloaded through the mutator // pipeline, which would run the bundle's preinit script a second time. func newBlockResolver(ctx context.Context, b *bundle.Bundle) *blockResolver { - root := b.Config.Value() - if !root.IsValid() { - return nil - } + root := b.Config.View() r := &blockResolver{ - blocks: make(map[sourceBlock]dyn.Value), + blocks: make(map[sourceBlock]structvar.View), target: b.Config.Bundle.Target, byLocation: make(map[diag.Location]sourceBlock), } @@ -92,14 +89,15 @@ func newBlockResolver(ctx context.Context, b *bundle.Bundle) *blockResolver { log.Debugf(ctx, "config-remote-sync: cannot read %s, treating its sequences as unsplit: %v", file, err) continue } - parsed, diags := config.LoadFromBytes(file, contents) + loaded, diags := config.LoadFromBytes(file, contents) if diags.HasError() { log.Debugf(ctx, "config-remote-sync: cannot parse %s, treating its sequences as unsplit: %v", file, diags.Error()) continue } - r.registerBlock(parsed.Value(), sourceBlock{file: file}) + parsed := loaded.View() + r.registerBlock(parsed, sourceBlock{file: file}) if r.target != "" { - r.registerBlock(parsed.Value(), sourceBlock{override: true, file: file}) + r.registerBlock(parsed, sourceBlock{override: true, file: file}) } } @@ -109,9 +107,9 @@ func newBlockResolver(ctx context.Context, b *bundle.Bundle) *blockResolver { return r } -func referencedFiles(root dyn.Value) map[string]struct{} { +func referencedFiles(root structvar.View) map[string]struct{} { files := map[string]struct{}{} - _ = dyn.WalkReadOnly(root, func(_ dyn.Path, v dyn.Value) error { + _ = structvar.Walk(root, func(_ *structpath.PathNode, v structvar.View) error { for _, location := range v.Locations() { if location.File != "" { files[location.File] = struct{}{} @@ -126,16 +124,16 @@ func referencedFiles(root dyn.Value) map[string]struct{} { // resources subtree back to it, so a merged value can later be traced to the // region it was written in. Does nothing when the file has no such region, which // is why r.blocks doubles as the set of blocks that exist. -func (r *blockResolver) registerBlock(parsed dyn.Value, block sourceBlock) { - subtree, err := dyn.GetByPath(parsed, r.regionPath(block, dyn.NewPath(dyn.Key("resources")))) - if err != nil { +func (r *blockResolver) registerBlock(parsed structvar.View, block sourceBlock) { + subtree := parsed.Lookup(r.regionPath(block, structpath.NewStringKey(nil, "resources"))) + if !subtree.IsValid() { return } // Keep the parsed file: resolving a path inside this block needs the tree it // came from, and the entry also marks the block as present for sortedBlocks. r.blocks[block] = parsed - _ = dyn.WalkReadOnly(subtree, func(_ dyn.Path, v dyn.Value) error { + _ = structvar.Walk(subtree, func(_ *structpath.PathNode, v structvar.View) error { for _, location := range v.Locations() { // A file only carries locations of its own, so a location seen here // belongs to this block. First writer wins: an outer node accumulates @@ -153,11 +151,11 @@ func (r *blockResolver) registerBlock(parsed dyn.Value, block sourceBlock) { } // regionPath prefixes a resources-relative path with the region it belongs to. -func (r *blockResolver) regionPath(block sourceBlock, path dyn.Path) dyn.Path { +func (r *blockResolver) regionPath(block sourceBlock, path *structpath.PathNode) *structpath.PathNode { if !block.override { return path } - return append(dyn.NewPath(dyn.Key("targets"), dyn.Key(r.target)), path...) + return structpath.Join(structpath.NewPath(nil, "targets", r.target), path.AsSlice()...) } // candidatePath renders a resolved path the way the patch layer addresses it, @@ -211,7 +209,7 @@ func compareBlocks(a, b sourceBlock) int { // blocksOf returns the distinct blocks that contributed to value, sorted with the // top-level block first. More than one result means the value is assembled from // several regions and has no single source location. -func (r *blockResolver) blocksOf(value dyn.Value) []sourceBlock { +func (r *blockResolver) blocksOf(value structvar.View) []sourceBlock { var blocks []sourceBlock for _, location := range value.Locations() { block, ok := r.byLocation[location] @@ -233,7 +231,7 @@ func (r *blockResolver) blocksOf(value dyn.Value) []sourceBlock { // Locations accumulate in merge order, so the first one that maps to a block is the // winner and writing any other copy would leave the effective value unchanged. Load // order is the only thing that distinguishes two blocks in the same scope. -func (r *blockResolver) winningBlock(value dyn.Value) (sourceBlock, bool) { +func (r *blockResolver) winningBlock(value structvar.View) (sourceBlock, bool) { for _, location := range value.Locations() { if block, ok := r.byLocation[location]; ok { return block, true @@ -245,17 +243,13 @@ func (r *blockResolver) winningBlock(value dyn.Value) (sourceBlock, bool) { // indexWithinBlock returns the position of element inside block, where sequencePath is // relative to the block. A block is one parsed file, so the sequence read here holds // only that file's entries and a plain index into it is block-local. -func (r *blockResolver) indexWithinBlock(block sourceBlock, sequencePath dyn.Path, element dyn.Value) (int, bool) { +func (r *blockResolver) indexWithinBlock(block sourceBlock, sequencePath *structpath.PathNode, element structvar.View) (int, bool) { parsed, ok := r.blocks[block] if !ok { return 0, false } - sequence, err := dyn.GetByPath(parsed, r.regionPath(block, sequencePath)) - if err != nil { - return 0, false - } - entries, ok := sequence.AsSequence() - if !ok { + sequence := parsed.Lookup(r.regionPath(block, sequencePath)) + if sequence.Kind() != structvar.KindSequence { return 0, false } @@ -264,7 +258,7 @@ func (r *blockResolver) indexWithinBlock(block sourceBlock, sequencePath dyn.Pat locations[location] = struct{}{} } - for local, entry := range entries { + for local, entry := range sequence.Sequence() { for _, location := range entry.Locations() { if _, ok := locations[location]; ok { return local, true @@ -323,7 +317,7 @@ func (d routeDestination) scopeKey() string { func (r *blockResolver) routeDestinations(change resolvedChange) ([]routeDestination, error) { // The value whose definitions have to be reached: the element itself when the // change addresses one, otherwise the field being removed. - var target dyn.Value + var target structvar.View if addressesWholeElement(change) { target = change.steps[len(change.steps)-1].element } else if change.operation == OperationRemove { @@ -489,15 +483,15 @@ func (r *blockResolver) blockForNewElement(change resolvedChange) (sourceBlock, // blocksDefiningSequence returns the blocks that write the sequence at sequencePath. // This is for a value that does not exist yet, where only the receiving sequence is // known; an existing value is traced through its own locations instead. -func (r *blockResolver) blocksDefiningSequence(change resolvedChange, sequencePath dyn.Path) []sourceBlock { +func (r *blockResolver) blocksDefiningSequence(change resolvedChange, sequencePath *structpath.PathNode) []sourceBlock { var blocks []sourceBlock for _, block := range r.sortedBlocks() { blockPath, ok := r.sequencePathWithinBlock(block, change, sequencePath) if !ok { continue } - sequence, err := dyn.GetByPath(r.blocks[block], r.regionPath(block, blockPath)) - if err != nil { + sequence := r.blocks[block].Lookup(r.regionPath(block, blockPath)) + if !sequence.IsValid() { continue } if slices.ContainsFunc(sequence.Locations(), func(l diag.Location) bool { return l.File == block.file }) { @@ -513,22 +507,22 @@ func (r *blockResolver) blocksDefiningSequence(change resolvedChange, sequencePa // // change.steps is ordered outermost first, so each translated index is already known // by the time a deeper step needs it. -func (r *blockResolver) sequencePathWithinBlock(block sourceBlock, change resolvedChange, sequencePath dyn.Path) (dyn.Path, bool) { - result := slices.Clone(sequencePath) +func (r *blockResolver) sequencePathWithinBlock(block sourceBlock, change resolvedChange, sequencePath *structpath.PathNode) (*structpath.PathNode, bool) { + result := sequencePath.AsSlice() for _, step := range change.steps { - at := len(step.sequencePath) - if step.newElement || at >= len(sequencePath) { + at := step.sequencePath.Len() + if step.newElement || at >= len(result) { continue } // The step's own path is a prefix of sequencePath, so the indices translated // so far already apply to it. - index, ok := r.indexWithinBlock(block, result[:at], step.element) + index, ok := r.indexWithinBlock(block, structpath.Join(nil, result[:at]...), step.element) if !ok { return nil, false } - result[at] = dyn.Index(index) + result[at] = structpath.NewIndex(nil, index) } - return result, true + return structpath.Join(nil, result...), true } // pathWithinBlock rewrites a change's path so every sequence index addresses the @@ -582,8 +576,8 @@ type sequenceStep struct { // component is how many path components precede this sequence's index. component int // sequencePath is the sequence itself, e.g. resources.jobs.j.tasks. - sequencePath dyn.Path - element dyn.Value + sequencePath *structpath.PathNode + element structvar.View // newElement marks an Add whose key is not in the merged sequence yet. newElement bool } diff --git a/bundle/configsync/telemetry_test.go b/bundle/configsync/telemetry_test.go index d2a3da537f8..001ecb2f9d2 100644 --- a/bundle/configsync/telemetry_test.go +++ b/bundle/configsync/telemetry_test.go @@ -3,7 +3,6 @@ package configsync import ( "testing" - "github.com/databricks/cli/libs/dyn" "github.com/stretchr/testify/assert" ) @@ -63,37 +62,30 @@ func TestResourceTypeFromKey(t *testing.T) { } func TestRestoreStatsCounters(t *testing.T) { - resolved := dyn.V(map[string]dyn.Value{ - "variables": dyn.V(map[string]dyn.Value{ - "region": dyn.V(map[string]dyn.Value{"value": dyn.V("us-east-1")}), - "other": dyn.V(map[string]dyn.Value{"value": dyn.V("eu-west-1")}), - }), - }) + resolved := variablesConfig(t, map[string]any{"region": "us-east-1", "other": "eu-west-1"}) // Original pure ref still matching: restored but not counted (safe path). var kept RestoreStats - result := restoreOriginalRefs("us-east-1", dyn.V("${var.region}"), resolved, &kept) + result := restoreOriginalRefs("us-east-1", scalarView(t, "${var.region}"), resolved, &kept) assert.Equal(t, "${var.region}", result) assert.Equal(t, RestoreStats{}, kept) // Pure ref whose value changed to another variable's value: re-targeted. var retargeted RestoreStats - result = restoreOriginalRefs("eu-west-1", dyn.V("${var.region}"), resolved, &retargeted) + result = restoreOriginalRefs("eu-west-1", scalarView(t, "${var.region}"), resolved, &retargeted) assert.Equal(t, "${var.other}", result) assert.Equal(t, RestoreStats{Retargeted: 1}, retargeted) // New sequence element leaf restored from a sibling reference. - siblings := []dyn.Value{ - dyn.V(map[string]dyn.Value{"region": dyn.V("${var.region}")}), - } + siblings := tasksView(t, `task_key: "${var.region}"`) var fromSiblings RestoreStats - resultMap := restoreFromSiblings(map[string]any{"region": "us-east-1"}, siblings, resolved, &fromSiblings).(map[string]any) - assert.Equal(t, "${var.region}", resultMap["region"]) + resultMap := restoreFromSiblings(map[string]any{"task_key": "us-east-1"}, siblings, resolved, &fromSiblings).(map[string]any) + assert.Equal(t, "${var.region}", resultMap["task_key"]) assert.Equal(t, RestoreStats{FromSiblings: 1}, fromSiblings) // Hardcoded value: nothing restored, nothing counted. var none RestoreStats - result = restoreOriginalRefs("hardcoded", dyn.V("hardcoded"), resolved, &none) + result = restoreOriginalRefs("hardcoded", scalarView(t, "hardcoded"), resolved, &none) assert.Equal(t, "hardcoded", result) assert.Equal(t, RestoreStats{}, none) } diff --git a/bundle/configsync/variables.go b/bundle/configsync/variables.go index 2a0ac89cc92..2a1412c285b 100644 --- a/bundle/configsync/variables.go +++ b/bundle/configsync/variables.go @@ -3,25 +3,25 @@ package configsync import ( "context" "errors" + "reflect" "strings" "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/mutator" "github.com/databricks/cli/bundle/config/mutator/resourcemutator" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) -// varPrefix is the dyn.Path prefix for the ${var.X} shorthand. -var varPrefix = dyn.NewPath(dyn.Key("var")) +// varPrefix is the path prefix for the ${var.X} shorthand. +var varPrefix = structpath.NewStringKey(nil, "var") // RestoreVariableReferences replaces hardcoded change values with variable // references (${var.foo}, ${bundle.target}, ${resources.X.Y.id}) when the // value can be traced back to a reference in the original YAML. Resource IDs // are injected from state since they aren't materialized into the resolved -// config's dyn.Value tree. +// config's tree. // // For Replace operations, restoration consults the pre-resolved YAML at the // exact field position and tries three steps in order: @@ -51,11 +51,11 @@ var varPrefix = dyn.NewPath(dyn.Key("var")) // Restoration counts by mechanism are accumulated into stats (used for // telemetry); pass nil when counters are not needed (the counter methods are // nil-safe). -func RestoreVariableReferences(ctx context.Context, b *bundle.Bundle, fieldChanges []FieldChange, preResolved dyn.Value, stats *RestoreStats) error { +func RestoreVariableReferences(ctx context.Context, b *bundle.Bundle, fieldChanges []FieldChange, preResolved structvar.View, stats *RestoreStats) error { if !preResolved.IsValid() { return errors.New("pre-resolved config unavailable; variable-backed fields will be hardcoded") } - resolved := b.Config.Value() + resolved := resolvedConfig{view: b.Config.View(), overrides: map[string]any{}} // Mirror mutator.lookup's source-linked deployment override: when enabled, // ${workspace.file_path} resolves to b.SyncRootPath rather than the typed @@ -64,21 +64,18 @@ func RestoreVariableReferences(ctx context.Context, b *bundle.Bundle, fieldChang // actual deployed path and variables are lost on Replace. Keep this in // sync with mutator.lookup if new overrides are added there. if b.IsSourceLinkedDeployment() { - fpPath := dyn.NewPath(dyn.Key("workspace"), dyn.Key("file_path")) - if updated, err := dyn.SetByPath(resolved, fpPath, dyn.V(b.SyncRootPath)); err == nil { - resolved = updated - } + resolved.overrides["workspace.file_path"] = b.SyncRootPath } // Augment resolved with resource IDs from state — only when the config // actually uses ${resources.X.Y.id} references. The IDs aren't materialized - // into b.Config.Value() (they live in the StateDB), so we inject them here + // into b.Config (they live in the StateDB), so we inject them here // to enable sibling-based restoration. Skipped entirely for bundles with // no resource refs to avoid opening state DB files unnecessarily. resourceRefs := collectResourceIDRefs(preResolved) if len(resourceRefs) > 0 { if lookup := resourceIDLookup(b); lookup != nil { - resolved = injectResourceIDs(ctx, resolved, resourceRefs, lookup) + injectResourceIDs(ctx, resolved, resourceRefs, lookup) } else { log.Debugf(ctx, "variable restoration: state DB unavailable, skipping resource ID injection for %d refs", len(resourceRefs)) } @@ -115,20 +112,21 @@ func RestoreVariableReferences(ctx context.Context, b *bundle.Bundle, fieldChang // LoadPreResolvedConfig loads the bundle's configuration through the standard // loader mutators (entry point, includes, target overrides) but without -// variable resolution. The resulting dyn.Value is fully merged across files +// variable resolution. The resulting configuration is fully merged across files // and targets, yet retains ${...} references as literal strings. Returns -// InvalidValue if loading fails (restoration is then skipped). -func LoadPreResolvedConfig(ctx context.Context, b *bundle.Bundle) dyn.Value { +// an invalid value if loading fails (restoration is then skipped). +func LoadPreResolvedConfig(ctx context.Context, b *bundle.Bundle) structvar.View { fresh := &bundle.Bundle{ BundleRootPath: b.BundleRootPath, BundleRoot: b.BundleRoot, } mutator.DefaultMutators(ctx, fresh) if target := b.Config.Bundle.Target; target != "" { - if _, ok := fresh.Config.Targets[target]; ok { + if fresh.Config.HasTarget(target) { bundle.ApplyContext(ctx, fresh, mutator.SelectTarget(target)) } } + mutator.FinishLoading(ctx, fresh) // Keyed sequences merge in the initialize phase, which this reload skips. Without // them the sequences here stay in file order while the change paths address the @@ -140,7 +138,26 @@ func LoadPreResolvedConfig(ctx context.Context, b *bundle.Bundle) dyn.Value { resourcemutator.MergePipelineClusters(), resourcemutator.MergeApps(), ) - return fresh.Config.Value() + return fresh.Config.View() +} + +// resolvedConfig is the bundle's resolved configuration plus values that are known +// only outside of it (the source-linked file path, resource IDs from state). +type resolvedConfig struct { + view structvar.View + overrides map[string]any // by path +} + +// lookup returns the value at path as a Go value, and whether it exists. +func (c resolvedConfig) lookup(path *structpath.PathNode) (any, bool) { + if v, ok := c.overrides[path.String()]; ok { + return v, true + } + v := c.view.Lookup(path) + if !v.IsValid() { + return nil, false + } + return v.AsAny(), true } // resourceIDLookup returns a function that resolves resource keys to their @@ -157,20 +174,17 @@ func resourceIDLookup(b *bundle.Bundle) func(string) string { // ${resources...id} references. Returns the unique set of paths // so the caller can inject IDs at those positions; returns nil if no such // references exist. -func collectResourceIDRefs(preResolved dyn.Value) []dyn.Path { +func collectResourceIDRefs(preResolved structvar.View) []*structpath.PathNode { seen := map[string]bool{} - var paths []dyn.Path - _ = dyn.WalkReadOnly(preResolved, func(_ dyn.Path, v dyn.Value) error { + var paths []*structpath.PathNode + _ = structvar.Walk(preResolved, func(_ *structpath.PathNode, v structvar.View) error { s, ok := v.AsString() - if !ok || !dynvar.IsPureVariableReference(s) || seen[s] { + if !ok || !structvar.IsPureVariableReference(s) || seen[s] { return nil } seen[s] = true - p, ok := dynvar.PureReferenceToPath(s) - if !ok { - return nil - } - if len(p) != 4 || p[0].Key() != "resources" || p[3].Key() != "id" { + p, ok := structvar.PureReferenceToPath(s) + if !ok || p.Len() != 4 || p.KeyAt(0) != "resources" || p.KeyAt(3) != "id" { return nil } paths = append(paths, p) @@ -179,47 +193,33 @@ func collectResourceIDRefs(preResolved dyn.Value) []dyn.Path { return paths } -// injectResourceIDs populates the resolved dyn.Value with IDs from state for -// the given resource reference paths. Skips references whose IDs aren't in -// state or that can't be written back into the dyn.Value tree. -func injectResourceIDs(ctx context.Context, resolved dyn.Value, paths []dyn.Path, lookupID func(string) string) dyn.Value { +// injectResourceIDs records IDs from state for the given resource reference paths in +// resolved. Skips references whose IDs aren't in state. +func injectResourceIDs(ctx context.Context, resolved resolvedConfig, paths []*structpath.PathNode, lookupID func(string) string) { for _, p := range paths { - resourceKey := p[:3].String() + resourceKey := p.Prefix(3).String() id := lookupID(resourceKey) if id == "" { log.Debugf(ctx, "variable restoration: no state entry for resource %q", resourceKey) continue } - updated, err := dyn.SetByPath(resolved, p, dyn.V(id)) - if err != nil { - log.Debugf(ctx, "variable restoration: SetByPath failed for %s: %v", p, err) - continue - } - resolved = updated + resolved.overrides[p.String()] = id } - return resolved } -// resolveReferencePath converts a variable reference string to the dyn.Path +// resolveReferencePath converts a variable reference string to the path // where its resolved value can be found in the bundle config. It applies the // same ${var.X} → variables.X.value shorthand rewriting as the variable // resolution mutator. -func resolveReferencePath(refStr string) (dyn.Path, bool) { - p, ok := dynvar.PureReferenceToPath(refStr) +func resolveReferencePath(refStr string) (*structpath.PathNode, bool) { + p, ok := structvar.PureReferenceToPath(refStr) if !ok { return nil, false } - if p.HasPrefix(varPrefix) && len(p) >= 2 { - newPath := dyn.NewPath( - dyn.Key("variables"), - p[1], - dyn.Key("value"), - ) - if len(p) > 2 { - newPath = newPath.Append(p[2:]...) - } - return newPath, true + if p.HasPrefix(varPrefix) && p.Len() >= 2 { + newPath := structpath.NewPath(nil, "variables", p.KeyAt(1), "value") + return structpath.Join(newPath, p.AsSlice()[2:]...), true } return p, true @@ -233,7 +233,7 @@ func resolveReferencePath(refStr string) (dyn.Path, bool) { // new value, falls back to a global lookup: if the new value uniquely matches // a different variable, that variable is used instead. The field's prior use // of a variable is the false-positive guard. -func restoreOriginalRefs(value any, preResolved, resolved dyn.Value, stats *RestoreStats) any { +func restoreOriginalRefs(value any, preResolved structvar.View, resolved resolvedConfig, stats *RestoreStats) any { switch v := value.(type) { case string, bool, int64: if ref, ok := matchOriginalRef(value, preResolved, resolved); ok { @@ -253,26 +253,14 @@ func restoreOriginalRefs(value any, preResolved, resolved dyn.Value, stats *Rest return value case map[string]any: - preMap, _ := preResolved.AsMap() for key, val := range v { - var childPre dyn.Value - if preMap.Len() > 0 { - if p, ok := preMap.GetPairByString(key); ok { - childPre = p.Value - } - } - v[key] = restoreOriginalRefs(val, childPre, resolved, stats) + v[key] = restoreOriginalRefs(val, preResolved.Get(key), resolved, stats) } return v case []any: - preSeq, _ := preResolved.AsSequence() for i, val := range v { - var childPre dyn.Value - if i < len(preSeq) { - childPre = preSeq[i] - } - v[i] = restoreOriginalRefs(val, childPre, resolved, stats) + v[i] = restoreOriginalRefs(val, preResolved.Index(i), resolved, stats) } return v @@ -286,37 +274,34 @@ func restoreOriginalRefs(value any, preResolved, resolved dyn.Value, stats *Rest // relative path: if exactly one unique pure variable reference across siblings // resolves to the leaf value, that reference is substituted. Multiple // different matching references are treated as ambiguous and skipped. -func restoreFromSiblings(value any, siblings []dyn.Value, resolved dyn.Value, stats *RestoreStats) any { - return restoreFromSiblingsAt(value, siblings, resolved, dyn.EmptyPath, stats) +func restoreFromSiblings(value any, siblings []structvar.View, resolved resolvedConfig, stats *RestoreStats) any { + return restoreFromSiblingsAt(value, siblings, resolved, nil, stats) } -func restoreFromSiblingsAt(value any, siblings []dyn.Value, resolved dyn.Value, relPath dyn.Path, stats *RestoreStats) any { +func restoreFromSiblingsAt(value any, siblings []structvar.View, resolved resolvedConfig, relPath *structpath.PathNode, stats *RestoreStats) any { switch v := value.(type) { case string, bool, int64: refs := map[string]struct{}{} strVal, isStr := value.(string) for _, sib := range siblings { - sv, err := dyn.GetByPath(sib, relPath) - if err != nil { - continue - } + sv := sib.Lookup(relPath) s, ok := sv.AsString() if !ok { continue } - if dynvar.IsPureVariableReference(s) { + if structvar.IsPureVariableReference(s) { rp, ok := resolveReferencePath(s) if !ok { continue } - rv, getErr := dyn.GetByPath(resolved, rp) - if getErr != nil { + rv, found := resolved.lookup(rp) + if !found { continue } - if rv.AsAny() == value { + if equalValues(rv, value) { refs[s] = struct{}{} } - } else if isStr && dynvar.ContainsVariableReference(s) { + } else if isStr && structvar.ContainsVariableReference(s) { // Compound interpolation in sibling: try to align the new // value against the sibling's template. If all variables // match at their positions, the template (possibly with @@ -336,13 +321,13 @@ func restoreFromSiblingsAt(value any, siblings []dyn.Value, resolved dyn.Value, case map[string]any: for key, val := range v { - v[key] = restoreFromSiblingsAt(val, siblings, resolved, relPath.Append(dyn.Key(key)), stats) + v[key] = restoreFromSiblingsAt(val, siblings, resolved, structpath.NewStringKey(relPath, key), stats) } return v case []any: for i, val := range v { - v[i] = restoreFromSiblingsAt(val, siblings, resolved, relPath.Append(dyn.Index(i)), stats) + v[i] = restoreFromSiblingsAt(val, siblings, resolved, structpath.NewIndex(relPath, i), stats) } return v @@ -354,51 +339,33 @@ func restoreFromSiblingsAt(value any, siblings []dyn.Value, resolved dyn.Value, // isPureVarRef reports whether the pre-resolved value at the field is a pure // ${var.X} reference. Used to gate the fallback substitution: only fields that // already used a variable can be re-targeted to a different variable. -func isPureVarRef(preResolved dyn.Value) bool { +func isPureVarRef(preResolved structvar.View) bool { if !preResolved.IsValid() { return false } s, ok := preResolved.AsString() - if !ok || !dynvar.IsPureVariableReference(s) { + if !ok || !structvar.IsPureVariableReference(s) { return false } - p, ok := dynvar.PureReferenceToPath(s) - if !ok { - return false - } - return p.HasPrefix(varPrefix) + p, ok := structvar.PureReferenceToPath(s) + return ok && p.HasPrefix(varPrefix) } // matchAnyVariable searches all bundle variables for a unique scalar value that // equals remoteValue. Returns the ${var.X} reference on a unique match, "" // otherwise. Multiple matches are treated as ambiguous and skipped. -func matchAnyVariable(remoteValue any, resolved dyn.Value) (string, bool) { - variables, err := dyn.GetByPath(resolved, dyn.NewPath(dyn.Key("variables"))) - if err != nil { - return "", false - } - vmap, ok := variables.AsMap() - if !ok { - return "", false - } +func matchAnyVariable(remoteValue any, resolved resolvedConfig) (string, bool) { var match string count := 0 - for _, pair := range vmap.Pairs() { - name, ok := pair.Key.AsString() - if !ok { - continue - } - v, getErr := dyn.GetByPath(pair.Value, dyn.NewPath(dyn.Key("value"))) - if getErr != nil { - continue - } + for name, variable := range resolved.view.Get("variables").MapItems() { + v := variable.Get("value") switch v.Kind() { - case dyn.KindString, dyn.KindInt, dyn.KindBool: - if v.AsAny() == remoteValue { - match = pathToRef(varPrefix.Append(dyn.Key(name))) + case structvar.KindString, structvar.KindInt, structvar.KindBool: + if equalValues(v.AsAny(), remoteValue) { + match = pathToRef(structpath.NewStringKey(varPrefix, name)) count++ } - case dyn.KindInvalid, dyn.KindMap, dyn.KindSequence, dyn.KindFloat, dyn.KindTime, dyn.KindNil: + case structvar.KindInvalid, structvar.KindMap, structvar.KindSequence, structvar.KindFloat, structvar.KindTime, structvar.KindNil: // Skip non-scalar / unsupported variable values. } } @@ -408,19 +375,19 @@ func matchAnyVariable(remoteValue any, resolved dyn.Value) (string, bool) { return "", false } -// pathToRef formats a dyn.Path as a "${...}" interpolation reference. -func pathToRef(p dyn.Path) string { +// pathToRef formats a path as a "${...}" interpolation reference. +func pathToRef(p *structpath.PathNode) string { return "${" + p.String() + "}" } // matchOriginalRef checks if the pre-resolved config value at this position // was a pure variable reference whose resolved value equals remoteValue. -func matchOriginalRef(remoteValue any, preResolved, resolved dyn.Value) (string, bool) { +func matchOriginalRef(remoteValue any, preResolved structvar.View, resolved resolvedConfig) (string, bool) { if !preResolved.IsValid() { return "", false } s, ok := preResolved.AsString() - if !ok || !dynvar.IsPureVariableReference(s) { + if !ok || !structvar.IsPureVariableReference(s) { return "", false } @@ -429,12 +396,12 @@ func matchOriginalRef(remoteValue any, preResolved, resolved dyn.Value) (string, return "", false } - resolvedV, err := dyn.GetByPath(resolved, resolvedPath) - if err != nil { + resolvedV, found := resolved.lookup(resolvedPath) + if !found { return "", false } - if resolvedV.AsAny() == remoteValue { + if equalValues(resolvedV, remoteValue) { return s, true } return "", false @@ -454,12 +421,12 @@ func matchOriginalRef(remoteValue any, preResolved, resolved dyn.Value) (string, // new value contains "in" inside an unrelated word, that occurrence is still // rewritten to ${var.X}. Variables in the template are processed in order of // appearance, which is usually what the user expects. -func restoreCompoundInterpolation(remoteValue string, preResolved, resolved dyn.Value) (string, bool) { +func restoreCompoundInterpolation(remoteValue string, preResolved structvar.View, resolved resolvedConfig) (string, bool) { if !preResolved.IsValid() { return "", false } template, ok := preResolved.AsString() - if !ok || !dynvar.ContainsVariableReference(template) || dynvar.IsPureVariableReference(template) { + if !ok || !structvar.ContainsVariableReference(template) || structvar.IsPureVariableReference(template) { return "", false } @@ -480,7 +447,7 @@ func restoreCompoundInterpolation(remoteValue string, preResolved, resolved dyn. result = result[:idx] + seg.raw + result[idx+len(seg.resolvedValue):] } - if !dynvar.ContainsVariableReference(result) { + if !structvar.ContainsVariableReference(result) { return "", false } return result, true @@ -497,8 +464,8 @@ type templateSegment struct { // parseTemplateSegments splits a template string like "/mnt/${var.X}/raw" // into alternating literal and variable segments, resolving each variable. // Returns nil if any variable can't be resolved. -func parseTemplateSegments(template string, resolved dyn.Value) []templateSegment { - ref, ok := dynvar.NewRef(dyn.V(template)) +func parseTemplateSegments(template string, resolved resolvedConfig) []templateSegment { + ref, ok := structvar.NewRef(template) if !ok { return nil } @@ -525,12 +492,12 @@ func parseTemplateSegments(template string, resolved dyn.Value) []templateSegmen return nil } - resolvedV, err := dyn.GetByPath(resolved, resolvedPath) - if err != nil { + resolvedV, found := resolved.lookup(resolvedPath) + if !found { return nil } - resolvedStr, ok := resolvedV.AsString() + resolvedStr, ok := resolvedV.(string) if !ok { return nil } @@ -553,18 +520,15 @@ func parseTemplateSegments(template string, resolved dyn.Value) []templateSegmen return segments } -// preResolvedValueAt returns the pre-resolved dyn.Value at the field path, +// preResolvedValueAt returns the pre-resolved value at the field path, // if the field exists in the merged pre-resolved config. -func preResolvedValueAt(preResolved dyn.Value, fieldPath string) (dyn.Value, bool) { - p, err := dyn.NewPathFromString(fieldPath) +func preResolvedValueAt(preResolved structvar.View, fieldPath string) (structvar.View, bool) { + p, err := structpath.ParsePath(fieldPath) if err != nil { - return dyn.InvalidValue, false + return structvar.View{}, false } - v, err := dyn.GetByPath(preResolved, p) - if err != nil { - return dyn.InvalidValue, false - } - return v, true + v := preResolved.Lookup(p) + return v, v.IsValid() } // parentIsVariableReference reports whether the parent of fieldPath resolves to a @@ -572,7 +536,7 @@ func preResolvedValueAt(preResolved dyn.Value, fieldPath string) (dyn.Value, boo // ${var.spark_conf}). A nested key or index cannot be written into such a scalar, // so config-remote-sync skips the change. fieldPath is a merged-index path, the // same space as FieldChange.originalPath. -func parentIsVariableReference(preResolved dyn.Value, fieldPath string) bool { +func parentIsVariableReference(preResolved structvar.View, fieldPath string) bool { node, err := structpath.ParsePattern(fieldPath) if err != nil { return false @@ -586,7 +550,7 @@ func parentIsVariableReference(preResolved dyn.Value, fieldPath string) bool { return false } s, ok := v.AsString() - return ok && dynvar.ContainsVariableReference(s) + return ok && structvar.ContainsVariableReference(s) } // sequenceSiblings returns the sibling elements of the parent sequence when @@ -594,7 +558,7 @@ func parentIsVariableReference(preResolved dyn.Value, fieldPath string) bool { // last component must be an index ([*] or [N]) and the parent must resolve // to a sequence in the pre-resolved config. Returns false for non-sequence // Adds (e.g., new map fields). -func sequenceSiblings(preResolved dyn.Value, fieldPath string) ([]dyn.Value, bool) { +func sequenceSiblings(preResolved structvar.View, fieldPath string) ([]structvar.View, bool) { node, err := structpath.ParsePattern(fieldPath) if err != nil { return nil, false @@ -603,17 +567,39 @@ func sequenceSiblings(preResolved dyn.Value, fieldPath string) ([]dyn.Value, boo if !hasIndex && !node.BracketStar() { return nil, false } - p, err := dyn.NewPathFromString(node.Parent().String()) + p, err := structpath.ParsePath(node.Parent().String()) if err != nil { return nil, false } - parentValue, err := dyn.GetByPath(preResolved, p) - if err != nil { + parentValue := preResolved.Lookup(p) + if parentValue.Kind() != structvar.KindSequence { return nil, false } - seq, ok := parentValue.AsSequence() - if !ok { - return nil, false + var seq []structvar.View + for _, elem := range parentValue.Sequence() { + seq = append(seq, elem) } return seq, true } + +// equalValues reports whether two configuration values are equal, treating integers of +// different Go types as equal: variables decoded from YAML hold int, while typed +// fields of the remote resource hold int64. +func equalValues(a, b any) bool { + ai, aok := asInt64(a) + bi, bok := asInt64(b) + if aok || bok { + return aok && bok && ai == bi + } + return a == b +} + +func asInt64(v any) (int64, bool) { + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int(), true + default: + return 0, false + } +} diff --git a/bundle/configsync/variables_test.go b/bundle/configsync/variables_test.go index 15f11d5eb3c..b82664aa9c4 100644 --- a/bundle/configsync/variables_test.go +++ b/bundle/configsync/variables_test.go @@ -1,22 +1,64 @@ package configsync import ( + "fmt" + "strings" "testing" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) +func loadTestConfig(t *testing.T, yaml string) *config.Root { + t.Helper() + root, diags := config.LoadFromBytes("test.yml", []byte(yaml)) + require.NoError(t, diags.Error()) + return root +} + +// scalarView returns a view of the string s. +func scalarView(t *testing.T, s string) structvar.View { + t.Helper() + root := loadTestConfig(t, fmt.Sprintf("bundle:\n name: %q\n", s)) + return root.View().Lookup(structpath.MustParsePath("bundle.name")) +} + +// tasksView returns views of job tasks, each given as the YAML of its fields in flow style. +func tasksView(t *testing.T, tasks ...string) []structvar.View { + t.Helper() + var sb strings.Builder + sb.WriteString("resources:\n jobs:\n j:\n tasks:\n") + for _, task := range tasks { + fmt.Fprintf(&sb, " - {%s}\n", task) + } + root := loadTestConfig(t, sb.String()) + var out []structvar.View + for _, elem := range root.View().Lookup(structpath.MustParsePath("resources.jobs.j.tasks")).Sequence() { + out = append(out, elem) + } + return out +} + +// variablesConfig returns a resolved configuration with the given variable values. +func variablesConfig(t *testing.T, values map[string]any) resolvedConfig { + t.Helper() + var sb strings.Builder + sb.WriteString("variables:\n") + for name, value := range values { + fmt.Fprintf(&sb, " %s:\n value: %#v\n", name, value) + } + return resolvedConfig{view: loadTestConfig(t, sb.String()).View(), overrides: map[string]any{}} +} + // TestRestoreOriginalRefs_HardcodedFieldNotRewritten fences the Replace safety // invariant: a hardcoded leaf must never be rewritten to a variable reference // just because the remote value coincidentally matches a variable elsewhere. func TestRestoreOriginalRefs_HardcodedFieldNotRewritten(t *testing.T) { - preResolved := dyn.V("us-east-1") - resolved := dyn.V(map[string]dyn.Value{ - "variables": dyn.V(map[string]dyn.Value{ - "region": dyn.V(map[string]dyn.Value{"value": dyn.V("main")}), - }), - }) + preResolved := scalarView(t, "us-east-1") + resolved := variablesConfig(t, map[string]any{"region": "main"}) // Even though "main" matches ${var.region}, restoreOriginalRefs must NOT // rewrite it — the original was hardcoded. result := restoreOriginalRefs("main", preResolved, resolved, &RestoreStats{}) @@ -29,17 +71,8 @@ func TestRestoreOriginalRefs_HardcodedFieldNotRewritten(t *testing.T) { func TestRestoreFromSiblings_ValueMatchesVariableButDifferentPath(t *testing.T) { // Sibling uses ${var.retry_count}=5 at .max_retries. New element has // .min_retry_interval=5 — coincidental match at a DIFFERENT relative path. - siblings := []dyn.Value{ - dyn.V(map[string]dyn.Value{ - "task_key": dyn.V("main"), - "max_retries": dyn.V("${var.retry_count}"), - }), - } - resolved := dyn.V(map[string]dyn.Value{ - "variables": dyn.V(map[string]dyn.Value{ - "retry_count": dyn.V(map[string]dyn.Value{"value": dyn.V(int64(5))}), - }), - }) + siblings := tasksView(t, `task_key: main, max_retries: "${var.retry_count}"`) + resolved := variablesConfig(t, map[string]any{"retry_count": 5}) value := map[string]any{ "task_key": "other", "min_retry_interval": int64(5), @@ -52,33 +85,32 @@ func TestRestoreFromSiblings_ValueMatchesVariableButDifferentPath(t *testing.T) // TestRestoreFromSiblings_AmbiguousAcrossSiblings fences the multi-variable // same-value rule: when two siblings use different variables at the same // relative path that both resolve to the same value, restoration is skipped. +func TestRestoreFromSiblings_IntVariableMatchesInt64Field(t *testing.T) { + // The variable decodes from YAML as int; the remote field is a typed int64. + siblings := tasksView(t, `task_key: main, max_retries: "${var.retry_count}"`) + resolved := variablesConfig(t, map[string]any{"retry_count": 5}) + value := map[string]any{"task_key": "other", "max_retries": int64(5)} + result := restoreFromSiblings(value, siblings, resolved, &RestoreStats{}).(map[string]any) + assert.Equal(t, "${var.retry_count}", result["max_retries"]) +} + func TestRestoreFromSiblings_AmbiguousAcrossSiblings(t *testing.T) { - siblings := []dyn.Value{ - dyn.V(map[string]dyn.Value{"default": dyn.V("${var.landing_schema}")}), - dyn.V(map[string]dyn.Value{"default": dyn.V("${var.curated_schema}")}), - } - resolved := dyn.V(map[string]dyn.Value{ - "variables": dyn.V(map[string]dyn.Value{ - "landing_schema": dyn.V(map[string]dyn.Value{"value": dyn.V("raw_data")}), - "curated_schema": dyn.V(map[string]dyn.Value{"value": dyn.V("raw_data")}), - }), - }) - value := map[string]any{"default": "raw_data"} + siblings := tasksView(t, `task_key: "${var.landing_schema}"`, `task_key: "${var.curated_schema}"`) + resolved := variablesConfig(t, map[string]any{"landing_schema": "raw_data", "curated_schema": "raw_data"}) + value := map[string]any{"task_key": "raw_data"} result := restoreFromSiblings(value, siblings, resolved, &RestoreStats{}).(map[string]any) - assert.Equal(t, "raw_data", result["default"]) + assert.Equal(t, "raw_data", result["task_key"]) } // TestRestoreCompoundInterpolation covers the template alignment algorithm. // End-to-end coverage (pure ref match, sibling match, non-sequence skip, etc.) // lives in acceptance/bundle/config-remote-sync/resolve_variables. func TestRestoreCompoundInterpolation(t *testing.T) { - resolved := dyn.V(map[string]dyn.Value{ - "variables": dyn.V(map[string]dyn.Value{ - "host": dyn.V(map[string]dyn.Value{"value": dyn.V("dev-sql.example.com")}), - "port": dyn.V(map[string]dyn.Value{"value": dyn.V("1433")}), - "db": dyn.V(map[string]dyn.Value{"value": dyn.V("analytics_dev")}), - "acct": dyn.V(map[string]dyn.Value{"value": dyn.V("acct")}), - }), + resolved := variablesConfig(t, map[string]any{ + "host": "dev-sql.example.com", + "port": "1433", + "db": "analytics_dev", + "acct": "acct", }) tests := []struct { @@ -108,7 +140,7 @@ func TestRestoreCompoundInterpolation(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := restoreOriginalRefs(tt.remote, dyn.V(tt.template), resolved, &RestoreStats{}) + result := restoreOriginalRefs(tt.remote, scalarView(t, tt.template), resolved, &RestoreStats{}) assert.Equal(t, tt.want, result) }) } diff --git a/bundle/deploy/metadata/compute.go b/bundle/deploy/metadata/compute.go index 9cc260f7b91..aa5bd064991 100644 --- a/bundle/deploy/metadata/compute.go +++ b/bundle/deploy/metadata/compute.go @@ -14,6 +14,7 @@ import ( "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/snapshot" + "github.com/databricks/cli/libs/structs/structpath" ) type compute struct { @@ -54,7 +55,7 @@ func (m *compute) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics for name, job := range b.Config.Resources.Jobs { // Compute config file path the job is defined in, relative to the bundle // root - l := b.Config.GetLocation("resources.jobs." + name) + l := b.Config.GetLocationOfExact(structpath.NewPath(nil, "resources", "jobs", name)) if l.File == "" { // Skip resources that exist only in the deployment state: statemgmt.Load, // which runs before this mutator, injects them into the config without a @@ -81,7 +82,7 @@ func (m *compute) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics for name, pipeline := range b.Config.Resources.Pipelines { // Compute config file path the pipeline is defined in, relative to the bundle // root - l := b.Config.GetLocation("resources.pipelines." + name) + l := b.Config.GetLocationOfExact(structpath.NewPath(nil, "resources", "pipelines", name)) if l.File == "" { // Skip resources that exist only in the deployment state: statemgmt.Load, // which runs before this mutator, injects them into the config without a @@ -106,7 +107,7 @@ func (m *compute) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics for name, dashboard := range b.Config.Resources.Dashboards { // Compute config file path the dashboard is defined in, relative to the bundle // root - l := b.Config.GetLocation("resources.dashboards." + name) + l := b.Config.GetLocationOfExact(structpath.NewPath(nil, "resources", "dashboards", name)) if l.File == "" { // Skip resources that exist only in the deployment state: statemgmt.Load, // which runs before this mutator, injects them into the config without a diff --git a/bundle/direct/bundle_plan.go b/bundle/direct/bundle_plan.go index 8f8336beaa5..8db85a1f654 100644 --- a/bundle/direct/bundle_plan.go +++ b/bundle/direct/bundle_plan.go @@ -12,12 +12,11 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct/dresources" "github.com/databricks/cli/bundle/direct/dstate" "github.com/databricks/cli/bundle/terraform_dabs_map" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" "github.com/databricks/cli/libs/retry" @@ -886,7 +885,7 @@ func isEmpty(rv reflect.Value) bool { // Empty slices and maps cannot be represented in proto and because of that they cannot be represented // by SDK's JSON encoder. However, they can be provided by users in the config and can be represented in - // Bundle struct (currently libs/structs and libs/dyn use ForceSendFields for maps and slices, unlike SDK). + // Bundle struct (currently libs/structs uses ForceSendFields for maps and slices, unlike SDK). // Thus we get permanent drift because we see that new config is [] but in the state it is omitted. if rv.Kind() == reflect.Slice { @@ -1087,7 +1086,7 @@ func (b *DeploymentBundle) resolveReferences(ctx context.Context, resourceKey st var resolved bool for fieldPathStr, refString := range sv.Refs { - refs, ok := dynvar.NewRef(dyn.V(refString)) + refs, ok := structvar.NewRef(refString) if !ok { logdiag.LogError(ctx, fmt.Errorf("%s: cannot parse %q", errorPrefix, refString)) return false @@ -1161,36 +1160,17 @@ func (b *DeploymentBundle) makePlan(ctx context.Context, configRoot *config.Root existingKeys := maps.Clone(db.State) - patterns := []dyn.Pattern{ - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey(), dyn.Key("permissions")), - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey(), dyn.Key("grants")), - } - - // Walk? if configRoot != nil { - for _, pat := range patterns { - _, err := dyn.MapByPattern( - configRoot.Value(), - pat, - func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - s := p.String() - resourceType := config.GetResourceTypeFromKey(s) - if resourceType == "" { - return v, fmt.Errorf("cannot parse resource key: %q", s) - } - _, ok := dresources.SupportedResources[resourceType] - if !ok { - return v, fmt.Errorf("unsupported resource type: %s", resourceType) - } - - nodes = append(nodes, s) - return dyn.InvalidValue, nil - }, - ) - if err != nil { - return nil, err + for _, s := range configRoot.ResourceNodes() { + resourceType := config.GetResourceTypeFromKey(s) + if resourceType == "" { + return nil, fmt.Errorf("cannot parse resource key: %q", s) + } + _, ok := dresources.SupportedResources[resourceType] + if !ok { + return nil, fmt.Errorf("unsupported resource type: %s", resourceType) } + nodes = append(nodes, s) } } @@ -1245,7 +1225,7 @@ func (b *DeploymentBundle) makePlan(ctx context.Context, configRoot *config.Root // This means input and state must be compatible: input can have more fields, but existing fields should not be moved // This means one cannot refer to fields not present in state (e.g. ${resources.jobs.foo.permissions}) - refs, err := extractReferences(configRoot.Value(), node, adapter.StateType()) + refs, err := extractReferences(configRoot, node, adapter.StateType()) if err != nil { return nil, fmt.Errorf("failed to read references from config for %s: %w", node, err) } @@ -1254,13 +1234,13 @@ func (b *DeploymentBundle) makePlan(ctx context.Context, configRoot *config.Root var dependsOn []deployplan.DependsOnEntry for _, reference := range refs { - ref, ok := dynvar.NewRef(dyn.V(reference)) + ref, ok := structvar.NewRef(reference) if !ok { continue } for _, targetPath := range ref.References() { - targetPathParsed, err := dyn.NewPathFromString(targetPath) + targetPathParsed, err := structpath.ParsePath(targetPath) if err != nil { return nil, fmt.Errorf("parsing %q: %w", targetPath, err) } @@ -1367,72 +1347,67 @@ func (b *DeploymentBundle) makePlan(ctx context.Context, configRoot *config.Root // keeping only those whose field path exists in stateType (references in input-only or // bundle:"readonly" fields, such as volumes' computed volume_path, are skipped). // Returns a map from structpath string (field path within the resource) to template string. -func ExtractReferences(root dyn.Value, node string, stateType reflect.Type) (map[string]string, error) { +func ExtractReferences(root *config.Root, node string, stateType reflect.Type) (map[string]string, error) { return extractReferences(root, node, stateType) } -func extractReferences(root dyn.Value, node string, stateType reflect.Type) (map[string]string, error) { +func extractReferences(root *config.Root, node string, stateType reflect.Type) (map[string]string, error) { nodeType := config.GetResourceTypeFromKey(node) refs := make(map[string]string) - path, err := dyn.NewPathFromString(node) + path, err := structpath.ParsePath(node) if err != nil { return nil, fmt.Errorf("internal error: bad node key: %q: %w", node, err) } - val, err := dyn.GetByPath(root, path) - if err != nil { - return nil, err + if _, ok := root.Resources.GetResource(path.KeyAt(1), path.KeyAt(2)); !ok { + return nil, fmt.Errorf("no value at %s", node) } - err = dyn.WalkReadOnly(val, func(p dyn.Path, v dyn.Value) error { - fullPath := append(path, p...) - targetType := config.GetResourceTypeFromKey(fullPath.String()) - if targetType != nodeType { - // Make sure these are associated with different nodes: - // resources.jobs.foo... - // resources.jobs.foo.permissions... - // resources.jobs.foo.grants... - return nil - } - ref, ok := dynvar.NewRef(v) - if !ok { - return nil - } - // ValidatePath and the refs keys both operate on structpath (keys are - // re-parsed and applied to the typed state in structvar.ResolveRef). - // structpath's bracket notation (['key.with.dots']) also round-trips - // keys with dots, which dyn.Path.String()'s dot notation cannot. - fieldPath := dynPathToStructPath(p) - - // References resolve against the state type, not the input config (see PlanResources - // and dresources.TestInputSubset). A field in input but not in state — e.g. a - // bundle:"readonly" field like volumes' volume_path — is dropped before deploy, so a - // reference it carries cannot resolve into state and is not a dependency here. Such - // references are still resolved earlier during initialize. - if structaccess.ValidatePath(stateType, fieldPath) == nil { - // Store the original string that contains references, not individual references. - refs[fieldPath.String()] = ref.Str - } - return nil + // The walk covers the whole resource: keep the strings under node. + walk.ResourceStringsOf(root, path.KeyAt(1), path.KeyAt(2), walk.WithReferences, func(n *walk.Node, s string) (string, bool) { + addReference(refs, path, nodeType, n.Path(), s, stateType) + return "", false }) - if err != nil { - return nil, fmt.Errorf("parsing refs: %w", err) + for p, ref := range root.ReferencesUnder(path) { + addReference(refs, path, nodeType, p, ref, stateType) } return refs, nil } -// dynPathToStructPath converts a dyn.Path to a structpath.PathNode. -func dynPathToStructPath(p dyn.Path) *structpath.PathNode { - var node *structpath.PathNode - for _, c := range p { - if key := c.Key(); key != "" { - node = structpath.NewStringKey(node, key) - } else { - node = structpath.NewIndex(node, c.Index()) - } +// addReference records in refs the string s with references at fullPath, keyed by its +// path relative to the node at path, if it belongs to that node. +func addReference(refs map[string]string, path *structpath.PathNode, nodeType string, fullPath *structpath.PathNode, s string, stateType reflect.Type) { + if !fullPath.HasPrefix(path) { + return + } + targetType := config.GetResourceTypeFromKey(fullPath.String()) + if targetType != nodeType { + // Make sure these are associated with different nodes: + // resources.jobs.foo... + // resources.jobs.foo.permissions... + // resources.jobs.foo.grants... + return + } + ref, ok := structvar.NewRef(s) + if !ok { + return + } + // ValidatePath and the refs keys both operate on structpath (keys are + // re-parsed and applied to the typed state in structvar.ResolveRef). + // structpath's bracket notation (['key.with.dots']) also round-trips + // keys with dots. + fieldPath := fullPath.SkipPrefix(path.Len()) + + // References resolve against the state type, not the input config (see PlanResources + // and dresources.TestInputSubset). A field in input but not in state — e.g. a + // bundle:"readonly" field like volumes' volume_path — is dropped before deploy, so a + // reference it carries cannot resolve into state and is not a dependency here. Such + // references are still resolved earlier during initialize. + if structaccess.ValidatePath(stateType, fieldPath) == nil { + // Store the original string that contains references, not individual references. + refs[fieldPath.String()] = ref.Str } - return node } func (b *DeploymentBundle) getAdapterForKey(resourceKey string) (*dresources.Adapter, error) { diff --git a/bundle/direct/bundle_plan_test.go b/bundle/direct/bundle_plan_test.go index 2d051dc21b9..59d8f37093b 100644 --- a/bundle/direct/bundle_plan_test.go +++ b/bundle/direct/bundle_plan_test.go @@ -1,15 +1,13 @@ package direct import ( - "bytes" "slices" "testing" + "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct/dresources" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/databricks/cli/libs/structs/structdiff" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/structs/structvar" @@ -20,35 +18,6 @@ import ( "github.com/stretchr/testify/require" ) -func TestDynPathToStructPath(t *testing.T) { - tests := []struct { - path dyn.Path - expected string - }{ - { - path: dyn.NewPath(dyn.Key("foo"), dyn.Key("bar")), - expected: "foo.bar", - }, - { - path: dyn.NewPath(dyn.Key("foo"), dyn.Index(1), dyn.Key("bar")), - expected: "foo[1].bar", - }, - { - path: dyn.NewPath(dyn.Key("configuration"), dyn.Key("europris.swipe.egress_streaming_schema")), - expected: "configuration['europris.swipe.egress_streaming_schema']", - }, - { - path: dyn.NewPath(dyn.Key("tags"), dyn.Key("it's.here")), - expected: "tags['it''s.here']", - }, - } - - for _, tc := range tests { - node := dynPathToStructPath(tc.path) - assert.Equal(t, tc.expected, node.String()) - } -} - // extractReferences gates references on the state type: a reference in an input-only field // (e.g. a bundle:"readonly" field like volumes' volume_path) must not become a dependency, // while references in state fields (e.g. comment) are still extracted. @@ -69,8 +38,8 @@ resources: comment: "${resources.schemas.kept.name}" volume_path: "/Volumes/main/${resources.schemas.dropped.name}/myvol" ` - root, err := yamlloader.LoadYAML("test", bytes.NewBufferString(yml)) - require.NoError(t, err) + root, diags := config.LoadFromBytes("test", []byte(yml)) + require.NoError(t, diags.Error()) refs, err := extractReferences(root, "resources.volumes.v", stateType) require.NoError(t, err) diff --git a/bundle/direct/dresources/adapter.go b/bundle/direct/dresources/adapter.go index 80ece24b2be..3f2da957804 100644 --- a/bundle/direct/dresources/adapter.go +++ b/bundle/direct/dresources/adapter.go @@ -512,7 +512,7 @@ func (a *Adapter) FieldIsStableOutput(path *structpath.PathNode) bool { // references needed to complete it. Resources without PrepareInputConfig pass their config through. func (a *Adapter) PrepareInputConfig(inputConfig any, resourceKey string) (*structvar.StructVar, error) { if a.prepareInputConfig == nil { - return &structvar.StructVar{Value: inputConfig, Refs: nil}, nil + return &structvar.StructVar{Value: inputConfig, Refs: nil, Locations: nil}, nil } outs, err := a.prepareInputConfig.Call(inputConfig, resourceKey) diff --git a/bundle/direct/dresources/cluster.go b/bundle/direct/dresources/cluster.go index 82167080c4d..bc3467786d2 100644 --- a/bundle/direct/dresources/cluster.go +++ b/bundle/direct/dresources/cluster.go @@ -528,7 +528,7 @@ func makeEditCluster(id string, config *compute.ClusterSpec) compute.EditCluster } // librariesPath is the ClusterState path of the libraries slice, used to detect library changes. -var librariesPath = structpath.MustParsePath("libraries") +var librariesPath = structpath.NewPath(nil, "libraries") // KeyedSlices compares libraries by identity rather than index so reordering (or the // read-order the status API returns) does not produce phantom diffs. diff --git a/bundle/direct/dresources/grants.go b/bundle/direct/dresources/grants.go index 3aa48c58279..3fac2e0800b 100644 --- a/bundle/direct/dresources/grants.go +++ b/bundle/direct/dresources/grants.go @@ -75,6 +75,7 @@ func (r *ResourceGrants) PrepareInputConfig(inputConfig *[]catalog.PrivilegeAssi Refs: map[string]string{ "full_name": "${" + baseNode + ".id}", }, + Locations: nil, }, nil } diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index f9c0a2314ac..b763a08fd05 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -15,11 +15,11 @@ import ( // Precalculated paths for HasChange checks var ( - pathTags = structpath.MustParsePath("tags") - pathAiGateway = structpath.MustParsePath("ai_gateway") - pathConfig = structpath.MustParsePath("config") - pathEmailNotifications = structpath.MustParsePath("email_notifications") - pathTelemetryConfig = structpath.MustParsePath("telemetry_config") + pathTags = structpath.NewPath(nil, "tags") + pathAiGateway = structpath.NewPath(nil, "ai_gateway") + pathConfig = structpath.NewPath(nil, "config") + pathEmailNotifications = structpath.NewPath(nil, "email_notifications") + pathTelemetryConfig = structpath.NewPath(nil, "telemetry_config") ) type ResourceModelServingEndpoint struct { diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index c2aebb4d0ac..b09e9cfe2ff 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -116,6 +116,7 @@ func (r *ResourcePermissions) PrepareInputConfig(inputConfig any, resourceKey st Refs: map[string]string{ "object_id": r.objectType + "${" + baseNode + "." + r.idField + "}", }, + Locations: nil, }, nil } diff --git a/bundle/direct/dresources/secret_scope_acls.go b/bundle/direct/dresources/secret_scope_acls.go index 1de301bde2c..61269ce4097 100644 --- a/bundle/direct/dresources/secret_scope_acls.go +++ b/bundle/direct/dresources/secret_scope_acls.go @@ -60,6 +60,7 @@ func (*ResourceSecretScopeAcls) PrepareInputConfig(inputConfig *[]resources.Secr Refs: map[string]string{ "scope_name": "${" + baseNode + ".name}", }, + Locations: nil, }, nil } diff --git a/bundle/direct/dresources/serialize_test.go b/bundle/direct/dresources/serialize_test.go index 76078460193..e1bd30579c6 100644 --- a/bundle/direct/dresources/serialize_test.go +++ b/bundle/direct/dresources/serialize_test.go @@ -130,7 +130,7 @@ func TestRoundtripAllFieldsRemoteType(t *testing.T) { // TestRoundtripAllFieldsInputConfigType verifies InputConfigType, the typed // bundle config a resource is loaded into, survives a JSON round-trip with every -// field populated. Bundle config is normally read and written through libs/dyn, +// field populated. Bundle config is normally read and written through libs/structs/structvar, // which walks the struct itself and never calls these marshalers, so this is a // latent trap rather than live corruption. It is guarded anyway because it is the // same trap as StateType and RemoteType: a resource that embeds a member with its diff --git a/bundle/direct/dresources/vector_search_endpoint.go b/bundle/direct/dresources/vector_search_endpoint.go index 12470872b62..3775f793b60 100644 --- a/bundle/direct/dresources/vector_search_endpoint.go +++ b/bundle/direct/dresources/vector_search_endpoint.go @@ -13,8 +13,8 @@ import ( ) var ( - pathBudgetPolicyId = structpath.MustParsePath("budget_policy_id") - pathTargetQps = structpath.MustParsePath("target_qps") + pathBudgetPolicyId = structpath.NewPath(nil, "budget_policy_id") + pathTargetQps = structpath.NewPath(nil, "target_qps") ) // VectorSearchEndpointRemote is remote state for a vector search endpoint. It embeds API response diff --git a/bundle/direct/resolve_config.go b/bundle/direct/resolve_config.go index 8ce28d710e1..3d8727faf2c 100644 --- a/bundle/direct/resolve_config.go +++ b/bundle/direct/resolve_config.go @@ -1,14 +1,18 @@ package direct import ( + "fmt" + "maps" + "slices" + "github.com/databricks/cli/bundle/config" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/dynvar" - "github.com/databricks/cli/libs/dyn/jsonloader" + "github.com/databricks/cli/bundle/config/walk" + "github.com/databricks/cli/libs/structs/structaccess" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) -var resourcesPrefix = dyn.MustPathFromString("resources") +var resourcesPrefix = structpath.NewPath(nil, "resources") // ResolveConfigAgainstState resolves ${resources.*} references within the resource at // target so its runner ("bundle run") sees concrete values rather than references. For a @@ -24,48 +28,102 @@ var resourcesPrefix = dyn.MustPathFromString("resources") // // The state DB holds fields like the immutable snapshot's full_path that never reach the // config, so it must be open. -func (b *DeploymentBundle) ResolveConfigAgainstState(cfg *config.Root, target dyn.Path) error { - return cfg.Mutate(func(root dyn.Value) (dyn.Value, error) { - // Fall back to the fully-normalized config so references to fields that are - // implied (not explicitly set) still resolve. - normalized, _ := convert.Normalize(cfg, root, convert.IncludeMissingFields) +func (b *DeploymentBundle) ResolveConfigAgainstState(cfg *config.Root, target *structpath.PathNode) error { + typ, name := target.KeyAt(1), target.KeyAt(2) + if _, ok := cfg.Resources.GetResource(typ, name); !ok { + return fmt.Errorf("resource %s not found in configuration", target) + } + view := cfg.View() + + // Collect every reference string first: resolving them in one call shares the + // lookups, and writing back changes the view. + templates := map[string]structvar.Template{} + paths := map[string]*structpath.PathNode{} + + lookup := func(path *structpath.PathNode) (structvar.View, error) { + if !path.HasPrefix(resourcesPrefix) { + return structvar.View{}, structvar.ErrSkipResolution + } + if v, ok := b.lookupStateField(path); ok { + return v, nil + } + // Fall back to the config, including fields that are implied (not explicitly set). + if v := view.Lookup(path); v.IsValid() { + return v, nil + } + got, err := structaccess.Get(cfg, path) + if err != nil { + return structvar.View{}, structvar.ErrSkipResolution + } + return structvar.NewView(&got, nil, nil), nil + } - return dyn.MapByPath(root, target, func(_ dyn.Path, resource dyn.Value) (dyn.Value, error) { - return dynvar.Resolve(resource, func(path dyn.Path) (dyn.Value, error) { - if !path.HasPrefix(resourcesPrefix) { - return dyn.InvalidValue, dynvar.ErrSkipResolution - } - if v, ok := b.lookupStateField(path); ok { - return v, nil - } - v, err := dyn.GetByPath(normalized, path) - if err != nil { - return dyn.InvalidValue, dynvar.ErrSkipResolution - } - return v, nil - }) - }) + walk.ResourceStringsOf(cfg, typ, name, walk.WithReferences, func(n *walk.Node, s string) (string, bool) { + addTemplate(templates, paths, target, n.Path(), s) + return "", false }) + for p, ref := range cfg.ReferencesUnder(target) { + addTemplate(templates, paths, target, p, ref) + } + + resolved, err := structvar.Resolve(templates, lookup) + if err != nil { + return err + } + + // Snapshot all values before writing: a resolved view may point into the configuration. + values := map[string]any{} + for key, v := range resolved { + values[key] = v.AsAny() + } + for _, key := range slices.Sorted(maps.Keys(values)) { + value := values[key] + // Decode converts like loading did, e.g. a string id into an int job_id. + diags, err := cfg.Decode(paths[key], structvar.NewView(&value, nil, nil)) + if err != nil { + return err + } + if diags.HasError() { + return diags.Error() + } + } + return nil +} + +// addTemplate adds the string s at path to templates if it has references, keyed by its +// path relative to target. +func addTemplate(templates map[string]structvar.Template, paths map[string]*structpath.PathNode, target, path *structpath.PathNode, s string) { + if _, ok := structvar.NewRef(s); !ok { + return + } + key := path.SkipPrefix(target.Len()).String() + templates[key] = structvar.Template{Value: s} + paths[key] = path } // lookupStateField returns the value at resources... from the // resource's persisted state, if that resource is in state and holds the field. -func (b *DeploymentBundle) lookupStateField(path dyn.Path) (dyn.Value, bool) { - if len(path) < 4 || path[0].Key() != "resources" { - return dyn.InvalidValue, false +func (b *DeploymentBundle) lookupStateField(path *structpath.PathNode) (structvar.View, bool) { + if path.Len() < 4 || path.KeyAt(0) != "resources" { + return structvar.View{}, false } - resourceKey := "resources." + path[1].Key() + "." + path[2].Key() + resourceKey := path.Prefix(3).String() entry, ok := b.StateDB.GetResourceEntry(resourceKey) if !ok || len(entry.State) == 0 { - return dyn.InvalidValue, false + return structvar.View{}, false } - stateVal, err := jsonloader.LoadJSON(entry.State, resourceKey) + // ParseJSON keeps large ids exact. + node, err := structvar.ParseJSON(resourceKey, entry.State) if err != nil { - return dyn.InvalidValue, false + return structvar.View{}, false + } + var state any + if _, _, err := structvar.DecodeYAMLNode(resourceKey, node, &state, nil); err != nil { + return structvar.View{}, false } - fieldVal, err := dyn.GetByPath(stateVal, path[3:]) - if err != nil || !fieldVal.IsValid() { - return dyn.InvalidValue, false + v := structvar.NewView(&state, nil, nil).Lookup(path.SkipPrefix(3)) + if !v.IsValid() { + return structvar.View{}, false } - return fieldVal, true + return v, true } diff --git a/bundle/direct/unescape.go b/bundle/direct/unescape.go index 31f9925e098..40ec5d02bed 100644 --- a/bundle/direct/unescape.go +++ b/bundle/direct/unescape.go @@ -5,9 +5,9 @@ import ( "reflect" "strings" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structaccess" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/cli/libs/structs/structwalk" ) @@ -24,7 +24,7 @@ func unescapeRefs(state any) error { return } paths = append(paths, path) - values = append(values, dynvar.Unescape(s)) + values = append(values, structvar.Unescape(s)) }) if err != nil { return err diff --git a/bundle/generate/alert.go b/bundle/generate/alert.go index eba68126dd8..d3765205d17 100644 --- a/bundle/generate/alert.go +++ b/bundle/generate/alert.go @@ -1,19 +1,16 @@ package generate import ( - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/sql" ) -func ConvertAlertToValue(alert *sql.AlertV2, filePath string) (dyn.Value, error) { +func ConvertAlertToValue(alert *sql.AlertV2, filePath string) (structyaml.Map, error) { // The majority of fields of the alert struct are present in .dbalert.json file. // We copy the relevant fields manually. - dv := map[string]dyn.Value{ - "display_name": dyn.NewValue(alert.DisplayName, []diag.Location{{Line: 1}}), - "warehouse_id": dyn.NewValue(alert.WarehouseId, []diag.Location{{Line: 2}}), - "file_path": dyn.NewValue(filePath, []diag.Location{{Line: 3}}), - } - - return dyn.V(dv), nil + return structyaml.M( + "display_name", alert.DisplayName, + "warehouse_id", alert.WarehouseId, + "file_path", filePath, + ), nil } diff --git a/bundle/generate/app.go b/bundle/generate/app.go index 0714fc9b324..a0f40a5a4ef 100644 --- a/bundle/generate/app.go +++ b/bundle/generate/app.go @@ -1,54 +1,47 @@ package generate import ( - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/apps" ) -func ConvertAppToValue(app *apps.App, sourceCodePath string) (dyn.Value, error) { - ar, err := convert.FromTyped(app.Resources, dyn.NilValue) - if err != nil { - return dyn.NilValue, err - } - +func ConvertAppToValue(app *apps.App, sourceCodePath string) (structyaml.Map, error) { // The majority of fields of the app struct are read-only. // We copy the relevant fields manually. - dv := map[string]dyn.Value{ - "name": dyn.NewValue(app.Name, []diag.Location{{Line: 1}}), - "description": dyn.NewValue(app.Description, []diag.Location{{Line: 2}}), - } + dv := structyaml.M( + "name", app.Name, + "description", app.Description, + ) // For a git-backed app, emit git_repository + git_source instead of a // workspace source_code_path. Otherwise the generated bundle would silently // down-convert the app to workspace source and point source_code_path at a // local directory that has nothing downloaded into it. if app.GitRepository != nil { - dv["git_repository"] = gitRepositoryValue(app.GitRepository) - if gs := gitSourceValue(app); gs.Kind() != dyn.KindNil { - dv["git_source"] = gs + dv.Add("git_repository", gitRepositoryValue(app.GitRepository)) + if gs := gitSourceValue(app); len(gs) > 0 { + dv.Add("git_source", gs) } } else { - dv["source_code_path"] = dyn.NewValue(sourceCodePath, []diag.Location{{Line: 4}}) + dv.Add("source_code_path", sourceCodePath) } - if ar.Kind() != dyn.KindNil { - dv["resources"] = ar.WithLocations([]diag.Location{{Line: 5}}) + if ar := structyaml.Value(app.Resources); ar != nil { + dv.Add("resources", ar) } - return dyn.V(dv), nil + return dv, nil } -func gitRepositoryValue(r *apps.GitRepository) dyn.Value { - m := map[string]dyn.Value{ - "url": dyn.NewValue(r.Url, []diag.Location{{Line: 1}}), - "provider": dyn.NewValue(r.Provider, []diag.Location{{Line: 2}}), - } +func gitRepositoryValue(r *apps.GitRepository) structyaml.Map { + m := structyaml.M( + "url", r.Url, + "provider", r.Provider, + ) if r.AutoDeploy { - m["auto_deploy"] = dyn.NewValue(r.AutoDeploy, []diag.Location{{Line: 3}}) + m.Add("auto_deploy", r.AutoDeploy) } - return dyn.NewValue(m, []diag.Location{{Line: 3}}) + return m } // gitSourceValue returns the reference the app deploys from (branch, tag, or @@ -56,29 +49,26 @@ func gitRepositoryValue(r *apps.GitRepository) dyn.Value { // configured git_source and falls back to the default source of the app's most // recent deployment. System-populated fields (resolved_commit and the nested // git_repository) are intentionally omitted. -func gitSourceValue(app *apps.App) dyn.Value { +func gitSourceValue(app *apps.App) structyaml.Map { src := app.GitSource if src == nil { src = app.DefaultGitSource } if src == nil { - return dyn.NilValue + return nil } - m := map[string]dyn.Value{} + var m structyaml.Map switch { case src.Branch != "": - m["branch"] = dyn.NewValue(src.Branch, []diag.Location{{Line: 1}}) + m.Add("branch", src.Branch) case src.Tag != "": - m["tag"] = dyn.NewValue(src.Tag, []diag.Location{{Line: 1}}) + m.Add("tag", src.Tag) case src.Commit != "": - m["commit"] = dyn.NewValue(src.Commit, []diag.Location{{Line: 1}}) + m.Add("commit", src.Commit) } if src.SourceCodePath != "" { - m["source_code_path"] = dyn.NewValue(src.SourceCodePath, []diag.Location{{Line: 2}}) - } - if len(m) == 0 { - return dyn.NilValue + m.Add("source_code_path", src.SourceCodePath) } - return dyn.NewValue(m, []diag.Location{{Line: 4}}) + return m } diff --git a/bundle/generate/dashboard.go b/bundle/generate/dashboard.go index 6f045a39cb2..a12e8b57f5d 100644 --- a/bundle/generate/dashboard.go +++ b/bundle/generate/dashboard.go @@ -1,19 +1,16 @@ package generate import ( - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/dashboards" ) -func ConvertDashboardToValue(dashboard *dashboards.Dashboard, filePath string) (dyn.Value, error) { +func ConvertDashboardToValue(dashboard *dashboards.Dashboard, filePath string) (structyaml.Map, error) { // The majority of fields of the dashboard struct are read-only. // We copy the relevant fields manually. - dv := map[string]dyn.Value{ - "display_name": dyn.NewValue(dashboard.DisplayName, []diag.Location{{Line: 1}}), - "warehouse_id": dyn.NewValue(dashboard.WarehouseId, []diag.Location{{Line: 2}}), - "file_path": dyn.NewValue(filePath, []diag.Location{{Line: 3}}), - } - - return dyn.V(dv), nil + return structyaml.M( + "display_name", dashboard.DisplayName, + "warehouse_id", dashboard.WarehouseId, + "file_path", filePath, + ), nil } diff --git a/bundle/generate/genie_space.go b/bundle/generate/genie_space.go index 639c65f314b..88cbe8b05d9 100644 --- a/bundle/generate/genie_space.go +++ b/bundle/generate/genie_space.go @@ -1,29 +1,28 @@ package generate import ( - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/dashboards" ) -func ConvertGenieSpaceToValue(genieSpace *dashboards.GenieSpace, filePath string) (dyn.Value, error) { +func ConvertGenieSpaceToValue(genieSpace *dashboards.GenieSpace, filePath string) (structyaml.Map, error) { // Emit only the fields a user authors in a bundle. serialized_space is // written to a separate file and referenced via file_path, and output-only // fields (e.g. space_id, etag) must not appear in the generated config, so // we build the value field by field rather than marshaling the struct. - dv := map[string]dyn.Value{ - "title": dyn.NewValue(genieSpace.Title, []diag.Location{{Line: 1}}), - "warehouse_id": dyn.NewValue(genieSpace.WarehouseId, []diag.Location{{Line: 2}}), - "file_path": dyn.NewValue(filePath, []diag.Location{{Line: 3}}), - } + dv := structyaml.M( + "title", genieSpace.Title, + "warehouse_id", genieSpace.WarehouseId, + "file_path", filePath, + ) if genieSpace.Description != "" { - dv["description"] = dyn.NewValue(genieSpace.Description, []diag.Location{{Line: 4}}) + dv.Add("description", genieSpace.Description) } if genieSpace.ParentPath != "" { - dv["parent_path"] = dyn.NewValue(ensureWorkspacePrefix(genieSpace.ParentPath), []diag.Location{{Line: 5}}) + dv.Add("parent_path", ensureWorkspacePrefix(genieSpace.ParentPath)) } - return dyn.V(dv), nil + return dv, nil } diff --git a/bundle/generate/job.go b/bundle/generate/job.go index 801eee1f5bd..b1784258e1c 100644 --- a/bundle/generate/job.go +++ b/bundle/generate/job.go @@ -1,50 +1,51 @@ package generate import ( - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/jobs" ) var ( - jobOrder = yamlsaver.NewOrder([]string{"name", "job_clusters", "compute", "tasks"}) - taskOrder = yamlsaver.NewOrder([]string{"task_key", "depends_on", "existing_cluster_id", "new_cluster", "job_cluster_key"}) + jobOrder = []string{"name", "job_clusters", "compute", "tasks", "parameters"} + taskOrder = []string{"task_key", "depends_on", "existing_cluster_id", "new_cluster", "job_cluster_key"} ) -func ConvertJobToValue(job *jobs.Job) (dyn.Value, error) { - value := make(map[string]dyn.Value) +func ConvertJobToValue(job *jobs.Job) (structyaml.Map, error) { + // Tasks and parameters are processed separately. + value, err := structyaml.Struct(job.Settings, "format", "new_cluster", "existing_cluster_id", "tasks", "parameters") + if err != nil { + return nil, err + } + if job.Settings.Tasks != nil { - var tasks []dyn.Value + var tasks []any for _, task := range job.Settings.Tasks { - v, err := convertTaskToValue(task, taskOrder) + v, err := convertTaskToValue(task) if err != nil { - return dyn.InvalidValue, err + return nil, err } tasks = append(tasks, v) } - // We're using location lines to define the order of keys in exported YAML. - value["tasks"] = dyn.NewValue(tasks, []diag.Location{{Line: jobOrder.Get("tasks")}}) + value.Add("tasks", tasks) } // We're processing job.Settings.Parameters separately to retain empty default values. if len(job.Settings.Parameters) > 0 { - var params []dyn.Value + var params []any for _, parameter := range job.Settings.Parameters { - p := map[string]dyn.Value{ - "name": dyn.NewValue(parameter.Name, []diag.Location{{Line: 0}}), // We use Line: 0 to ensure that the name goes first. - "default": dyn.NewValue(parameter.Default, []diag.Location{{Line: 1}}), - } - params = append(params, dyn.V(p)) + params = append(params, structyaml.M("name", parameter.Name, "default", parameter.Default)) } - value["parameters"] = dyn.NewValue(params, []diag.Location{{Line: jobOrder.Get("parameters")}}) + value.Add("parameters", params) } - return yamlsaver.ConvertToMapValue(job.Settings, jobOrder, []string{"format", "new_cluster", "existing_cluster_id"}, value) + return value.Order(jobOrder...), nil } -func convertTaskToValue(task jobs.Task, order *yamlsaver.Order) (dyn.Value, error) { - dst := make(map[string]dyn.Value) - return yamlsaver.ConvertToMapValue(task, order, []string{"format"}, dst) +func convertTaskToValue(task jobs.Task) (structyaml.Map, error) { + value, err := structyaml.Struct(task, "format") + if err != nil { + return nil, err + } + return value.Order(taskOrder...), nil } diff --git a/bundle/generate/pipeline.go b/bundle/generate/pipeline.go index 115330ea0f6..beb8bf2e5ad 100644 --- a/bundle/generate/pipeline.go +++ b/bundle/generate/pipeline.go @@ -3,14 +3,13 @@ package generate import ( "strings" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/pipelines" ) -var pipelineOrder = yamlsaver.NewOrder([]string{"name", "clusters", "configuration", "libraries"}) +var pipelineOrder = []string{"name", "clusters", "configuration", "libraries"} -func ConvertPipelineToValue(pipeline *pipelines.PipelineSpec, rootPath, remoteRootPath string) (dyn.Value, error) { +func ConvertPipelineToValue(pipeline *pipelines.PipelineSpec, rootPath, remoteRootPath string) (structyaml.Map, error) { if pipeline.RootPath != "" { pipeline.RootPath = rootPath } @@ -24,11 +23,14 @@ func ConvertPipelineToValue(pipeline *pipelines.PipelineSpec, rootPath, remoteRo } } - value := make(map[string]dyn.Value) // We ignore the following fields: // - id: this is a read-only field // - storage: changes to this field are rare because changing the storage recreates pipeline-related resources // - edition: this field is rarely changed // - development: this field is specific to the mode where it's used and does not need to be saved to the bundle configuration. - return yamlsaver.ConvertToMapValue(pipeline, pipelineOrder, []string{"id", "storage", "edition", "development"}, value) + value, err := structyaml.Struct(pipeline, "id", "storage", "edition", "development") + if err != nil { + return nil, err + } + return value.Order(pipelineOrder...), nil } diff --git a/bundle/internal/bundletest/benchmark.go b/bundle/internal/bundletest/benchmark.go index 59647d814fd..2cb4b738652 100644 --- a/bundle/internal/bundletest/benchmark.go +++ b/bundle/internal/bundletest/benchmark.go @@ -9,7 +9,6 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/require" ) @@ -210,32 +209,6 @@ const jobExample = ` } }` -func BundleV(b *testing.B, numJobs int) dyn.Value { - allJobs := map[string]*resources.Job{} - for i := range numJobs { - job := jobs.JobSettings{} - err := json.Unmarshal([]byte(jobExample), &job) - require.NoError(b, err) - - allJobs[strconv.Itoa(i)] = &resources.Job{ - JobSettings: job, - } - } - - myBundle := bundle.Bundle{ - Config: config.Root{ - Resources: config.Resources{ - Jobs: allJobs, - }, - }, - } - - // Apply noop mutator to initialize the bundle value. - bundle.ApplyFuncContext(b.Context(), &myBundle, func(ctx context.Context, b *bundle.Bundle) {}) - - return myBundle.Config.Value() -} - func Bundle(b *testing.B, numJobs int) *bundle.Bundle { allJobs := map[string]*resources.Job{} for i := range numJobs { diff --git a/bundle/internal/bundletest/benchmark_test.go b/bundle/internal/bundletest/benchmark_test.go index 05999daf6fb..df8d3659a4b 100644 --- a/bundle/internal/bundletest/benchmark_test.go +++ b/bundle/internal/bundletest/benchmark_test.go @@ -3,30 +3,18 @@ package bundletest import ( "testing" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/stretchr/testify/assert" ) -// This took 40ms to run on 18th June 2025. func BenchmarkWalkReadOnly(b *testing.B) { - input := BundleV(b, 10000) + input := Bundle(b, 10000).Config.View() for b.Loop() { - err := dyn.WalkReadOnly(input, func(p dyn.Path, v dyn.Value) error { + err := structvar.Walk(input, func(p *structpath.PathNode, v structvar.View) error { return nil }) assert.NoError(b, err) } } - -// This took 160ms to run on 18th June 2025. -func BenchmarkWalk(b *testing.B) { - input := BundleV(b, 10000) - - for b.Loop() { - _, err := dyn.Walk(input, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - return v, nil - }) - assert.NoError(b, err) - } -} diff --git a/bundle/internal/bundletest/component_benchmark_test.go b/bundle/internal/bundletest/component_benchmark_test.go new file mode 100644 index 00000000000..b7b33956540 --- /dev/null +++ b/bundle/internal/bundletest/component_benchmark_test.go @@ -0,0 +1,125 @@ +package bundletest + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "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/validate" + "github.com/databricks/cli/bundle/libraries" + "github.com/stretchr/testify/require" +) + +// The benchmarks below run one mutator (or loading) on a bundle with 1000 jobs. They +// only use APIs that also exist in the libs/dyn implementation, so the same file can +// be run there to compare components one by one. + +const componentJobs = 1000 + +func benchmarkMutator(b *testing.B, m bundle.Mutator) { + myBundle := Bundle(b, componentJobs) + myBundle.BundleRootPath = b.TempDir() + myBundle.SyncRootPath = myBundle.BundleRootPath + for b.Loop() { + bundle.Apply(b.Context(), myBundle, m) + } +} + +// BenchmarkComponentNoop measures the cost bundle.Apply adds around every mutator, so +// it can be subtracted from the other results. +func BenchmarkComponentNoop(b *testing.B) { + myBundle := Bundle(b, componentJobs) + for b.Loop() { + bundle.ApplyFuncContext(b.Context(), myBundle, func(context.Context, *bundle.Bundle) {}) + } +} + +func BenchmarkComponentRequired(b *testing.B) { benchmarkMutator(b, validate.Required()) } + +func BenchmarkComponentEnum(b *testing.B) { benchmarkMutator(b, validate.Enum()) } + +func BenchmarkComponentSingleNodeCluster(b *testing.B) { + benchmarkMutator(b, validate.SingleNodeCluster()) +} + +func BenchmarkComponentTFOnlyReferences(b *testing.B) { + benchmarkMutator(b, validate.TFOnlyReferences()) +} + +func BenchmarkComponentTranslatePaths(b *testing.B) { benchmarkMutator(b, mutator.TranslatePaths()) } + +func BenchmarkComponentNormalizePaths(b *testing.B) { benchmarkMutator(b, mutator.NormalizePaths()) } + +func BenchmarkComponentRewriteWorkspacePrefix(b *testing.B) { + benchmarkMutator(b, mutator.RewriteWorkspacePrefix()) +} + +func BenchmarkComponentPrependWorkspacePrefix(b *testing.B) { + benchmarkMutator(b, mutator.PrependWorkspacePrefix()) +} + +func BenchmarkComponentDropEmptyStrings(b *testing.B) { + benchmarkMutator(b, mutator.DropEmptyStrings()) +} + +func BenchmarkComponentResolveVariableReferences(b *testing.B) { + benchmarkMutator(b, mutator.ResolveVariableReferencesOnlyResources("bundle", "workspace", "variables")) +} + +func BenchmarkComponentExpandGlobReferences(b *testing.B) { + benchmarkMutator(b, libraries.ExpandGlobReferences()) +} + +// componentLoadInput returns a configuration file with 1000 jobs (JSON is valid YAML). +func componentLoadInput(b *testing.B) []byte { + var job map[string]any + require.NoError(b, json.Unmarshal([]byte(jobExample), &job)) + jobs := map[string]any{} + for i := range componentJobs { + jobs[fmt.Sprintf("job_%d", i)] = job + } + raw, err := json.Marshal(map[string]any{ + "bundle": map[string]any{"name": "benchmark"}, + "resources": map[string]any{"jobs": jobs}, + }) + require.NoError(b, err) + require.True(b, strings.HasPrefix(string(raw), "{")) + return raw +} + +// BenchmarkComponentLoad loads a configuration file with 1000 jobs into the typed +// configuration: [BenchmarkComponentLoadDyn] plus [BenchmarkComponentFinishLoading]. +func BenchmarkComponentLoad(b *testing.B) { + raw := componentLoadInput(b) + for b.Loop() { + _, diags := config.LoadFromBytes("databricks.yml", raw) + require.False(b, diags.HasError(), "%v", diags) + } +} + +// BenchmarkComponentLoadDyn loads a configuration file with 1000 jobs into a loading +// configuration (configuration tree), like the load phase does for every file. +func BenchmarkComponentLoadDyn(b *testing.B) { + raw := componentLoadInput(b) + for b.Loop() { + _, diags := config.LoadDynFromBytes("databricks.yml", raw) + require.False(b, diags.HasError(), "%v", diags) + } +} + +// BenchmarkComponentFinishLoading converts a loading configuration with 1000 jobs into +// the typed configuration, which the load phase does once. +func BenchmarkComponentFinishLoading(b *testing.B) { + r, diags := config.LoadDynFromBytes("databricks.yml", componentLoadInput(b)) + require.False(b, diags.HasError(), "%v", diags) + for b.Loop() { + c := *r + _, err := c.FinishLoading() + require.NoError(b, err) + } +} diff --git a/bundle/internal/bundletest/loading.go b/bundle/internal/bundletest/loading.go new file mode 100644 index 00000000000..b2bb93b890d --- /dev/null +++ b/bundle/internal/bundletest/loading.go @@ -0,0 +1,20 @@ +package bundletest + +import ( + "testing" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/libs/diag" + "github.com/stretchr/testify/require" +) + +// ApplyLoading applies mutators of the load phase, which work on the configuration +// tree, to b (whose configuration may be typed) and converts the result into the +// typed configuration, like the load phase does. +func ApplyLoading(t testing.TB, b *bundle.Bundle, mutators ...bundle.Mutator) diag.Diagnostics { + require.NoError(t, b.Config.StartLoading()) + diags := bundle.ApplySeq(t.Context(), b, mutators...) + finishDiags, err := b.Config.FinishLoading() + require.NoError(t, err) + return diags.Extend(finishDiags) +} diff --git a/bundle/internal/bundletest/location.go b/bundle/internal/bundletest/location.go index b96cab0907d..7290a8ae2a9 100644 --- a/bundle/internal/bundletest/location.go +++ b/bundle/internal/bundletest/location.go @@ -3,32 +3,17 @@ package bundletest import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) // SetLocation sets the location of all values in the bundle to the given path. // This is useful for testing where we need to associate configuration // with the path it is loaded from. func SetLocation(b *bundle.Bundle, prefix string, locations []diag.Location) { - start := dyn.MustPathFromString(prefix) - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - return dyn.Walk(root, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - // If the path has the given prefix, set the location. - if p.HasPrefix(start) { - return v.WithLocations(locations), nil - } - - // The path is not nested under the given prefix. - // If the path is a prefix of the prefix, keep traversing and return the node verbatim. - if start.HasPrefix(p) { - return v, nil - } - - // Return verbatim, but skip traversal. - return v, dyn.ErrSkip - }) - }) - if err != nil { - panic("Mutate() failed: " + err.Error()) + // "." is the root of the configuration. + var path *structpath.PathNode + if prefix != "." { + path = structpath.MustParsePath(prefix) } + b.Config.SetLocations(path, locations) } diff --git a/bundle/internal/bundletest/mutate.go b/bundle/internal/bundletest/mutate.go deleted file mode 100644 index 00adc7d29e0..00000000000 --- a/bundle/internal/bundletest/mutate.go +++ /dev/null @@ -1,17 +0,0 @@ -package bundletest - -import ( - "context" - "testing" - - "github.com/databricks/cli/bundle" - "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.ApplyFuncContext(t.Context(), b, func(ctx context.Context, b *bundle.Bundle) { - err := b.Config.Mutate(f) - require.NoError(t, err) - }) -} diff --git a/bundle/internal/bundletest/mutator_benchmark_test.go b/bundle/internal/bundletest/mutator_benchmark_test.go index 03d63dcd072..5e60f0cfab0 100644 --- a/bundle/internal/bundletest/mutator_benchmark_test.go +++ b/bundle/internal/bundletest/mutator_benchmark_test.go @@ -7,7 +7,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/validate" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/stretchr/testify/assert" ) @@ -35,9 +36,9 @@ func benchmarkWalkReadOnlyBaseline(b *testing.B, numJobs int) { myBundle := Bundle(b, numJobs) for b.Loop() { - var paths []dyn.Path + var paths []*structpath.PathNode bundle.ApplyFuncContext(b.Context(), myBundle, func(ctx context.Context, b *bundle.Bundle) { - _ = dyn.WalkReadOnly(b.Config.Value(), func(p dyn.Path, v dyn.Value) error { + _ = structvar.Walk(b.Config.View(), func(p *structpath.PathNode, v structvar.View) error { paths = append(paths, p) return nil }) diff --git a/bundle/internal/schema/annotations.go b/bundle/internal/schema/annotations.go index 548cf5ae95f..894930f5491 100644 --- a/bundle/internal/schema/annotations.go +++ b/bundle/internal/schema/annotations.go @@ -2,15 +2,14 @@ package main import ( "fmt" + "maps" "reflect" "regexp" + "slices" "strings" "github.com/databricks/cli/bundle/internal/annotation" "github.com/databricks/cli/internal/clijson" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/merge" "github.com/databricks/cli/libs/jsonschema" ) @@ -27,12 +26,8 @@ type annotationHandler struct { // Adds annotations to the JSON schema reading from the annotation files. // More details https://json-schema.org/understanding-json-schema/reference/annotations func newAnnotationHandler(extracted, fromFile annotation.File) (*annotationHandler, error) { - merged, err := mergeAnnotationFiles(extracted, fromFile) - if err != nil { - return nil, err - } return &annotationHandler{ - parsedAnnotations: merged, + parsedAnnotations: mergeAnnotationFiles(extracted, fromFile), fileAnnotations: fromFile, missingAnnotations: annotation.File{}, }, nil @@ -79,25 +74,66 @@ func isEmptyDescriptor(d annotation.Descriptor) bool { // mergeAnnotationFiles merges later layers over earlier ones with the same // semantics the on-disk annotation files used to be merged with: maps merge // recursively, scalars take the later value, sequences concatenate. -func mergeAnnotationFiles(files ...annotation.File) (annotation.File, error) { - prev := dyn.NilValue +func mergeAnnotationFiles(files ...annotation.File) annotation.File { + merged := annotation.File{} for _, f := range files { - v, err := convert.FromTyped(f, dyn.NilValue) - if err != nil { - return nil, err + for typeKey, ta := range f { + merged.SetSelf(typeKey, mergeDescriptor(merged[typeKey].Self, ta.Self)) + for name, d := range ta.Fields { + merged.SetField(typeKey, name, mergeDescriptor(merged[typeKey].Fields[name], d)) + } } - prev, err = merge.Merge(prev, v) - if err != nil { - return nil, err + } + return merged +} + +// mergeDescriptor merges the set fields of b over a: maps merge by key, sequences +// concatenate and other values are replaced. +func mergeDescriptor(a, b annotation.Descriptor) annotation.Descriptor { + dst, src := reflect.ValueOf(&a).Elem(), reflect.ValueOf(b) + for i := range dst.NumField() { + f, g := dst.Field(i), src.Field(i) + switch { + case g.IsZero(): + case f.Kind() == reflect.Slice: + f.Set(reflect.AppendSlice(reflect.AppendSlice(reflect.Zero(f.Type()), f), g)) + case f.Kind() == reflect.Interface: + f.Set(reflect.ValueOf(mergeAny(f.Interface(), g.Interface()))) + case f.Kind() == reflect.Map: + m := reflect.MakeMap(f.Type()) + for _, v := range []reflect.Value{f, g} { + for iter := v.MapRange(); iter.Next(); { + m.SetMapIndex(iter.Key(), iter.Value()) + } + } + f.Set(m) + default: + f.Set(g) } } + return a +} - var data annotation.File - err := convert.ToTyped(&data, prev) - if err != nil { - return nil, err +// mergeAny merges b over a like mergeDescriptor merges fields: maps merge by key +// (recursively), sequences concatenate and other values are replaced. +func mergeAny(a, b any) any { + switch bv := b.(type) { + case map[string]any: + av, ok := a.(map[string]any) + if !ok { + return b + } + out := maps.Clone(av) + for k, v := range bv { + out[k] = mergeAny(av[k], v) + } + return out + case []any: + if av, ok := a.([]any); ok { + return slices.Concat(av, bv) + } } - return data, nil + return b } func (d *annotationHandler) addAnnotations(typ reflect.Type, s jsonschema.Schema) jsonschema.Schema { @@ -125,10 +161,7 @@ func (d *annotationHandler) addAnnotations(typ reflect.Type, s jsonschema.Schema // descriptions for fields that have no documentation anywhere. Entries for // fields that no longer exist in the config are dropped with a warning. func (d *annotationHandler) syncWithMissingAnnotations(outputPath string, g *typeGraph) error { - updated, err := mergeAnnotationFiles(d.fileAnnotations, d.missingAnnotations) - if err != nil { - return err - } + updated := mergeAnnotationFiles(d.fileAnnotations, d.missingAnnotations) detached, err := saveAnnotationsFile(outputPath, updated, g) if err != nil { diff --git a/bundle/internal/schema/annotations_file.go b/bundle/internal/schema/annotations_file.go index 78e279f4476..df759fe29c4 100644 --- a/bundle/internal/schema/annotations_file.go +++ b/bundle/internal/schema/annotations_file.go @@ -2,20 +2,19 @@ package main import ( "bytes" + "cmp" + "errors" "fmt" "os" + "path/filepath" "reflect" "slices" + "strconv" "strings" - yaml3 "go.yaml.in/yaml/v3" - "github.com/databricks/cli/bundle/internal/annotation" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/yamlloader" - "github.com/databricks/cli/libs/dyn/yamlsaver" + "github.com/databricks/cli/libs/structs/structvar" + "go.yaml.in/yaml/v3" ) // fieldsKey nests a type's block of field nodes inside the node of a field @@ -34,14 +33,6 @@ const ( typeDocKey = "$type" ) -// lineTypeDoc and lineFields sort the "$type" and "$fields" keys after a -// node's inline descriptor keys, which the saver orders with small line -// numbers (see descriptorKeyOrder). -const ( - lineTypeDoc = 9999 - lineFields = 10000 -) - const annotationsFileHeader = `# This file contains the documentation the CLI owns for the bundle # configuration JSON schema: docs for fields that do not exist in the upstream # API spec (.codegen/cli.json), and overrides of upstream docs. Documentation @@ -82,26 +73,71 @@ var descriptorKeys = func() map[string]bool { // follow alphabetically. var descriptorKeyOrder = []string{"description", "markdown_description", "title", "default", "enum"} -// descriptorToMap serializes d into dst with its keys ordered. It returns the -// nil value (writing nothing) when d carries no content. -// -// The empty check relies on the dyn.NilValue reference: with a nil reference, -// FromTyped omits zero-valued fields, so an all-zero descriptor collapses to -// KindNil rather than an empty map. Do not pass a map reference here, or empty -// descriptors would start serializing as "{}". -func descriptorToMap(d annotation.Descriptor, dst map[string]dyn.Value) (dyn.Value, error) { - v, err := convert.FromTyped(d, dyn.NilValue) - if err != nil || v.Kind() == dyn.KindNil { - return dyn.NilValue, err - } - return yamlsaver.ConvertToMapValue(d, yamlsaver.NewOrder(descriptorKeyOrder), []string{}, dst) +// descriptorNodes serializes d into the key and value nodes of a mapping, with its +// keys ordered. It returns nothing when d carries no content. +func descriptorNodes(d annotation.Descriptor, style yaml.Style) []*yaml.Node { + var pairs [][2]*yaml.Node + for key, v := range structvar.NewView(d, nil, nil).MapItems() { + pairs = append(pairs, [2]*yaml.Node{{Kind: yaml.ScalarNode, Value: key, Style: style}, valueNode(v, style)}) + } + // Keys in descriptorKeyOrder come first, the others follow alphabetically. + rank := func(key string) int { + if i := slices.Index(descriptorKeyOrder, key); i >= 0 { + return i + } + return len(descriptorKeyOrder) + } + slices.SortFunc(pairs, func(a, b [2]*yaml.Node) int { + return cmp.Or(cmp.Compare(rank(a[0].Value), rank(b[0].Value)), cmp.Compare(a[0].Value, b[0].Value)) + }) + var out []*yaml.Node + for _, p := range pairs { + out = append(out, p[:]...) + } + return out } -// descriptorEmpty reports whether d carries no documentation. See -// descriptorToMap for why the nil reference is what makes this work. +// valueNode converts a value of a descriptor to a YAML node. +func valueNode(x structvar.View, style yaml.Style) *yaml.Node { + switch x.Kind() { + case structvar.KindMap: + n := &yaml.Node{Kind: yaml.MappingNode, Style: style} + for k, c := range x.MapItems() { + n.Content = append(n.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: k, Style: style}, valueNode(c, style)) + } + return n + case structvar.KindSequence: + n := &yaml.Node{Kind: yaml.SequenceNode, Style: style} + for _, c := range x.Sequence() { + n.Content = append(n.Content, valueNode(c, style)) + } + return n + case structvar.KindString: + s, _ := x.AsString() + // A string that reads as another scalar (bool, number) is quoted to stay a string. + if isScalarValueInString(s) { + style = yaml.DoubleQuotedStyle + } + return &yaml.Node{Kind: yaml.ScalarNode, Value: s, Style: style} + default: + return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprint(x.AsAny()), Style: style} + } +} + +func isScalarValueInString(s string) bool { + if s == "true" || s == "false" || s == "" { + return true + } + if _, err := strconv.ParseInt(s, 0, 64); err == nil { + return true + } + _, err := strconv.ParseFloat(s, 64) + return err == nil +} + +// descriptorEmpty reports whether d carries no documentation. func descriptorEmpty(d annotation.Descriptor) bool { - v, err := convert.FromTyped(d, dyn.NilValue) - return err == nil && v.Kind() == dyn.KindNil + return structvar.NewView(d, nil, nil).Kind() == structvar.KindNil } // loadAnnotationsFile reads the tree-format annotations file and flattens it @@ -113,10 +149,14 @@ func loadAnnotationsFile(path string, g *typeGraph) (annotation.File, []string, if err != nil { return nil, nil, err } - v, err := yamlloader.LoadYAML(path, bytes.NewBuffer(b)) + v, err := structvar.ParseYAML(bytes.NewReader(b)) if err != nil { return nil, nil, err } + if v == nil { + // An empty document has no annotations. + return annotation.File{}, nil, nil + } l := &fileLoader{graph: g, data: annotation.File{}} err = l.block(v, g.root, "") @@ -133,17 +173,14 @@ type fileLoader struct { } // block loads one type's block of field nodes. -func (l *fileLoader) block(v dyn.Value, typeKey, where string) error { - if v.Kind() == dyn.KindNil { - return nil - } - m, ok := v.AsMap() - if !ok { - return fmt.Errorf("%s: expected a mapping, got %s", where, v.Kind()) +func (l *fileLoader) block(v *yaml.Node, typeKey, where string) error { + pairs, err := mappingPairs(v, where) + if err != nil { + return err } - for _, pair := range m.Pairs() { - key := pair.Key.MustString() + for i := 0; i < len(pairs); i += 2 { + key := pairs[i].Value child := where + "." + key if where == "" { child = key @@ -154,7 +191,7 @@ func (l *fileLoader) block(v dyn.Value, typeKey, where string) error { l.unknown = append(l.unknown, child) continue } - err := l.node(pair.Value, typeKey, edge, child) + err := l.node(pairs[i+1], typeKey, edge, child) if err != nil { return err } @@ -165,26 +202,23 @@ func (l *fileLoader) block(v dyn.Value, typeKey, where string) error { // node loads one field's node: the inline descriptor for the field, the // "$type" docs for the type it resolves to, and the "$fields" block of that // type's fields. -func (l *fileLoader) node(v dyn.Value, typeKey string, edge fieldEdge, where string) error { - if v.Kind() == dyn.KindNil { - return nil - } - m, ok := v.AsMap() - if !ok { - return fmt.Errorf("%s: expected a mapping, got %s", where, v.Kind()) +func (l *fileLoader) node(v *yaml.Node, typeKey string, edge fieldEdge, where string) error { + pairs, err := mappingPairs(v, where) + if err != nil { + return err } - desc := dyn.NewMapping() - for _, pair := range m.Pairs() { - key := pair.Key.MustString() + var desc []*yaml.Node + for i := 0; i < len(pairs); i += 2 { + key, value := pairs[i].Value, pairs[i+1] switch { case key == fieldsKey && edge.typ != "": - err := l.block(pair.Value, edge.typ, where+"."+fieldsKey) + err := l.block(value, edge.typ, where+"."+fieldsKey) if err != nil { return err } case key == typeDocKey && edge.typ != "": - d, ok, err := l.descriptor(pair.Value, where+"."+typeDocKey) + d, ok, err := l.descriptor(value, where+"."+typeDocKey) if err != nil { return err } @@ -192,13 +226,13 @@ func (l *fileLoader) node(v dyn.Value, typeKey string, edge fieldEdge, where str l.data.SetSelf(edge.typ, d) } case descriptorKeys[key]: - desc.SetLoc(key, nil, pair.Value) + desc = append(desc, pairs[i], value) default: l.unknown = append(l.unknown, where+"."+key) } } - if desc.Len() > 0 { + if len(desc) > 0 { d, err := toDescriptor(desc, where) if err != nil { return err @@ -211,21 +245,20 @@ func (l *fileLoader) node(v dyn.Value, typeKey string, edge fieldEdge, where str // descriptor parses a mapping of descriptor keys (the value of a "$type" key). // Non-descriptor keys are flagged as unknown. The second return is false when // the mapping carries no descriptor keys. -func (l *fileLoader) descriptor(v dyn.Value, where string) (annotation.Descriptor, bool, error) { - m, ok := v.AsMap() - if !ok { - return annotation.Descriptor{}, false, fmt.Errorf("%s: expected a mapping, got %s", where, v.Kind()) - } - desc := dyn.NewMapping() - for _, pair := range m.Pairs() { - key := pair.Key.MustString() +func (l *fileLoader) descriptor(v *yaml.Node, where string) (annotation.Descriptor, bool, error) { + if v.Kind != yaml.MappingNode { + return annotation.Descriptor{}, false, fmt.Errorf("%s: expected a mapping, got %s", where, nodeKind(v)) + } + var desc []*yaml.Node + for i := 0; i < len(v.Content); i += 2 { + key := v.Content[i].Value if descriptorKeys[key] { - desc.SetLoc(key, nil, pair.Value) + desc = append(desc, v.Content[i], v.Content[i+1]) } else { l.unknown = append(l.unknown, where+"."+key) } } - if desc.Len() == 0 { + if len(desc) == 0 { return annotation.Descriptor{}, false, nil } d, err := toDescriptor(desc, where) @@ -233,15 +266,40 @@ func (l *fileLoader) descriptor(v dyn.Value, where string) (annotation.Descripto } // toDescriptor converts a mapping of descriptor keys to a typed descriptor. -func toDescriptor(desc dyn.Mapping, where string) (annotation.Descriptor, error) { +func toDescriptor(desc []*yaml.Node, where string) (annotation.Descriptor, error) { var d annotation.Descriptor - err := convert.ToTyped(&d, dyn.V(desc)) + _, diags, err := structvar.DecodeYAMLNode(where, &yaml.Node{Kind: yaml.MappingNode, Content: desc}, &d, nil) + if err == nil { + err = diags.Error() + } if err != nil { return annotation.Descriptor{}, fmt.Errorf("%s: %w", where, err) } return d, nil } +// mappingPairs returns the alternating key and value nodes of a mapping. A null node has none. +func mappingPairs(v *yaml.Node, where string) ([]*yaml.Node, error) { + switch { + case v.ShortTag() == "!!null": + return nil, nil + case v.Kind != yaml.MappingNode: + return nil, fmt.Errorf("%s: expected a mapping, got %s", where, nodeKind(v)) + } + return v.Content, nil +} + +func nodeKind(v *yaml.Node) string { + switch v.Kind { + case yaml.MappingNode: + return "map" + case yaml.SequenceNode: + return "sequence" + default: + return strings.TrimPrefix(v.ShortTag(), "!!") + } +} + // saveAnnotationsFile writes data to path in the canonical tree layout: a // depth-first walk over the config type graph in struct declaration order // expands every type at its first occurrence; keys are emitted alphabetically. @@ -258,18 +316,10 @@ func saveAnnotationsFile(path string, data annotation.File, g *typeGraph) ([]str } s.assignCanonical(g.root) - root, err := s.block(g.root) - if err != nil { - return nil, err - } - - // Style every top-level key so all nested scalars render in literal block - // style, matching the formatting of the previous annotation files. - style := map[string]yaml3.Style{} - for k := range root { - style[k] = yaml3.LiteralStyle - } - err = yamlsaver.NewSaverWithStyle(style).SaveAsYAML(root, path, true) + // Everything below the top-level keys renders in literal block style, matching + // the formatting of the previous annotation files. + root := &yaml.Node{Kind: yaml.MappingNode, Content: s.block(g.root, 0)} + err := writeYAML(path, root) if err != nil { return nil, err } @@ -280,6 +330,20 @@ func saveAnnotationsFile(path string, data annotation.File, g *typeGraph) ([]str return s.detached(), nil } +func writeYAML(path string, node *yaml.Node) error { + err := os.MkdirAll(filepath.Dir(path), 0o755) + if err != nil { + return err + } + f, err := os.Create(path) + if err != nil { + return err + } + enc := yaml.NewEncoder(f) + enc.SetIndent(2) + return errors.Join(enc.Encode(node), f.Close()) +} + type edgeKey struct { typ string name string @@ -307,61 +371,52 @@ func (s *fileSaver) assignCanonical(typeKey string) { } } -// block renders one type's block of field nodes, emitted alphabetically. -// Lines in the value locations encode the output order for the YAML saver. -func (s *fileSaver) block(typeKey string) (map[string]dyn.Value, error) { - out := map[string]dyn.Value{} - line := 0 +// block renders one type's block of field nodes as the content of a mapping, emitted +// alphabetically, with the keys in keyStyle. +func (s *fileSaver) block(typeKey string, keyStyle yaml.Style) []*yaml.Node { + var out []*yaml.Node edges := slices.Clone(s.graph.fields[typeKey]) slices.SortFunc(edges, func(a, b fieldEdge) int { return strings.Compare(a.name, b.name) }) for _, edge := range edges { - node, err := s.node(typeKey, edge) - if err != nil { - return nil, err - } + node := s.node(typeKey, edge) if len(node) > 0 { - out[edge.name] = dyn.NewValue(node, []diag.Location{{Line: line}}) - line++ + out = append(out, &yaml.Node{Kind: yaml.ScalarNode, Value: edge.name, Style: keyStyle}, mapping(node)) } } - return out, nil + return out +} + +func mapping(content []*yaml.Node) *yaml.Node { + return &yaml.Node{Kind: yaml.MappingNode, Content: content, Style: yaml.LiteralStyle} +} + +func literalKey(name string) *yaml.Node { + return &yaml.Node{Kind: yaml.ScalarNode, Value: name, Style: yaml.LiteralStyle} } // node renders one field's node: the inline field descriptor plus, at the // field's canonical position, the resolved type's "$type" docs and the // "fields" block of its fields. -func (s *fileSaver) node(typeKey string, edge fieldEdge) (map[string]dyn.Value, error) { - out := map[string]dyn.Value{} +func (s *fileSaver) node(typeKey string, edge fieldEdge) []*yaml.Node { + var out []*yaml.Node - // The inline descriptor keys are written directly into the node, sharing - // it with the "$type" and "$fields" keys added below. if d, ok := s.takeField(typeKey, edge.name); ok { - if _, err := descriptorToMap(d, out); err != nil { - return nil, err - } + out = descriptorNodes(d, yaml.LiteralStyle) } if s.expandAt[edgeKey{typeKey, edge.name}] { - v, err := descriptorToMap(s.takeSelf(edge.typ), map[string]dyn.Value{}) - if err != nil { - return nil, err - } - if v.Kind() != dyn.KindNil { - out[typeDocKey] = v.WithLocations([]diag.Location{{Line: lineTypeDoc}}) + if doc := descriptorNodes(s.takeSelf(edge.typ), yaml.LiteralStyle); len(doc) > 0 { + out = append(out, literalKey(typeDocKey), mapping(doc)) } - child, err := s.block(edge.typ) - if err != nil { - return nil, err - } - if len(child) > 0 { - out[fieldsKey] = dyn.NewValue(child, []diag.Location{{Line: lineFields}}) + if child := s.block(edge.typ, yaml.LiteralStyle); len(child) > 0 { + out = append(out, literalKey(fieldsKey), mapping(child)) } } - return out, nil + return out } // takeField returns the descriptor for a field and marks it consumed for the diff --git a/bundle/internal/schema/annotations_file_test.go b/bundle/internal/schema/annotations_file_test.go index 42899b0c184..d50dcf6ff1b 100644 --- a/bundle/internal/schema/annotations_file_test.go +++ b/bundle/internal/schema/annotations_file_test.go @@ -167,3 +167,15 @@ func TestAnnotationsFileDetachedEntries(t *testing.T) { "github.com/databricks/cli/no/such.Type: field", }, detached) } + +func TestAnnotationsFileEmpty(t *testing.T) { + g, err := newTypeGraph(reflect.TypeFor[tgRoot]()) + require.NoError(t, err) + path := filepath.Join(t.TempDir(), "annotations.yml") + require.NoError(t, os.WriteFile(path, []byte("# no annotations yet\n"), 0o644)) + + data, unknown, err := loadAnnotationsFile(path, g) + require.NoError(t, err) + assert.Empty(t, data) + assert.Empty(t, unknown) +} diff --git a/bundle/internal/schema/annotations_test.go b/bundle/internal/schema/annotations_test.go index 3ad9bae6fd6..e43acc97c20 100644 --- a/bundle/internal/schema/annotations_test.go +++ b/bundle/internal/schema/annotations_test.go @@ -270,3 +270,16 @@ func TestBuildEnumDescriptions(t *testing.T) { assert.Equal(t, []string{"[Public Preview]", "", "[Beta]"}, got) }) } + +func TestMergeAnnotationFilesMergesDefaults(t *testing.T) { + a := annotation.File{} + a.SetField("t", "f", annotation.Descriptor{Default: map[string]any{"a": 1, "nested": map[string]any{"x": 1}}}) + a.SetField("t", "g", annotation.Descriptor{Default: []any{1}}) + b := annotation.File{} + b.SetField("t", "f", annotation.Descriptor{Default: map[string]any{"b": 2, "nested": map[string]any{"y": 2}}}) + b.SetField("t", "g", annotation.Descriptor{Default: []any{2}}) + + merged := mergeAnnotationFiles(a, b) + assert.Equal(t, map[string]any{"a": 1, "b": 2, "nested": map[string]any{"x": 1, "y": 2}}, merged["t"].Fields["f"].Default) + assert.Equal(t, []any{1, 2}, merged["t"].Fields["g"].Default) +} diff --git a/bundle/internal/schema/main.go b/bundle/internal/schema/main.go index 01a743933b2..75d36dd4824 100644 --- a/bundle/internal/schema/main.go +++ b/bundle/internal/schema/main.go @@ -13,17 +13,17 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/config/variable" "github.com/databricks/cli/internal/clijson" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/jsonschema" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/pipelines" ) // interpolationPattern builds a JSON Schema regex for ${prefix.path...} references. -// Path segments use [dynvar.BaseVarDef]; unlike the runtime matcher in ref.go, this +// Path segments use [structvar.BaseVarDef]; unlike the runtime matcher in ref.go, this // requires a fixed prefix (var, resources, ...) and at least one ".segment" after it. func interpolationPattern(s string) string { - return fmt.Sprintf(`\$\{(%s(\.%s(\[[0-9]+\])*)+)\}`, s, dynvar.BaseVarDef) + return fmt.Sprintf(`\$\{(%s(\.%s(\[[0-9]+\])*)+)\}`, s, structvar.BaseVarDef) } func addInterpolationPatterns(typ reflect.Type, s jsonschema.Schema) jsonschema.Schema { diff --git a/bundle/internal/schema/main_test.go b/bundle/internal/schema/main_test.go index 92184a2d48f..4f6a4c741d8 100644 --- a/bundle/internal/schema/main_test.go +++ b/bundle/internal/schema/main_test.go @@ -1,21 +1,20 @@ package main import ( - "bytes" "io" + "maps" "os" "path" "reflect" + "slices" "strings" "testing" "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/merge" - "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/databricks/cli/libs/jsonschema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" ) const cliJSONPath = "../../../.codegen/cli.json" @@ -73,36 +72,45 @@ func TestRequiredAnnotationsForNewFields(t *testing.T) { require.NoError(t, err) currentFile, err := os.ReadFile(annotationsPath) require.NoError(t, err) - original, err := yamlloader.LoadYAML("", bytes.NewBuffer(originalFile)) - require.NoError(t, err) - current, err := yamlloader.LoadYAML("", bytes.NewBuffer(currentFile)) - require.NoError(t, err) + var original, current any + require.NoError(t, yaml.Unmarshal(originalFile, &original)) + require.NoError(t, yaml.Unmarshal(currentFile, ¤t)) // Regenerating from the committed file must be a no-op: no new placeholders // (a new undocumented config field) and no deletes/updates (stale - // placeholders not yet pruned). VisitDelete/VisitUpdate must be set or - // Override panics on any change. + // placeholders not yet pruned). var addedFieldPaths []string var changedFieldPaths []string - _, err = merge.Override(original, current, merge.OverrideVisitor{ - VisitInsert: func(basePath dyn.Path, right dyn.Value) (dyn.Value, error) { - addedFieldPaths = append(addedFieldPaths, basePath.String()) - return right, nil - }, - VisitDelete: func(basePath dyn.Path, left dyn.Value) error { - changedFieldPaths = append(changedFieldPaths, basePath.String()) - return nil - }, - VisitUpdate: func(basePath dyn.Path, left, right dyn.Value) (dyn.Value, error) { - changedFieldPaths = append(changedFieldPaths, basePath.String()) - return right, nil - }, - }) - assert.NoError(t, err) + diffYAML("", original, current, &addedFieldPaths, &changedFieldPaths) assert.Empty(t, addedFieldPaths, "Missing JSON-schema descriptions for new config fields in bundle/internal/schema/annotations.yml:\n%s", strings.Join(addedFieldPaths, "\n")) assert.Empty(t, changedFieldPaths, "annotations.yml is out of sync; run `./task generate-schema` and commit the result:\n%s", strings.Join(changedFieldPaths, "\n")) } +// diffYAML records the paths of the values added to, and removed from or changed in, left to get right. +func diffYAML(path string, left, right any, added, changed *[]string) { + l, lok := left.(map[string]any) + r, rok := right.(map[string]any) + if !lok || !rok { + if !reflect.DeepEqual(left, right) { + *changed = append(*changed, path) + } + return + } + for _, k := range slices.Sorted(maps.Keys(l)) { + if _, ok := r[k]; !ok { + *changed = append(*changed, strings.TrimPrefix(path+"."+k, ".")) + } + } + for _, k := range slices.Sorted(maps.Keys(r)) { + p := strings.TrimPrefix(path+"."+k, ".") + if lv, ok := l[k]; ok { + diffYAML(p, lv, r[k], added, changed) + } else { + *added = append(*added, p) + } + } +} + // Checks that the annotations file only contains entries that match the // current bundle configuration structure. func TestNoDetachedAnnotations(t *testing.T) { diff --git a/bundle/internal/validation/enum.go b/bundle/internal/validation/enum.go index e3abcd72940..3251e21951e 100644 --- a/bundle/internal/validation/enum.go +++ b/bundle/internal/validation/enum.go @@ -90,8 +90,8 @@ func getEnumValues(typ reflect.Type) ([]string, error) { return enumStrings, nil } -// extractEnumFields walks through a struct type and returns the valid values of the -// enum fields by their pattern. +// extractEnumFields walks through a struct type and returns the valid values by the +// pattern of the enum fields. func extractEnumFields(typ reflect.Type) (map[string][]string, error) { fieldsByPattern := make(map[string][]string) diff --git a/bundle/internal/validation/main.go b/bundle/internal/validation/main.go index bd35dfcd06d..29f8809f372 100644 --- a/bundle/internal/validation/main.go +++ b/bundle/internal/validation/main.go @@ -4,8 +4,9 @@ import ( "log" ) -// This package is meant to be run from the root of the CLI repo. It generates the -// typed walkers of the required and enum field validators. +// This package is meant to be run from the root of the CLI repo. It generates typed +// walkers over the bundle configuration: those of the required and enum field +// validators, and the string walker of bundle/config/walk. func main() { err := generateWalkers("bundle/config/validate") if err != nil { diff --git a/bundle/internal/validation/required.go b/bundle/internal/validation/required.go index 78ad6e0bc4a..35580cf7314 100644 --- a/bundle/internal/validation/required.go +++ b/bundle/internal/validation/required.go @@ -1,16 +1,33 @@ package main import ( + "fmt" "reflect" "slices" + "strings" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/structs/structtag" "github.com/databricks/cli/libs/structs/structwalk" ) +// formatSliceToString formats a list of values into string of the form `{value1, value2, ...}` +// representing a Go slice literal. +func formatSliceToString(values []string) string { + if len(values) == 0 { + return "{}" + } + + var quoted []string + for _, value := range values { + quoted = append(quoted, fmt.Sprintf("%q", value)) + } + + return "{" + strings.Join(quoted, ", ") + "}" +} + // extractRequiredFields walks through a struct type and returns the required fields -// (JSON names, in declaration order) by the pattern of their parent. +// (JSON names) by the pattern of the struct that has them. func extractRequiredFields(typ reflect.Type) (map[string][]string, error) { fieldsByPattern := make(map[string][]string) @@ -54,3 +71,16 @@ func extractRequiredFields(typ reflect.Type) (map[string][]string, error) { return fieldsByPattern, err } + +// getGroupingKey determines the grouping key for organizing patterns +func getGroupingKey(pattern string) string { + parts := strings.Split(pattern, ".") + + // Group resources by their resource type (e.g., "resources.jobs") + if parts[0] == "resources" && len(parts) > 1 { + return parts[0] + "." + parts[1] + } + + // Use the top level key for other fields + return parts[0] +} diff --git a/bundle/internal/validation/resources_iterate.go b/bundle/internal/validation/resources_iterate.go new file mode 100644 index 00000000000..f156dac683b --- /dev/null +++ b/bundle/internal/validation/resources_iterate.go @@ -0,0 +1,142 @@ +package main + +import ( + "bytes" + "cmp" + _ "embed" + "fmt" + "go/format" + "os" + "path" + "reflect" + "slices" + "strconv" + "strings" + "text/template" + + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/databricks/databricks-sdk-go/service/catalog" +) + +//go:embed resources_iterate.go.tmpl +var resourcesIterateSource string + +var resourcesIterateTemplate = template.Must(template.New("resources_iterate").Parse(resourcesIterateSource)) + +// generateResourcesIterate writes bundle/config/resources_iterate.go: typed iterators +// over all resources, their permissions and their grants, generated from the fields of +// config.Resources so that new resource types are covered. +func generateResourcesIterate(file string) error { + g := newGen("", false) + g.aliases["config"] = true + rt := reflect.TypeFor[config.Resources]() + info := structvar.GetStructInfo(rt) + + type resourceType struct { + key, field string + elem reflect.Type // the resource struct + perms string // Go field of the permissions, if any + permElem reflect.Type + grants string // Go field of the grants, if any + id string // Go field of the deployed id, if any + status bool // the resource has a modified_status + } + var types []resourceType + grantsType := reflect.TypeFor[[]catalog.PrivilegeAssignment]() + for _, f := range info.List { + ft := rt.FieldByIndex(f.Index).Type + if ft.Kind() != reflect.Map || ft.Elem().Kind() != reflect.Pointer { + return fmt.Errorf("resources.%s: expected a map of pointers, got %s", f.Key, ft) + } + t := resourceType{key: f.Key, field: rt.FieldByIndex(f.Index).Name, elem: ft.Elem().Elem()} + ei := structvar.GetStructInfo(t.elem) + if idx, ok := ei.Fields["permissions"]; ok { + pf := t.elem.FieldByIndex(idx) + if !isPermissionsSlice(pf.Type) { + return fmt.Errorf("resources.%s.permissions: unexpected type %s", f.Key, pf.Type) + } + t.perms = pf.Name + t.permElem = pf.Type.Elem() + } + if idx, ok := ei.Fields["grants"]; ok { + gf := t.elem.FieldByIndex(idx) + if gf.Type != grantsType { + return fmt.Errorf("resources.%s.grants: unexpected type %s", f.Key, gf.Type) + } + t.grants = gf.Name + } + if idx, ok := ei.Fields["id"]; ok && t.elem.FieldByIndex(idx).Type.Kind() == reflect.String { + t.id = t.elem.FieldByIndex(idx).Name + } + _, t.status = ei.Fields["modified_status"] + types = append(types, t) + } + slices.SortFunc(types, func(a, b resourceType) int { return cmp.Compare(a.key, b.key) }) + + // Type names register their imports, so the data is built before the header. + type typeData struct { + Key, Field, Elem string + Perms, PermElem, PermLevel string + Grants, ID string + HasState bool + } + data := struct { + Imports []string + Types []typeData + ModifiedStatus, Grant string + }{ + ModifiedStatus: g.typeName(reflect.TypeFor[resources.ModifiedStatus]()), + Grant: g.typeName(grantsType.Elem()), + } + for _, t := range types { + d := typeData{Key: t.key, Field: t.field, Elem: g.typeName(t.elem), Perms: t.perms, Grants: t.grants, ID: t.id, HasState: t.id != "" && t.status} + if t.perms != "" { + d.PermElem = g.typeName(t.permElem) + d.PermLevel = g.typeName(t.permElem.Field(0).Type) + } + data.Types = append(data.Types, d) + } + for p, alias := range g.imports { + switch { + case p == "github.com/databricks/cli/bundle/config": + case path.Base(p) == alias: + data.Imports = append(data.Imports, strconv.Quote(p)) + default: + data.Imports = append(data.Imports, alias+" "+strconv.Quote(p)) + } + } + slices.SortFunc(data.Imports, func(a, b string) int { return cmp.Compare(importPath(a), importPath(b)) }) + + var src bytes.Buffer + if err := resourcesIterateTemplate.Execute(&src, data); err != nil { + return err + } + formatted, err := format.Source(src.Bytes()) + if err != nil { + _ = os.WriteFile(file+".broken", src.Bytes(), 0o644) + return fmt.Errorf("failed to format %s: %w", file, err) + } + return os.WriteFile(file, formatted, 0o644) +} + +// importPath returns the quoted path of an import line (with or without an alias). +func importPath(line string) string { + _, p, _ := strings.Cut(line, `"`) + return p +} + +// isPermissionsSlice reports whether t is a slice of a struct with the fields of +// resources.PermissionFields. +func isPermissionsSlice(t reflect.Type) bool { + if t.Kind() != reflect.Slice || t.Elem().Kind() != reflect.Struct { + return false + } + e := t.Elem() + var names []string + for i := range e.NumField() { + names = append(names, e.Field(i).Name) + } + return strings.Join(names, ",") == "Level,UserName,ServicePrincipalName,GroupName" && e.Field(0).Type.Kind() == reflect.String +} diff --git a/bundle/internal/validation/resources_iterate.go.tmpl b/bundle/internal/validation/resources_iterate.go.tmpl new file mode 100644 index 00000000000..6befe8b5642 --- /dev/null +++ b/bundle/internal/validation/resources_iterate.go.tmpl @@ -0,0 +1,149 @@ +// Code generated by bundle/internal/validation. DO NOT EDIT. + +package config + +import ( +{{- range .Imports}} + {{.}} +{{- end}} +) + +// IterateAllResources calls fn for every resource, in the order of the resource type +// and then of the name, until fn returns false. Resources defined as null are +// visited with a nil res. +func (r *Resources) IterateAllResources(fn func(typ, name string, res ConfigResource) bool) { +{{- range .Types}} + for _, name := range sortedKeys(r.{{.Field}}) { + var res ConfigResource + if x := r.{{.Field}}[name]; x != nil { + res = x + } + if !fn({{printf "%q" .Key}}, name, res) { + return + } + } +{{- end}} +} + +// Select returns the resources of r whose names are in keys (by type): new maps that +// share the resources. Types not in keys are nil. +func (r *Resources) Select(keys map[string]map[string]struct{}) Resources { + var out Resources +{{- range .Types}} + if names, ok := keys[{{printf "%q" .Key}}]; ok { + out.{{.Field}} = make(map[string]*{{.Elem}}, len(names)) + for name := range names { + if x, ok := r.{{.Field}}[name]; ok { + out.{{.Field}}[name] = x + } + } + } +{{- end}} + return out +} + +// CopyResource sets the resource name of type typ to the one in src (shared, not +// copied). It returns false if src has no such resource. +func (r *Resources) CopyResource(src *Resources, typ, name string) bool { + switch typ { +{{- range .Types}} + case {{printf "%q" .Key}}: + x, ok := src.{{.Field}}[name] + if !ok { + return false + } + if r.{{.Field}} == nil { + r.{{.Field}} = map[string]*{{.Elem}}{} + } + r.{{.Field}}[name] = x + return true +{{- end}} + } + return false +} + +// GetResource returns the resource name of type typ. ok is false if there is no such +// resource or it is defined as null. +func (r *Resources) GetResource(typ, name string) (res ConfigResource, ok bool) { + switch typ { +{{- range .Types}} + case {{printf "%q" .Key}}: + if x := r.{{.Field}}[name]; x != nil { + return x, true + } +{{- end}} + } + return nil, false +} + +// IterateAllPermissions calls fn for the permissions of every resource that can have +// them, in the order of IterateAllResources, until fn returns false. +func (r *Resources) IterateAllPermissions(fn func(typ, name string, perms PermissionList) bool) { +{{- range .Types}}{{if .Perms}} + for _, name := range sortedKeys(r.{{.Field}}) { + if x := r.{{.Field}}[name]; x != nil && !fn({{printf "%q" .Key}}, name, permissionList[{{.PermElem}}, {{.PermLevel}}]{&x.{{.Perms}}}) { + return + } + } +{{- end}}{{end}} +} + +// PermissionsOf returns the permissions of the resource name of type typ. ok is false +// if there is no such resource or resources of type typ have no permissions. +func (r *Resources) PermissionsOf(typ, name string) (perms PermissionList, ok bool) { + switch typ { +{{- range .Types}}{{if .Perms}} + case {{printf "%q" .Key}}: + if x := r.{{.Field}}[name]; x != nil { + return permissionList[{{.PermElem}}, {{.PermLevel}}]{&x.{{.Perms}}}, true + } +{{- end}}{{end}} + } + return nil, false +} + +// IterateAllResourceStates calls fn for the deployed id and modified status of every +// resource that has them, in the order of IterateAllResources, until fn returns false. +func (r *Resources) IterateAllResourceStates(fn func(typ, name string, id *string, status *{{.ModifiedStatus}}) bool) { +{{- range .Types}}{{if .HasState}} + for _, name := range sortedKeys(r.{{.Field}}) { + if x := r.{{.Field}}[name]; x != nil && !fn({{printf "%q" .Key}}, name, &x.{{.ID}}, &x.ModifiedStatus) { + return + } + } +{{- end}}{{end}} +} + +// GetOrCreateState returns the deployed id and modified status of the resource name of +// type typ, creating the resource if it does not exist (created is then true). ok is +// false if resources of type typ have no state. +func (r *Resources) GetOrCreateState(typ, name string) (id *string, status *{{.ModifiedStatus}}, created, ok bool) { + switch typ { +{{- range .Types}}{{if .HasState}} + case {{printf "%q" .Key}}: + x, exists := r.{{.Field}}[name] + if x == nil { + if r.{{.Field}} == nil { + r.{{.Field}} = map[string]*{{.Elem}}{} + } + x = &{{.Elem}}{} + r.{{.Field}}[name] = x + } + return &x.{{.ID}}, &x.ModifiedStatus, !exists, true +{{- end}}{{end}} + default: + return nil, nil, false, false + } +} + +// IterateAllGrants calls fn for the grants of every resource that can have them, in +// the order of IterateAllResources, until fn returns false. +func (r *Resources) IterateAllGrants(fn func(typ, name string, grants *[]{{.Grant}}) bool) { +{{- range .Types}}{{if .Grants}} + for _, name := range sortedKeys(r.{{.Field}}) { + if x := r.{{.Field}}[name]; x != nil && !fn({{printf "%q" .Key}}, name, &x.{{.Grants}}) { + return + } + } +{{- end}}{{end}} +} diff --git a/bundle/internal/validation/strings_walker.go b/bundle/internal/validation/strings_walker.go new file mode 100644 index 00000000000..83b55843839 --- /dev/null +++ b/bundle/internal/validation/strings_walker.go @@ -0,0 +1,247 @@ +package main + +import ( + "bytes" + "cmp" + "fmt" + "go/format" + "os" + "path" + "reflect" + "slices" + "strings" + + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structvar" +) + +// stringsGen generates the walker visiting every string of the configuration +// (bundle/config/walk/strings_walk.go): one function per struct type that contains +// strings, with plain field access. +type stringsGen struct { + gen + hasStrings map[reflect.Type]bool + names map[reflect.Type]string +} + +// containsStrings reports whether values of type t can contain strings. +func (g *stringsGen) containsStrings(t reflect.Type, visiting map[reflect.Type]bool) bool { + if v, ok := g.hasStrings[t]; ok { + return v + } + switch t.Kind() { + case reflect.String, reflect.Interface: + return true + case reflect.Pointer, reflect.Slice, reflect.Map: + return g.containsStrings(t.Elem(), visiting) + case reflect.Struct: + if structvar.IsSDKNativeType(t) { + return false + } + if visiting[t] { + return false + } + visiting[t] = true + defer delete(visiting, t) + found := false + info := structvar.GetStructInfo(t) + for _, f := range info.List { + if g.containsStrings(t.FieldByIndex(f.Index).Type, visiting) { + found = true + } + } + if len(visiting) == 1 { + g.hasStrings[t] = found + } + return found + default: + return false + } +} + +func (g *stringsGen) funcName(t reflect.Type) string { + if name, ok := g.names[t]; ok { + return name + } + base := "strings" + capitalize(path.Base(t.PkgPath())) + t.Name() + g.gen.names[base]++ + name := base + if n := g.gen.names[base]; n > 1 { + name = fmt.Sprintf("%s%d", base, n) + } + g.names[t] = name + g.pendingTypes = append(g.pendingTypes, t) + return name +} + +// emitStringsStruct writes the function visiting the strings of struct type t. +func (g *stringsGen) emitStringsStruct(t reflect.Type) { + // A value replaced by a pure reference hides what is below it, as in the view; the + // reference itself is in config.Root.References. + fmt.Fprintf(&g.out, "\nfunc %s(c *stringsWalk, n *Node, v *%s) {\n\tif n.isRef() {\n\t\treturn\n\t}\n", g.funcName(t), g.typeName(t)) + info := structvar.GetStructInfo(t) + for _, f := range info.List { + ft := t.FieldByIndex(f.Index).Type + if !g.containsStrings(ft, map[reflect.Type]bool{}) { + continue + } + expr, guards := selector("v", t, f.Index) + if t == reflect.TypeFor[config.Root]() && f.Key == "resources" { + // See walk.StringsWithoutResources. + guards = append(guards, "!c.skipResources") + } + switch ft.Kind() { + case reflect.String, reflect.Interface: + default: + // A field replaced by a pure reference hides what is below it, as in the + // view; the reference itself is in config.Root.References. + guards = append(guards, fmt.Sprintf("!n.IsRef(%q)", f.Key)) + } + if len(guards) > 0 { + fmt.Fprintf(&g.out, "\tif %s {\n", strings.Join(guards, " && ")) + } + g.emitEmptyField(structField("v", t, f.Key), fmt.Sprintf("n.Child(%q)", f.Key)) + g.emitValue("\t", expr, ft, fmt.Sprintf("n.Child(%q)", f.Key), true) + if len(guards) > 0 { + fmt.Fprintf(&g.out, "\t}\n") + } + } + fmt.Fprintf(&g.out, "}\n") +} + +// emitEmptyField writes the code visiting the struct field f in mode EmptyFields if it +// holds an empty string that [structvar.View] finds present: a forced string, a non-nil +// pointer to a string, or an interface. Fields without omitempty are skipped: their +// zero value is always sent. +func (g *stringsGen) emitEmptyField(f field, nodeExpr string) { + if f.forceEmpty { + return + } + var cond string + switch { + case f.typ.Kind() == reflect.String && f.fsf != "": + cond = fmt.Sprintf(`%s == "" && Forced(%s, %q)`, f.expr, f.fsf, f.goName) + case f.typ.Kind() == reflect.Pointer && f.typ.Elem().Kind() == reflect.String: + cond = fmt.Sprintf(`%s != nil && *%s == ""`, f.expr, f.expr) + case f.typ.Kind() == reflect.Interface: + cond = f.expr + ` == ""` + default: + return + } + fmt.Fprintf(&g.out, "\tif c.mode == EmptyFields && %s {\n\t\tch := %s\n\t\tc.fn(&ch, \"\")\n\t}\n", cond, nodeExpr) +} + +// emitValue writes the code visiting the value expr of type t whose node is nodeExpr. +// settable reports whether expr can be assigned to. +func (g *stringsGen) emitValue(indent, expr string, t reflect.Type, nodeExpr string, settable bool) { + switch t.Kind() { + case reflect.String: + conv := "" + if t.PkgPath() != "" { + conv = g.typeName(t) + } + fmt.Fprintf(&g.out, "%sif s := string(%s); c.match(s) {\n%s\tch := %s\n%s\tif r, ok := c.fn(&ch, s); ok {\n", indent, expr, indent, nodeExpr, indent) + if settable { + fmt.Fprintf(&g.out, "%s\t\t%s = %s(r)\n", indent, expr, conv) + } else { + fmt.Fprintf(&g.out, "%s\t\t_ = r\n", indent) + } + fmt.Fprintf(&g.out, "%s\t}\n%s}\n", indent, indent) + case reflect.Interface: + fmt.Fprintf(&g.out, "%sif %s != nil {\n%s\tch := %s\n%s\tc.any(&ch, &%s)\n%s}\n", indent, expr, indent, nodeExpr, indent, expr, indent) + case reflect.Pointer: + et := t.Elem() + if et.Kind() == reflect.Struct { + fmt.Fprintf(&g.out, "%sif x := %s; x != nil {\n%s\tch := %s\n%s\t%s(c, &ch, x)\n%s}\n", indent, expr, indent, nodeExpr, indent, g.funcName(et), indent) + } else { + fmt.Fprintf(&g.out, "%sif x := %s; x != nil {\n", indent, expr) + g.emitValue(indent+"\t", "*x", et, nodeExpr, true) + fmt.Fprintf(&g.out, "%s}\n", indent) + } + case reflect.Struct: + fmt.Fprintf(&g.out, "%s{\n%s\tch := %s\n%s\t%s(c, &ch, &%s)\n%s}\n", indent, indent, nodeExpr, indent, g.funcName(t), expr, indent) + case reflect.Slice: + fmt.Fprintf(&g.out, "%sif len(%s) > 0 {\n%s\tcn := %s\n%s\tfor i := range %s {\n", indent, expr, indent, nodeExpr, indent, expr) + g.emitValue(indent+"\t\t", expr+"[i]", t.Elem(), "cn.Elem(i)", true) + fmt.Fprintf(&g.out, "%s\t}\n%s}\n", indent, indent) + case reflect.Map: + et := t.Elem() + fmt.Fprintf(&g.out, "%sif len(%s) > 0 {\n%s\tcn := %s\n%s\tfor _, k := range sortedKeys(%s) {\n", indent, expr, indent, nodeExpr, indent, expr) + switch { + case et.Kind() == reflect.Pointer: + g.emitValue(indent+"\t\t", expr+"[k]", et, "cn.Child(string(k))", false) + case et.Kind() == reflect.String: + // Map values are not addressable: write the replacement back. + fmt.Fprintf(&g.out, "%s\t\tif s := string(%s[k]); c.match(s) {\n%s\t\t\tch := cn.Child(string(k))\n%s\t\t\tif r, ok := c.fn(&ch, s); ok {\n%s\t\t\t\t%s[k] = %s(r)\n%s\t\t\t}\n%s\t\t}\n", + indent, expr, indent, indent, indent, expr, g.convName(et), indent, indent) + default: + fmt.Fprintf(&g.out, "%s\t\tx := %s[k]\n", indent, expr) + g.emitValue(indent+"\t\t", "x", et, "cn.Child(string(k))", true) + fmt.Fprintf(&g.out, "%s\t\t%s[k] = x\n", indent, expr) + } + fmt.Fprintf(&g.out, "%s\t}\n%s}\n", indent, indent) + default: + panic(fmt.Sprintf("unsupported type %s", t)) + } +} + +// emitResourceDispatch writes stringsResource, which visits the strings of one resource +// by its type and name. +func (g *stringsGen) emitResourceDispatch() { + rt := reflect.TypeFor[config.Resources]() + fmt.Fprintf(&g.out, "\nfunc stringsResource(c *stringsWalk, n *Node, v *config.Resources, typ, name string) {\n\tswitch typ {\n") + for _, f := range structvar.GetStructInfo(rt).List { + ft := rt.FieldByIndex(f.Index) + et := ft.Type.Elem().Elem() + if !g.containsStrings(et, map[reflect.Type]bool{}) { + continue + } + fmt.Fprintf(&g.out, "\tcase %q:\n\t\tif x := v.%s[name]; x != nil {\n\t\t\tch := n.Child(typ)\n\t\t\trn := ch.Child(name)\n\t\t\t%s(c, &rn, x)\n\t\t}\n", f.Key, ft.Name, g.funcName(et)) + } + fmt.Fprintf(&g.out, "\t}\n}\n") +} + +func (g *stringsGen) convName(t reflect.Type) string { + if t.PkgPath() == "" { + return "" + } + return g.typeName(t) +} + +func generateStringsWalker(file string) error { + g := &stringsGen{gen: *newGen("strings", false), hasStrings: map[reflect.Type]bool{}, names: map[reflect.Type]string{}} + g.aliases["strings"] = true + g.aliases["stringsWalk"] = true + root := reflect.TypeFor[config.Root]() + g.funcName(root) + for len(g.pendingTypes) > 0 { + t := g.pendingTypes[0] + g.pendingTypes = g.pendingTypes[1:] + g.emitStringsStruct(t) + } + // After the loop: the resource types are all named by then, so their order stays. + g.emitResourceDispatch() + + var src bytes.Buffer + fmt.Fprintf(&src, "// Code generated by bundle/internal/validation. DO NOT EDIT.\n\npackage walk\n\nimport (\n") + paths := make([]string, 0, len(g.imports)) + for p := range g.imports { + paths = append(paths, p) + } + slices.SortFunc(paths, cmp.Compare) + for _, p := range paths { + if path.Base(p) == g.imports[p] { + fmt.Fprintf(&src, "\t%q\n", p) + } else { + fmt.Fprintf(&src, "\t%s %q\n", g.imports[p], p) + } + } + fmt.Fprintf(&src, ")\n") + src.Write(g.out.Bytes()) + formatted, err := format.Source(src.Bytes()) + if err != nil { + _ = os.WriteFile(file+".broken", src.Bytes(), 0o644) + return fmt.Errorf("failed to format %s: %w", file, err) + } + return os.WriteFile(file, formatted, 0o644) +} diff --git a/bundle/internal/validation/walker.go b/bundle/internal/validation/walker.go index 8f20007daef..cda81aa7910 100644 --- a/bundle/internal/validation/walker.go +++ b/bundle/internal/validation/walker.go @@ -5,7 +5,6 @@ import ( "cmp" "fmt" "go/format" - "maps" "os" "path" "reflect" @@ -14,28 +13,32 @@ import ( "strings" "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resourcedefaults" "github.com/databricks/cli/libs/structs/structpath" - "github.com/databricks/cli/libs/structs/structtag" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/databricks/databricks-sdk-go/service/jobs" ) -// The walkers generated here replace matching every node of the configuration against -// the required and enum field patterns: they descend only along the patterns' paths -// of the typed configuration, with plain field access, and check the fields of each -// node on the way. -// -// The typed configuration cannot tell an unset field from one set to its zero value, -// null or a pure variable reference, and it has no locations. The generated code -// asks the node (bundle/config/validate/walk_node.go) for those. +// The walkers generated here replace matching every configuration node against the +// required and enum field patterns: they descend only along the patterns' paths, with +// plain field access, and check each node's fields during a single walk. // tnode is a node of the tree of pattern paths, rooted at config.Root. type tnode struct { typ reflect.Type // the declared type of the value at this node required []string // required fields (JSON names) of the struct at this node enum []string // valid values if the value at this node is an enum + defaults []defaultKey // fields of the struct at this node that have a default children map[string]*tnode sig string } +// defaultKey is a field (JSON name) with a default, the index of the default. +type defaultKey struct { + key string + index int +} + func newTnode(typ reflect.Type) *tnode { return &tnode{typ: typ, children: map[string]*tnode{}} } @@ -52,28 +55,25 @@ func (t *tnode) insert(p string) (*tnode, error) { var next reflect.Type elem := deref(n.typ) switch { - case c.DotStar(): - if elem.Kind() != reflect.Map { - return nil, fmt.Errorf("%s: .* on %s", p, elem) - } + case c.DotStar() || c.BracketStar(): key = "*" - next = elem.Elem() - case c.BracketStar(): - if elem.Kind() != reflect.Slice { - return nil, fmt.Errorf("%s: [*] on %s", p, elem) + if c.BracketStar() { + key = "[*]" + } + if elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice { + return nil, fmt.Errorf("%s: wildcard on %s", p, elem) } - key = "[*]" next = elem.Elem() default: k, ok := c.StringKey() if !ok || elem.Kind() != reflect.Struct { return nil, fmt.Errorf("%s: unsupported component %s on %s", p, c, elem) } - index, ok := fieldIndex(elem, k) + key = k + index, ok := structvar.GetStructInfo(elem).Fields[k] if !ok { return nil, fmt.Errorf("%s: no field %q in %s", p, k, elem) } - key = k next = elem.FieldByIndex(index).Type } child, ok := n.children[key] @@ -86,35 +86,6 @@ func (t *tnode) insert(p string) (*tnode, error) { return n, nil } -// fieldIndex returns the index path of the field with JSON name key in struct st, as -// [convert.ToTyped] resolves it: fields of embedded structs are promoted, and a field -// of an outer struct takes precedence. -func fieldIndex(st reflect.Type, key string) ([]int, bool) { - queue := [][]int{{}} - for i := 0; i < len(queue); i++ { - prefix := queue[i] - typ := st - if len(prefix) > 0 { - typ = deref(st.FieldByIndex(prefix).Type) - } - for j := range typ.NumField() { - sf := typ.Field(j) - index := append(slices.Clone(prefix), j) - if sf.Anonymous { - queue = append(queue, index) - continue - } - if !sf.IsExported() { - continue - } - if structtag.JSONTag(sf.Tag.Get("json")).Name() == key { - return index, true - } - } - } - return nil, false -} - func deref(t reflect.Type) reflect.Type { for t.Kind() == reflect.Pointer { t = t.Elem() @@ -128,7 +99,7 @@ func (t *tnode) signature() string { return t.sig } var b strings.Builder - fmt.Fprintf(&b, "%s|r=%v|e=%v|", t.typ, t.required, t.enum) + fmt.Fprintf(&b, "%s|r=%v|e=%v|d=%v|", t.typ, t.required, t.enum, t.defaults) for _, k := range sortedKeys(t.children) { fmt.Fprintf(&b, "%s{%s}", k, t.children[k].signature()) } @@ -152,24 +123,46 @@ type gen struct { aliases map[string]bool funcs map[string]string // signature -> function name names map[string]int - enums map[reflect.Type]enumVar // enum type -> its valid values + enums map[string]string // joined values -> variable name out bytes.Buffer pending []*tnode + + // pendingTypes are the struct types whose functions are still to be written + // (strings walker). + pendingTypes []reflect.Type + + // walkerType is the name of the type of the walker state (first parameter). + walkerType string + + // refBranches visits nil pointer fields that are present (in ForceSendFields, or + // written without omitempty) or hold a pure reference as nodes without fields, so + // their required fields are reported. + refBranches bool } -func newGen(prefix string) *gen { +func newGen(prefix string, refBranches bool) *gen { return &gen{ - prefix: prefix, - imports: map[string]string{}, - aliases: map[string]bool{"slices": true, "dyn": true}, - funcs: map[string]string{}, - names: map[string]int{}, - enums: map[reflect.Type]enumVar{}, + prefix: prefix, + refBranches: refBranches, + imports: map[string]string{}, + aliases: map[string]bool{"slices": true, "structvar": true, "config": false}, + funcs: map[string]string{}, + names: map[string]int{}, + enums: map[string]string{}, } } -// typeName returns the Go name of the named type t, importing its package. +// typeName returns the Go name of t, importing its package. func (g *gen) typeName(t reflect.Type) string { + switch t.Kind() { + case reflect.Pointer: + return "*" + g.typeName(t.Elem()) + case reflect.Slice: + return "[]" + g.typeName(t.Elem()) + case reflect.Map: + return "map[" + g.typeName(t.Key()) + "]" + g.typeName(t.Elem()) + default: + } if t.PkgPath() == "" { return t.String() } @@ -210,248 +203,266 @@ func capitalize(s string) string { return strings.ToUpper(s[:1]) + s[1:] } -// enumVar is the variable holding the valid values of an enum type. -type enumVar struct { - name string - values []string +func (g *gen) enumVar(values []string) string { + key := strings.Join(values, "\x00") + if name, ok := g.enums[key]; ok { + return name + } + name := fmt.Sprintf("%sValues%d", g.prefix, len(g.enums)) + g.enums[key] = name + return name } -// enumVarFor returns the name of the variable holding the valid values of the enum type -// typ, e.g. enumValuesPostgresPipelineChannel for postgres.PipelineChannel. -func (g *gen) enumVarFor(typ reflect.Type, values []string) (string, error) { - if v, ok := g.enums[typ]; ok { - if !slices.Equal(v.values, values) { - return "", fmt.Errorf("enum type %s has different valid values at different paths", typ) +// selector returns the Go expression selecting the field with the given index path +// from v, and the nil checks of the embedded pointers on the way. +func selector(v string, t reflect.Type, index []int) (string, []string) { + var guards []string + var expr strings.Builder + expr.WriteString(v) + for i, x := range index { + f := deref(t).Field(x) + expr.WriteString("." + f.Name) + if i < len(index)-1 && f.Type.Kind() == reflect.Pointer { + guards = append(guards, expr.String()+" != nil") } - return v.name, nil - } - base := g.prefix + "Values" + capitalize(path.Base(typ.PkgPath())) + typ.Name() - g.names[base]++ - name := base - if g.names[base] > 1 { - name = base + strconv.Itoa(g.names[base]) + t = f.Type } - g.enums[typ] = enumVar{name: name, values: values} - return name, nil + return expr.String(), guards +} + +// zeroIsValid lists the required fields (by struct type) whose zero value is a valid +// value, so it is not reported as missing. Presence comes from ForceSendFields only, and +// these structs have none to record an explicit zero value. +var zeroIsValid = map[reflect.Type]string{ + // An empty default is a common, valid job parameter default. + reflect.TypeFor[jobs.JobParameterDefinition](): "default", } -// field is the field with JSON name key of the struct v points to. +// field describes the field k of the struct type st as seen from variable v. type field struct { - key string - typ reflect.Type - expr string // selector of the field - guards []string // nil checks of the embedded pointers on the way + key, goName string + typ reflect.Type + expr string + guards []string + fsf string // selector of the governing ForceSendFields, if any + forceEmpty bool // the field has no omitempty } -func structField(st reflect.Type, key string) (field, bool) { - index, ok := fieldIndex(st, key) - if !ok { - return field{}, false - } - f := field{key: key, expr: "v"} - t := st - for i, x := range index { - sf := deref(t).Field(x) - f.expr += "." + sf.Name - if i < len(index)-1 && sf.Type.Kind() == reflect.Pointer { - f.guards = append(f.guards, f.expr+" != nil") - } - t = sf.Type - } - f.typ = t - return f, true +func structField(v string, st reflect.Type, k string) field { + info := structvar.GetStructInfo(st) + expr, guards := selector(v, st, info.Fields[k]) + f := field{key: k, goName: info.GolangNames[k], typ: st.FieldByIndex(info.Fields[k]).Type, expr: expr, guards: guards, forceEmpty: info.ForceEmpty[k]} + if index, ok := info.ForceSendFieldsIndex[k]; ok { + f.fsf, _ = selector(v, st, index) + } + return f } -func (f field) guarded(cond string) string { - if len(f.guards) == 0 { +func guarded(guards []string, cond string) string { + if len(guards) == 0 { return cond } - return strings.Join(f.guards, " && ") + " && " + cond + return "(" + strings.Join(guards, " && ") + " && (" + cond + "))" } -// typedSet returns the condition under which the typed value shows the field is set, -// or "" if it cannot (a struct is never nil). A field that is not set by this -// condition can still be set to its zero value, so the node is asked. -func (f field) typedSet() string { +// forcedCond returns the condition that the field is listed in its ForceSendFields. +func (f field) forcedCond() string { + if f.fsf == "" { + return "" + } + return fmt.Sprintf(" || walk.Forced(%s, %q)", f.fsf, f.goName) +} + +// present returns the condition under which a required field counts as set. Presence +// comes from the value and ForceSendFields only, never from locations: a scalar or a +// struct is set if it is not zero or is in ForceSendFields (decoding records a written +// zero value there, e.g. a job parameter's `default: ""`); a pointer, slice, map or +// interface if it is not nil. A value that cannot hold a string can also be replaced +// by a pure reference. +func (f field) present() string { + ref := fmt.Sprintf(" || n.IsRef(%q)", f.key) switch f.typ.Kind() { case reflect.String: - return f.guarded(f.expr + ` != ""`) + return guarded(f.guards, f.expr+` != ""`+f.forcedCond()) case reflect.Pointer, reflect.Slice, reflect.Map, reflect.Interface: - return f.guarded(f.expr + " != nil") + return guarded(f.guards, f.expr+" != nil") + ref case reflect.Bool: - return f.guarded(f.expr) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64: - return f.guarded(f.expr + " != 0") + return guarded(f.guards, f.expr+f.forcedCond()) + ref + case reflect.Struct: + // A struct whose fields are only references is zero, but set. + return guarded(f.guards, fmt.Sprintf("!walk.IsZeroStruct(&%s)", f.expr)+f.forcedCond()) + fmt.Sprintf(" || n.HasRefs(%q)", f.key) default: - return "" + return guarded(f.guards, f.expr+" != 0"+f.forcedCond()) + ref + } +} + +// isSet returns the condition under which the field is set as [structvar.View.Get] +// finds it valid: like [field.present], and a forced nil pointer, slice or map is also +// set. +func (f field) isSet() string { + switch f.typ.Kind() { + case reflect.Pointer, reflect.Slice, reflect.Map, reflect.Interface: + return guarded(f.guards, f.expr+" != nil"+f.forcedCond()) + fmt.Sprintf(" || n.IsRef(%q)", f.key) + default: + return f.present() } } // emitStruct writes the function walking struct node t. -func (g *gen) emitStruct(t *tnode) error { +func (g *gen) emitStruct(t *tnode) { st := deref(t.typ) - if st.Kind() != reflect.Struct { - return fmt.Errorf("%s is not a struct", st) + fmt.Fprintf(&g.out, "\nfunc %s(w *%s, n *walk.Node, v *%s) {\n", g.funcFor(t), g.walkerType, g.typeName(st)) + fmt.Fprintf(&g.out, "\tif v == nil {\n") + for _, k := range t.required { + fmt.Fprintf(&g.out, "\t\tw.missing(n, %q)\n", k) } - name := g.typeName(st) - fmt.Fprintf(&g.out, "\nfunc %s(w *walker, n *node, v *%s) {\n", g.funcFor(t), name) - // The value is null or a pure variable reference. - fmt.Fprintf(&g.out, "\tif v == nil {\n\t\tv = &%s{}\n\t}\n", name) + fmt.Fprintf(&g.out, "\t\treturn\n\t}\n") for _, k := range t.required { - cond := "" - if f, ok := structField(st, k); ok { - cond = f.typedSet() - } - if cond == "" { - fmt.Fprintf(&g.out, "\tif !n.has(%q) {\n\t\tw.missing(n, %q)\n\t}\n", k, k) - } else { - fmt.Fprintf(&g.out, "\tif !(%s) && !n.has(%q) {\n\t\tw.missing(n, %q)\n\t}\n", cond, k, k) + if zeroIsValid[st] == k { + continue } + f := structField("v", st, k) + fmt.Fprintf(&g.out, "\tif !(%s) {\n\t\tw.missing(n, %q)\n\t}\n", f.present(), k) + } + for _, d := range t.defaults { + f := structField("v", st, d.key) + fmt.Fprintf(&g.out, "\tif !(%s) {\n\t\tc := n.Child(%q)\n\t\tw.unset(&c, %d)\n\t}\n", f.isSet(), d.key, d.index) } for _, k := range sortedKeys(t.children) { - f, ok := structField(st, k) - if !ok { - return fmt.Errorf("no field %q in %s", k, st) - } - if err := g.emitField(f, t.children[k]); err != nil { - return err - } + g.emitField(structField("v", st, k), t.children[k]) } fmt.Fprintf(&g.out, "}\n") - return nil } -// emitField writes the code visiting the field f of the struct v (node n). -func (g *gen) emitField(f field, child *tnode) error { +// emitField writes the code visiting the field f of the struct v. +func (g *gen) emitField(f field, child *tnode) { if len(f.guards) > 0 { fmt.Fprintf(&g.out, "\tif %s {\n", strings.Join(f.guards, " && ")) } switch { case child.enum != nil: - if f.typ.Kind() != reflect.String { - return fmt.Errorf("enum field %q of type %s", f.key, f.typ) - } - values, err := g.enumVarFor(f.typ, child.enum) - if err != nil { - return err - } - fmt.Fprintf(&g.out, "\tw.enumField(n, %q, string(%s), %s)\n", f.key, f.expr, values) + fmt.Fprintf(&g.out, "\tif s := string(%s); s != \"\"%s {\n\t\tc := n.Child(%q)\n\t\tw.enumValue(&c, s, %s)\n\t}\n", f.expr, f.forcedCond(), f.key, g.enumVar(child.enum)) case f.typ.Kind() == reflect.Pointer: - fmt.Fprintf(&g.out, "\tif c := n.child(%q); c.exists() {\n\t\t%s(w, &c, %s)\n\t}\n", f.key, g.funcFor(child), f.expr) + fn := g.funcFor(child) + fmt.Fprintf(&g.out, "\tif x := %s; x != nil {\n\t\tc := n.Child(%q)\n\t\t%s(w, &c, x)\n\t}", f.expr, f.key, fn) + if g.refBranches { + // A forced nil value (e.g. "run_job_task:") or one replaced by a pure + // reference is a node without fields. + cond := strings.TrimPrefix(f.forcedCond()+fmt.Sprintf(" || n.IsRef(%q)", f.key), " || ") + fmt.Fprintf(&g.out, " else if %s {\n\t\tc := n.Child(%q)\n\t\t%s(w, &c, nil)\n\t}", cond, f.key, fn) + } + fmt.Fprintf(&g.out, "\n") case f.typ.Kind() == reflect.Struct: - fmt.Fprintf(&g.out, "\tif c := n.child(%q); c.exists() {\n\t\t%s(w, &c, &%s)\n\t}\n", f.key, g.funcFor(child), f.expr) + fmt.Fprintf(&g.out, "\tif x := &%s; !walk.IsZeroStruct(x)%s || n.HasRefs(%q) {\n\t\tc := n.Child(%q)\n\t\t%s(w, &c, x)\n\t}\n", f.expr, f.forcedCond(), f.key, f.key, g.funcFor(child)) case f.typ.Kind() == reflect.Slice || f.typ.Kind() == reflect.Map: - fmt.Fprintf(&g.out, "\tif len(%s) > 0 {\n\t\tc := n.child(%q)\n", f.expr, f.key) - if err := g.emitContainer("\t\t", f.expr, f.typ, child, 0); err != nil { - return err - } + fmt.Fprintf(&g.out, "\tif len(%s) > 0 {\n\t\tc := n.Child(%q)\n", f.expr, f.key) + g.emitContainer("\t\t", f.expr, f.typ, child) fmt.Fprintf(&g.out, "\t}\n") default: - return fmt.Errorf("unsupported field %q of type %s", f.key, f.typ) + panic(fmt.Sprintf("unsupported field %s of type %s", f.key, f.typ)) } if len(f.guards) > 0 { fmt.Fprintf(&g.out, "\t}\n") } - return nil } // emitContainer writes the loop over the slice or map expr (node c) and its elements. -func (g *gen) emitContainer(indent, expr string, typ reflect.Type, t *tnode, depth int) error { - if len(t.children) != 1 { - return fmt.Errorf("container %s needs exactly one wildcard child", typ) - } +func (g *gen) emitContainer(indent, expr string, typ reflect.Type, t *tnode) { var elem *tnode for _, c := range t.children { elem = c } + if elem == nil || len(t.children) != 1 { + panic(fmt.Sprintf("container %s needs exactly one wildcard child", typ)) + } if typ.Kind() == reflect.Slice { - i := fmt.Sprintf("i%d", depth) - fmt.Fprintf(&g.out, "%sfor %s := range %s {\n%s\te := c.elem(%s)\n", indent, i, expr, indent, i) - if err := g.emitElem(indent+"\t", expr+"["+i+"]", typ.Elem(), elem, depth); err != nil { - return err - } + fmt.Fprintf(&g.out, "%sfor i := range %s {\n%s\te := c.Elem(i)\n", indent, expr, indent) + g.emitElem(indent+"\t", expr+"[i]", typ.Elem(), elem) } else { - k, x := fmt.Sprintf("k%d", depth), fmt.Sprintf("x%d", depth) - key := k - if typ.Key() != reflect.TypeFor[string]() { - key = "string(" + k + ")" - } // The dynamic walk this replaces never matched an empty key against the wildcard. - fmt.Fprintf(&g.out, "%sfor %s, %s := range %s {\n%s\tif %s == \"\" {\n%s\t\tcontinue\n%s\t}\n%s\te := c.child(%s)\n", indent, k, x, expr, indent, key, indent, indent, indent, key) - if err := g.emitElem(indent+"\t", x, typ.Elem(), elem, depth); err != nil { - return err - } + fmt.Fprintf(&g.out, "%sfor k, x := range %s {\n%s\tif k == \"\" {\n%s\t\tcontinue\n%s\t}\n%s\te := c.Child(k)\n", indent, expr, indent, indent, indent, indent) + g.emitElem(indent+"\t", "x", typ.Elem(), elem) } fmt.Fprintf(&g.out, "%s}\n", indent) - return nil } // emitElem writes the code visiting the element expr (node e) of type typ. -func (g *gen) emitElem(indent, expr string, typ reflect.Type, t *tnode, depth int) error { +func (g *gen) emitElem(indent, expr string, typ reflect.Type, t *tnode) { switch { case t.enum != nil: - if typ.Kind() != reflect.String { - return fmt.Errorf("enum element of type %s", typ) - } - values, err := g.enumVarFor(typ, t.enum) - if err != nil { - return err - } - fmt.Fprintf(&g.out, "%sw.enumElem(&e, string(%s), %s)\n", indent, expr, values) + fmt.Fprintf(&g.out, "%sw.enumValue(&e, string(%s), %s)\n", indent, expr, g.enumVar(t.enum)) case typ.Kind() == reflect.Pointer: fmt.Fprintf(&g.out, "%s%s(w, &e, %s)\n", indent, g.funcFor(t), expr) case typ.Kind() == reflect.Struct: fmt.Fprintf(&g.out, "%s%s(w, &e, &%s)\n", indent, g.funcFor(t), expr) case typ.Kind() == reflect.Slice || typ.Kind() == reflect.Map: fmt.Fprintf(&g.out, "%s{\n%s\tc := e\n", indent, indent) - if err := g.emitContainer(indent+"\t", expr, typ, t, depth+1); err != nil { - return err - } + g.emitContainer(indent+"\t", expr, typ, t) fmt.Fprintf(&g.out, "%s}\n", indent) default: - return fmt.Errorf("unsupported element type %s", typ) + panic(fmt.Sprintf("unsupported element type %s", typ)) } - return nil } -// generateWalker writes the walker of the required fields (by parent pattern) and the -// enum values (by field pattern) to file. The entry point is ConfigRoot. -func generateWalker(file, prefix, doc string, required, enums map[string][]string) error { +// generateWalker writes the walker for the patterns to file; fn is the name of the +// entry point. +// walkerSpec describes a generated walker. +type walkerSpec struct { + file, pkg, prefix, doc string + walkerType string + required, enums map[string][]string + defaults []string // patterns of fields with a default, in order +} + +func generateWalker(spec walkerSpec) error { + file, prefix, doc := spec.file, spec.prefix, spec.doc root := newTnode(reflect.TypeFor[config.Root]()) - for _, p := range sortedKeys(required) { + for _, p := range sortedKeys(spec.required) { n, err := root.insert(p) if err != nil { return err } - n.required = required[p] + n.required = spec.required[p] } - for _, p := range sortedKeys(enums) { + for _, p := range sortedKeys(spec.enums) { n, err := root.insert(p) if err != nil { return err } - n.enum = enums[p] + n.enum = spec.enums[p] + } + for i, p := range spec.defaults { + pattern, err := structpath.ParsePattern(p) + if err != nil { + return err + } + key, ok := pattern.StringKey() + if !ok { + return fmt.Errorf("default %s: the last component must be a key", p) + } + n, err := root.insert(pattern.Parent().String()) + if err != nil { + return err + } + n.defaults = append(n.defaults, defaultKey{key, i}) } - g := newGen(prefix) + g := newGen(prefix, spec.required != nil) + g.walkerType = spec.walkerType g.funcFor(root) for len(g.pending) > 0 { t := g.pending[0] g.pending = g.pending[1:] - if err := g.emitStruct(t); err != nil { - return err - } + g.emitStruct(t) } var src bytes.Buffer - fmt.Fprintf(&src, "// Code generated by bundle/internal/validation. DO NOT EDIT.\n\n// %s\n\npackage validate\n\nimport (\n", doc) + fmt.Fprintf(&src, "// Code generated by bundle/internal/validation. DO NOT EDIT.\n\n// %s\n\npackage %s\n\nimport (\n\t\"github.com/databricks/cli/bundle/config/walk\"\n", doc, spec.pkg) paths := make([]string, 0, len(g.imports)) for p := range g.imports { paths = append(paths, p) } - slices.Sort(paths) + slices.SortFunc(paths, cmp.Compare) for _, p := range paths { if path.Base(p) == g.imports[p] { fmt.Fprintf(&src, "\t%q\n", p) @@ -460,49 +471,84 @@ func generateWalker(file, prefix, doc string, required, enums map[string][]strin } } fmt.Fprintf(&src, ")\n") - vars := slices.SortedFunc(maps.Values(g.enums), func(a, b enumVar) int { return cmp.Compare(a.name, b.name) }) - for _, e := range vars { - var quoted []string - for _, value := range e.values { - quoted = append(quoted, strconv.Quote(value)) - } - fmt.Fprintf(&src, "\nvar %s = []string{%s}\n", e.name, strings.Join(quoted, ", ")) + keys := make([]string, 0, len(g.enums)) + for k := range g.enums { + keys = append(keys, k) + } + slices.SortFunc(keys, func(a, b string) int { return cmp.Compare(g.enums[a], g.enums[b]) }) + for _, k := range keys { + values := strings.Split(k, "\x00") + fmt.Fprintf(&src, "\nvar %s = %s\n", g.enums[k], "[]string"+formatSliceToString(values)) } src.Write(g.out.Bytes()) formatted, err := format.Source(src.Bytes()) if err != nil { + _ = os.WriteFile(file+".broken", src.Bytes(), 0o644) return fmt.Errorf("failed to format %s: %w", file, err) } return os.WriteFile(file, formatted, 0o644) } -// withoutTargets drops the patterns under targets and environments: they are merged -// into the root before validation. -func withoutTargets(patterns map[string][]string) map[string][]string { - out := map[string][]string{} - for p, v := range patterns { - top, _, _ := strings.Cut(p, ".") - if top == "targets" || top == "environments" { - continue +// patternMaps returns the required fields by parent pattern and the enum values by +// field pattern, without targets and environments (they are merged into the root). +func patternMaps() (map[string][]string, map[string][]string, error) { + required, err := extractRequiredFields(reflect.TypeFor[config.Root]()) + if err != nil { + return nil, nil, err + } + enums, err := extractEnumFields(reflect.TypeFor[config.Root]()) + if err != nil { + return nil, nil, err + } + skip := func(p string) bool { + k := getGroupingKey(p) + return k == "targets" || k == "environments" + } + r := map[string][]string{} + for p, fields := range required { + if !skip(p) { + r[p] = fields + } + } + e := map[string][]string{} + for p, values := range enums { + if !skip(p) { + e[p] = values } - out[p] = v } - return out + return r, e, nil } func generateWalkers(dir string) error { - required, err := extractRequiredFields(reflect.TypeFor[config.Root]()) + required, enums, err := patternMaps() if err != nil { - return fmt.Errorf("failed to extract required fields: %w", err) + return err } - enums, err := extractEnumFields(reflect.TypeFor[config.Root]()) + err = generateWalker(walkerSpec{ + file: dir + "/required_walk.go", pkg: "validate", prefix: "required", walkerType: "walker", + doc: "requiredWalk reports required fields that are not set.", required: required, + }) if err != nil { - return fmt.Errorf("failed to extract enum fields: %w", err) + return err + } + err = generateWalker(walkerSpec{ + file: dir + "/enum_walk.go", pkg: "validate", prefix: "enum", walkerType: "walker", + doc: "enumWalk reports enum fields with invalid values.", enums: enums, + }) + if err != nil { + return err + } + err = generateWalker(walkerSpec{ + file: "bundle/config/mutator/resourcemutator/defaults_walk.go", pkg: "resourcemutator", prefix: "defaults", walkerType: "defaultsWalker", + doc: "defaultsWalk calls unset for the fields with a default (resourcedefaults.Defaults) that are not set.", defaults: resourcedefaults.Patterns(), + }) + if err != nil { + return err } - err = generateWalker(dir+"/required_walk.go", "required", "requiredConfigRoot reports the required fields that are not set.", withoutTargets(required), nil) + err = generateStringsWalker("bundle/config/walk/strings_walk.go") if err != nil { return err } - return generateWalker(dir+"/enum_walk.go", "enum", "enumConfigRoot reports the enum fields with invalid values.", nil, withoutTargets(enums)) + return generateResourcesIterate("bundle/config/resources_iterate.go") } diff --git a/bundle/libraries/expand_glob_references.go b/bundle/libraries/expand_glob_references.go index df089f880a7..8d6876b2ac5 100644 --- a/bundle/libraries/expand_glob_references.go +++ b/bundle/libraries/expand_glob_references.go @@ -8,31 +8,29 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/patchwheel" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/compute" ) type expand struct{} -func matchError(p dyn.Path, l []diag.Location, message string) diag.Diagnostic { +func matchError(p *structpath.PathNode, l []diag.Location, message string) diag.Diagnostic { return diag.Diagnostic{ Severity: diag.Error, Summary: message, Locations: l, - Paths: dyn.ToStructPaths(p), + Paths: []*structpath.PathNode{p}, } } -func getLibDetails(v dyn.Value) (string, string, bool) { - m := v.MustMap() - whl, ok := m.GetByString("whl") - if ok { - return whl.MustString(), "whl", true +func getLibDetails(lib compute.Library) (string, string, bool) { + if lib.Whl != "" { + return lib.Whl, "whl", true } - jar, ok := m.GetByString("jar") - if ok { - return jar.MustString(), "jar", true + if lib.Jar != "" { + return lib.Jar, "jar", true } return "", "", false @@ -72,48 +70,70 @@ func isGlobPattern(path string) bool { return strings.ContainsAny(path, "*?[") } -func expandLibraries(ctx context.Context, b *bundle.Bundle, p dyn.Path, v dyn.Value) (diag.Diagnostics, []dyn.Value) { - var output []dyn.Value +// expandFunc expands the item at path p into the items it is replaced with. +// If relocate is not empty, it names the field of each new item that takes the locations of the item. +type expandFunc[T any] func(ctx context.Context, b *bundle.Bundle, p *structpath.PathNode, item T) (output []T, relocate string, diags diag.Diagnostics) + +// expandSequence replaces the items of the sequence at path p with the items expandFunc returns for them. +// New items keep the locations of the item they were expanded from. +func expandSequence[T any](ctx context.Context, b *bundle.Bundle, p *structpath.PathNode, items *[]T, fn expandFunc[T]) diag.Diagnostics { var diags diag.Diagnostics + output := make([]T, 0, len(*items)) + var sources [][]int + var relocates []string + var locations [][]diag.Location + for i, item := range *items { + ip := structpath.NewIndex(p, i) + expanded, relocate, d := fn(ctx, b, ip, item) + diags = diags.Extend(d) + locs := b.Config.GetLocationsOfExact(ip) + for _, e := range expanded { + output = append(output, e) + sources = append(sources, []int{i}) + relocates = append(relocates, relocate) + locations = append(locations, locs) + } + } + + *items = output + b.Config.UpdateSequence(p, sources) + for i, relocate := range relocates { + if relocate != "" { + b.Config.SetLocations(structpath.NewPath(p, i, relocate), locations[i]) + } + } - libs := v.MustSequence() - for i, lib := range libs { - lp := p.Append(dyn.Index(i)) + return diags +} + +func expandLibraries(ctx context.Context, b *bundle.Bundle, p *structpath.PathNode, libs *[]compute.Library) diag.Diagnostics { + return expandSequence(ctx, b, p, libs, func(ctx context.Context, b *bundle.Bundle, ip *structpath.PathNode, lib compute.Library) ([]compute.Library, string, diag.Diagnostics) { path, libType, supported := getLibDetails(lib) if !supported || !IsLibraryLocal(path) { - output = append(output, lib) - continue + return []compute.Library{lib}, "", nil } - lp = lp.Append(dyn.Key(libType)) - matches, err := findMatches(ctx, b, path) if err != nil { - diags = diags.Append(matchError(lp, lib.Locations(), err.Error())) - continue + return nil, "", diag.Diagnostics{matchError(structpath.NewStringKey(ip, libType), b.Config.GetLocationsOfExact(ip), err.Error())} } + var output []compute.Library for _, match := range matches { - output = append(output, dyn.NewValue(map[string]dyn.Value{ - libType: dyn.NewValue(match, lib.Locations()), - }, lib.Locations())) + if libType == "whl" { + output = append(output, compute.Library{Whl: match}) + } else { + output = append(output, compute.Library{Jar: match}) + } } - } - - return diags, output + return output, libType, nil + }) } -func expandEnvironmentDeps(ctx context.Context, b *bundle.Bundle, p dyn.Path, v dyn.Value) (diag.Diagnostics, []dyn.Value) { - var output []dyn.Value - var diags diag.Diagnostics - - deps := v.MustSequence() - for i, dep := range deps { - lp := p.Append(dyn.Index(i)) - path := dep.MustString() +func expandEnvironmentDeps(ctx context.Context, b *bundle.Bundle, p *structpath.PathNode, deps *[]string) diag.Diagnostics { + return expandSequence(ctx, b, p, deps, func(ctx context.Context, b *bundle.Bundle, ip *structpath.PathNode, path string) ([]string, string, diag.Diagnostics) { if !IsLibraryLocal(path) { - output = append(output, dep) - continue + return []string{path}, "", nil } // Strip extras before globbing so "[...]" isn't read as a glob class, then re-append. @@ -121,134 +141,41 @@ func expandEnvironmentDeps(ctx context.Context, b *bundle.Bundle, p dyn.Path, v matches, err := findMatches(ctx, b, path) if err != nil { - diags = diags.Append(matchError(lp, dep.Locations(), err.Error())) - continue + return nil, "", diag.Diagnostics{matchError(ip, b.Config.GetLocationsOfExact(ip), err.Error())} } + var output []string for _, match := range matches { - output = append(output, dyn.NewValue(match+extras, dep.Locations())) + output = append(output, match+extras) } - } - - return diags, output -} - -type expandPattern struct { - pattern dyn.Pattern - fn func(ctx context.Context, b *bundle.Bundle, p dyn.Path, v dyn.Value) (diag.Diagnostics, []dyn.Value) + return output, "", nil + }) } -var taskLibrariesPattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("tasks"), - dyn.AnyIndex(), - dyn.Key("libraries"), -) - -var forEachTaskLibrariesPattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("tasks"), - dyn.AnyIndex(), - dyn.Key("for_each_task"), - dyn.Key("task"), - dyn.Key("libraries"), -) - -var aiRuntimeCodeSourcePattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("tasks"), - dyn.AnyIndex(), - dyn.Key("ai_runtime_task"), - dyn.Key("code_source_path"), -) - -var forEachAiRuntimeCodeSourcePattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("tasks"), - dyn.AnyIndex(), - dyn.Key("for_each_task"), - dyn.Key("task"), - dyn.Key("ai_runtime_task"), - dyn.Key("code_source_path"), -) - -var envDepsPattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("jobs"), - dyn.AnyKey(), - dyn.Key("environments"), - dyn.AnyIndex(), - dyn.Key("spec"), - dyn.Key("dependencies"), -) - -var pipelineEnvDepsPattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("pipelines"), - dyn.AnyKey(), - dyn.Key("environment"), - dyn.Key("dependencies"), -) - -var clusterLibrariesPattern = dyn.NewPattern( - dyn.Key("resources"), - dyn.Key("clusters"), - dyn.AnyKey(), - dyn.Key("libraries"), -) - func (e *expand) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - expanders := []expandPattern{ - { - pattern: taskLibrariesPattern, - fn: expandLibraries, - }, - { - pattern: forEachTaskLibrariesPattern, - fn: expandLibraries, - }, - { - pattern: envDepsPattern, - fn: expandEnvironmentDeps, - }, - { - pattern: pipelineEnvDepsPattern, - fn: expandEnvironmentDeps, - }, - { - pattern: clusterLibrariesPattern, - fn: expandLibraries, - }, - } - var diags diag.Diagnostics - - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - var err error - for _, expander := range expanders { - v, err = dyn.MapByPattern(v, expander.pattern, func(p dyn.Path, lv dyn.Value) (dyn.Value, error) { - d, output := expander.fn(ctx, b, p, lv) - diags = diags.Extend(d) - return dyn.NewValue(output, lv.Locations()), nil - }) - if err != nil { - return dyn.InvalidValue, err - } - } - - return v, nil - }) - if err != nil { - diags = diags.Extend(diag.FromErr(err)) + r := &b.Config.Resources + + // Task libraries, the libraries of tasks in for_each_task, job and pipeline + // environment dependencies, then cluster libraries. + for _, nested := range []bool{false, true} { + r.IterateTaskLibraries(nested, func(p *structpath.PathNode, libs *[]compute.Library) bool { + diags = diags.Extend(expandLibraries(ctx, b, p, libs)) + return true + }) } + r.IterateJobEnvironmentDependencies(func(p *structpath.PathNode, deps *[]string) bool { + diags = diags.Extend(expandEnvironmentDeps(ctx, b, p, deps)) + return true + }) + r.IteratePipelineEnvironmentDependencies(func(p *structpath.PathNode, deps *[]string) bool { + diags = diags.Extend(expandEnvironmentDeps(ctx, b, p, deps)) + return true + }) + r.IterateClusterLibraries(func(p *structpath.PathNode, libs *[]compute.Library) bool { + diags = diags.Extend(expandLibraries(ctx, b, p, libs)) + return true + }) return diags } diff --git a/bundle/libraries/expand_glob_references_test.go b/bundle/libraries/expand_glob_references_test.go index 331fbf2e7cb..037a91a4d32 100644 --- a/bundle/libraries/expand_glob_references_test.go +++ b/bundle/libraries/expand_glob_references_test.go @@ -10,7 +10,7 @@ import ( "github.com/databricks/cli/bundle/internal/bundletest" "github.com/databricks/cli/internal/testutil" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/compute" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/pipelines" @@ -283,11 +283,19 @@ func TestExpandGlobReferencesPreservesLocations(t *testing.T) { diags := bundle.Apply(t.Context(), b, ExpandGlobReferences()) require.Empty(t, diags) - libs, err := dyn.GetByPath(b.Config.Value(), dyn.MustPathFromString("resources.jobs.job.tasks[0].libraries")) - require.NoError(t, err) - assert.Equal(t, loc.File, libs.Location().File) + libs := b.Config.GetLocationsOfExact(structpath.MustParsePath("resources.jobs.job.tasks[0].libraries")) + require.NotEmpty(t, libs) + assert.Equal(t, loc.File, libs[0].File) - deps, err := dyn.GetByPath(b.Config.Value(), dyn.MustPathFromString("resources.pipelines.pipeline.environment.dependencies")) - require.NoError(t, err) - assert.Equal(t, loc.File, deps.Location().File) + lib := b.Config.GetLocationsOfExact(structpath.MustParsePath("resources.jobs.job.tasks[0].libraries[0].whl")) + require.NotEmpty(t, lib) + assert.Equal(t, loc.File, lib[0].File) + + deps := b.Config.GetLocationsOfExact(structpath.MustParsePath("resources.pipelines.pipeline.environment.dependencies")) + require.NotEmpty(t, deps) + assert.Equal(t, loc.File, deps[0].File) + + dep := b.Config.GetLocationsOfExact(structpath.MustParsePath("resources.pipelines.pipeline.environment.dependencies[0]")) + require.NotEmpty(t, dep) + assert.Equal(t, loc.File, dep[0].File) } diff --git a/bundle/libraries/library_values.go b/bundle/libraries/library_values.go new file mode 100644 index 00000000000..81b21a469a9 --- /dev/null +++ b/bundle/libraries/library_values.go @@ -0,0 +1,69 @@ +package libraries + +import ( + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/databricks-sdk-go/service/compute" +) + +// visitLibraryValues calls fn for every library path value of the configuration, in +// this order: the wheels and jars of task libraries, of the libraries of tasks in +// for_each_task and of cluster libraries, the environment dependencies of jobs and of +// pipelines, and (codeSources) the code_source_path of AI Runtime tasks (also nested). +// Empty values are not visited. It stops at the first error fn returns. +func visitLibraryValues(r *config.Resources, codeSources bool, fn func(p *structpath.PathNode, s *string) error) error { + v := &libraryValues{fn: fn} + r.IterateTaskLibraries(false, v.wheels) + r.IterateTaskLibraries(false, v.jars) + r.IterateTaskLibraries(true, v.wheels) + r.IterateTaskLibraries(true, v.jars) + r.IterateClusterLibraries(v.wheels) + r.IterateClusterLibraries(v.jars) + r.IterateJobEnvironmentDependencies(v.dependencies) + r.IteratePipelineEnvironmentDependencies(v.dependencies) + if codeSources { + r.IterateAiRuntimeCodeSources(false, v.value) + r.IterateAiRuntimeCodeSources(true, v.value) + } + return v.err +} + +// libraryValues visits library path values with fn until it returns an error. +type libraryValues struct { + fn func(p *structpath.PathNode, s *string) error + err error +} + +func (v *libraryValues) value(p *structpath.PathNode, s *string) bool { + if v.err == nil && *s != "" { + v.err = v.fn(p, s) + } + return v.err == nil +} + +func (v *libraryValues) wheels(p *structpath.PathNode, libs *[]compute.Library) bool { + for i := range *libs { + if (*libs)[i].Whl != "" && !v.value(structpath.NewPath(p, i, "whl"), &(*libs)[i].Whl) { + return false + } + } + return v.err == nil +} + +func (v *libraryValues) jars(p *structpath.PathNode, libs *[]compute.Library) bool { + for i := range *libs { + if (*libs)[i].Jar != "" && !v.value(structpath.NewPath(p, i, "jar"), &(*libs)[i].Jar) { + return false + } + } + return v.err == nil +} + +func (v *libraryValues) dependencies(p *structpath.PathNode, deps *[]string) bool { + for i := range *deps { + if (*deps)[i] != "" && !v.value(structpath.NewIndex(p, i), &(*deps)[i]) { + return false + } + } + return v.err == nil +} diff --git a/bundle/libraries/remote_path.go b/bundle/libraries/remote_path.go index 70e577a1a2a..796f1a920f6 100644 --- a/bundle/libraries/remote_path.go +++ b/bundle/libraries/remote_path.go @@ -11,8 +11,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/patchwheel" + "github.com/databricks/cli/libs/structs/structpath" ) // LocalLibraryPaths returns the local file paths of all libraries in the bundle @@ -101,25 +101,20 @@ func ReplaceWithRemotePath(ctx context.Context, b *bundle.Bundle) (map[string][] sources := slices.Sorted(maps.Keys(libs)) // Update all the config paths to point to the uploaded location - err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - for _, source := range sources { - locations := libs[source] - remotePath := path.Join(uploadPath, filepath.Base(source)) - - for _, location := range locations { - // Re-append the extras suffix that was stripped before upload. - remotePathWithExtras := remotePath + location.extras - v, err = dyn.SetByPath(v, location.configPath, dyn.NewValue(remotePathWithExtras, []diag.Location{location.location})) - if err != nil { - return v, fmt.Errorf("internal error: failed to update path %#v to %#v: %w", source, remotePathWithExtras, err) - } + for _, source := range sources { + locations := libs[source] + remotePath := path.Join(uploadPath, filepath.Base(source)) + + for _, location := range locations { + // Re-append the extras suffix that was stripped before upload. + remotePathWithExtras := remotePath + location.extras + err = b.Config.Set(location.configPath, remotePathWithExtras) + if err != nil { + diags = diags.Extend(diag.FromErr(fmt.Errorf("internal error: failed to update path %#v to %#v: %w", source, remotePathWithExtras, err))) + return libs, diags } + b.Config.SetLocations(location.configPath, []diag.Location{location.location}) } - - return v, nil - }) - if err != nil { - diags = diags.Extend(diag.FromErr(err)) } return libs, diags @@ -134,94 +129,51 @@ func ReplaceWithRemotePath(ctx context.Context, b *bundle.Bundle) (map[string][] func collectLocalLibraries(b *bundle.Bundle) (map[string][]LocationToUpdate, error) { libs := make(map[string]([]LocationToUpdate)) - patterns := []dyn.Pattern{ - taskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("whl")), - taskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("jar")), - forEachTaskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("whl")), - forEachTaskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("jar")), - clusterLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("whl")), - clusterLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("jar")), - envDepsPattern.Append(dyn.AnyIndex()), - pipelineEnvDepsPattern.Append(dyn.AnyIndex()), - // The AI Runtime task's code_source_path is a local archive (typically an - // artifact-built .tar.gz) that must be uploaded and referenced by its remote - // path, exactly like a wheel or jar library. - aiRuntimeCodeSourcePattern, - forEachAiRuntimeCodeSourcePattern, - } - - for _, pattern := range patterns { - 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) { - source, ok := v.AsString() - if !ok { - return v, fmt.Errorf("expected string, got %s", v.Kind()) - } - - if !IsLibraryLocal(source) { - return v, nil - } - - // Split off any pip extras suffix so the upload targets the real - // file; the suffix is re-appended to the remote path afterwards. - source, extras := patchwheel.SplitWheelExtras(source) + // The AI Runtime task's code_source_path is a local archive (typically an + // artifact-built .tar.gz) that must be uploaded and referenced by its remote path, + // exactly like a wheel or jar library. + err := visitLibraryValues(&b.Config.Resources, true, func(p *structpath.PathNode, s *string) error { + source := *s + if !IsLibraryLocal(source) { + return nil + } - source = filepath.Join(b.SyncRootPath, source) - libs[source] = append(libs[source], LocationToUpdate{ - configPath: p, - location: v.Location(), - extras: extras, - }) + // Split off any pip extras suffix so the upload targets the real + // file; the suffix is re-appended to the remote path afterwards. + source, extras := patchwheel.SplitWheelExtras(source) - return v, nil - }) + source = filepath.Join(b.SyncRootPath, source) + libs[source] = append(libs[source], LocationToUpdate{ + configPath: p, + location: b.Config.GetLocationOfExact(p), + extras: extras, }) - if err != nil { - return nil, err - } + return nil + }) + if err != nil { + return nil, err } - artifactPattern := dyn.NewPattern( - dyn.Key("artifacts"), - dyn.AnyKey(), - dyn.Key("files"), - dyn.AnyIndex(), - ) - - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.MapByPattern(v, artifactPattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - file, ok := v.AsMap() - if !ok { - return v, fmt.Errorf("expected map, got %s", v.Kind()) - } - - sv, ok := file.GetByString("source") - if !ok { - return v, nil - } - - source, ok := sv.AsString() - if !ok { - return v, fmt.Errorf("expected string, got %s", v.Kind()) + for _, name := range slices.Sorted(maps.Keys(b.Config.Artifacts)) { + a := b.Config.Artifacts[name] + if a == nil { + continue + } + for i, f := range a.Files { + p := structpath.NewPath(nil, "artifacts", name, "files", i) + // An empty source is missing (ArtifactFile has no ForceSendFields). + if f.Source == "" { + continue } - - if sv, ok = file.GetByString("patched"); ok { - patched, ok := sv.AsString() - if ok && patched != "" { - source = patched - } + source := f.Source + if f.Patched != "" { + source = f.Patched } - libs[source] = append(libs[source], LocationToUpdate{ - configPath: p.Append(dyn.Key("remote_path")), - location: v.Location(), + configPath: structpath.NewStringKey(p, "remote_path"), + location: b.Config.GetLocationOfExact(p), }) - - return v, nil - }) - }) - if err != nil { - return nil, err + } } return libs, nil diff --git a/bundle/libraries/same_name_libraries.go b/bundle/libraries/same_name_libraries.go index 722d42c7f64..bead58bce36 100644 --- a/bundle/libraries/same_name_libraries.go +++ b/bundle/libraries/same_name_libraries.go @@ -7,23 +7,11 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" ) type checkForSameNameLibraries struct{} -var patterns = []dyn.Pattern{ - taskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("whl")), - taskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("jar")), - forEachTaskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("whl")), - forEachTaskLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("jar")), - clusterLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("whl")), - clusterLibrariesPattern.Append(dyn.AnyIndex(), dyn.Key("jar")), - envDepsPattern.Append(dyn.AnyIndex()), - pipelineEnvDepsPattern.Append(dyn.AnyIndex()), -} - type libData struct { fullPath string locations []diag.Location @@ -35,49 +23,33 @@ func (c checkForSameNameLibraries) Apply(ctx context.Context, b *bundle.Bundle) var diags diag.Diagnostics libs := make(map[string]*libData) - err := b.Config.Mutate(func(rootConfig dyn.Value) (dyn.Value, error) { - var err error - for _, pattern := range patterns { - rootConfig, err = dyn.MapByPattern(rootConfig, pattern, func(p dyn.Path, libraryValue dyn.Value) (dyn.Value, error) { - libPath, ok := libraryValue.AsString() - if !ok { - return libraryValue, nil - } - - // If not local library, skip the check - if !IsLibraryLocal(libPath) { - return libraryValue, nil - } - - lib := filepath.Base(libPath) - // If the same basename was seen already but full path is different - // then it's a duplicate. Add the location to the location list. - lp, ok := libs[lib] - if !ok { - libs[lib] = &libData{ - fullPath: libPath, - locations: []diag.Location{libraryValue.Location()}, - paths: dyn.ToStructPaths(p), - otherPaths: []string{}, - } - } else if lp.fullPath != libPath { - lp.locations = append(lp.locations, libraryValue.Location()) - lp.paths = append(lp.paths, dyn.ToStructPath(p)) - lp.otherPaths = append(lp.otherPaths, libPath) - } + err := visitLibraryValues(&b.Config.Resources, false, func(p *structpath.PathNode, s *string) error { + libPath := *s - return libraryValue, nil - }) - if err != nil { - return dyn.InvalidValue, err - } + // If not local library, skip the check + if !IsLibraryLocal(libPath) { + return nil } - if err != nil { - return dyn.InvalidValue, err + lib := filepath.Base(libPath) + loc := b.Config.GetLocationOfExact(p) + // If the same basename was seen already but full path is different + // then it's a duplicate. Add the location to the location list. + lp, ok := libs[lib] + if !ok { + libs[lib] = &libData{ + fullPath: libPath, + locations: []diag.Location{loc}, + paths: []*structpath.PathNode{p}, + otherPaths: []string{}, + } + } else if lp.fullPath != libPath { + lp.locations = append(lp.locations, loc) + lp.paths = append(lp.paths, p) + lp.otherPaths = append(lp.otherPaths, libPath) } - return rootConfig, nil + return nil }) // Iterate over all the libraries and check if there are any duplicates. diff --git a/bundle/libraries/upload.go b/bundle/libraries/upload.go index eeb71cfb9e8..7c7087aca36 100644 --- a/bundle/libraries/upload.go +++ b/bundle/libraries/upload.go @@ -12,10 +12,10 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/filer" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/retry" + "github.com/databricks/cli/libs/structs/structpath" "golang.org/x/sync/errgroup" ) @@ -37,7 +37,7 @@ type upload struct { } type LocationToUpdate struct { - configPath dyn.Path + configPath *structpath.PathNode location diag.Location // extras is the pip extras suffix (e.g. "[train]") to re-append to the // rewritten remote path. Empty for libraries that carry no extras. diff --git a/bundle/migrate/build_state.go b/bundle/migrate/build_state.go index be8132fd82d..019771f68b3 100644 --- a/bundle/migrate/build_state.go +++ b/bundle/migrate/build_state.go @@ -14,8 +14,6 @@ import ( "github.com/databricks/cli/bundle/direct" "github.com/databricks/cli/bundle/direct/dresources" "github.com/databricks/cli/bundle/direct/dstate" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structaccess" "github.com/databricks/cli/libs/structs/structpath" @@ -58,25 +56,7 @@ func BuildStateFromTF( ) (bool, error) { warningsSeen := false // Collect all resource nodes (same patterns as makePlan). - var nodes []string - patterns := []dyn.Pattern{ - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey(), dyn.Key("permissions")), - dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey(), dyn.Key("grants")), - } - for _, pat := range patterns { - _, err := dyn.MapByPattern( - configRoot.Value(), - pat, - func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - nodes = append(nodes, p.String()) - return dyn.InvalidValue, nil - }, - ) - if err != nil { - return warningsSeen, err - } - } + nodes := configRoot.ResourceNodes() for _, node := range nodes { id, ok := tfIDs[node] @@ -113,7 +93,7 @@ func BuildStateFromTF( return warningsSeen, fmt.Errorf("%s: PrepareState: %w", node, err) } - refs, err := direct.ExtractReferences(configRoot.Value(), node, adapter.StateType()) + refs, err := direct.ExtractReferences(configRoot, node, adapter.StateType()) if err != nil { return warningsSeen, fmt.Errorf("%s: extracting references: %w", node, err) } @@ -126,12 +106,12 @@ func BuildStateFromTF( // Same logic as makePlan in bundle/direct/bundle_plan.go. var dependsOn []deployplan.DependsOnEntry //nolint:prealloc for _, refTemplate := range refs { - ref, ok := dynvar.NewRef(dyn.V(refTemplate)) + ref, ok := structvar.NewRef(refTemplate) if !ok { continue } for _, targetPath := range ref.References() { - targetPathParsed, err := dyn.NewPathFromString(targetPath) + targetPathParsed, err := structpath.ParsePath(targetPath) if err != nil { continue } @@ -281,18 +261,18 @@ func BuildStateFromTF( // referencesUndeployed reports whether refTemplate references a resource that is not in the terraform state. func referencesUndeployed(refTemplate string, tfIDs map[string]string) bool { - ref, ok := dynvar.NewRef(dyn.V(refTemplate)) + ref, ok := structvar.NewRef(refTemplate) if !ok { return false } for _, targetPath := range ref.References() { - path, err := dyn.NewPathFromString(targetPath) + path, err := structpath.ParsePath(targetPath) if err != nil { continue } targetNode, _ := config.GetNodeAndType(path) // Not a resource reference (e.g. ${var.x}). - if len(targetNode) == 0 { + if targetNode == nil { continue } if _, ok := tfIDs[targetNode.String()]; !ok { diff --git a/bundle/migrate/build_state_test.go b/bundle/migrate/build_state_test.go index 108c0591968..c1e97c37f17 100644 --- a/bundle/migrate/build_state_test.go +++ b/bundle/migrate/build_state_test.go @@ -1,7 +1,6 @@ package migrate_test import ( - "bytes" "encoding/json" "os" "path/filepath" @@ -12,24 +11,18 @@ import ( "github.com/databricks/cli/bundle/direct/dresources" "github.com/databricks/cli/bundle/direct/dstate" "github.com/databricks/cli/bundle/migrate" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/yamlloader" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // rootFromYAML builds a config.Root from a YAML snippet. // Template strings like "${resources.jobs.src.name}" are preserved in the -// internal dyn.Value so BuildStateFromTF can find them via ExtractReferences. +// configuration so BuildStateFromTF can find them via ExtractReferences. func rootFromYAML(t *testing.T, yaml string) config.Root { t.Helper() - v, err := yamlloader.LoadYAML("test", bytes.NewBufferString(yaml)) - require.NoError(t, err) - var root config.Root - require.NoError(t, convert.ToTyped(&root, v)) - require.NoError(t, root.Mutate(func(_ dyn.Value) (dyn.Value, error) { return v, nil })) - return root + root, diags := config.LoadFromBytes("test", []byte(yaml)) + require.NoError(t, diags.Error()) + return *root } func runBuildStateFromTF( diff --git a/bundle/migrate/resolve.go b/bundle/migrate/resolve.go index 23d0180e3c6..41e09176e47 100644 --- a/bundle/migrate/resolve.go +++ b/bundle/migrate/resolve.go @@ -5,16 +5,15 @@ import ( "fmt" "strings" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) // evaluateTemplate evaluates a template string like "${resources.pipelines.bar.cluster[0].label}" // by looking up each ${...} reference from TF state. func evaluateTemplate(state TFStateAttrs, template string) (string, error) { - ref, ok := dynvar.NewRef(dyn.V(template)) + ref, ok := structvar.NewRef(template) if !ok { return template, nil } diff --git a/bundle/mutator.go b/bundle/mutator.go index 41ef8fda48c..ed9bd5e56e5 100644 --- a/bundle/mutator.go +++ b/bundle/mutator.go @@ -75,19 +75,6 @@ func ApplyContext(ctx context.Context, b *Bundle, m Mutator) { log.Debugf(ctx, "Apply") - err := b.Config.MarkMutatorEntry(ctx) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("entry error: %w", err)) - return - } - - defer func() { - err := b.Config.MarkMutatorExit(ctx) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("exit error: %w", err)) - } - }() - diags := m.Apply(ctx, b) for _, d := range diags { @@ -104,34 +91,6 @@ func ApplySeqContext(ctx context.Context, b *Bundle, mutators ...Mutator) { } } -// ApplySeqInScopeContext applies mutators without opening a mutator scope per mutator, -// reusing the caller's scope instead. -// -// [ApplyContext] converts the whole configuration tree between its typed and dynamic -// representations on entry and exit (see [config.Root.MarkMutatorEntry]). That cost is -// proportional to the size of the accumulated configuration, so applying N mutators this -// way is quadratic in N. For a bundle with thousands of included files that dominates -// load time, hence this variant. -// -// Only use it for mutators that modify the configuration through [config.Root.Mutate] -// (which keeps both representations in sync). A mutator that assigns to a typed field -// directly relies on the scope entry to carry that value into the dynamic tree, and -// would lose it here. -func ApplySeqInScopeContext(ctx context.Context, b *Bundle, mutators ...Mutator) { - for _, m := range mutators { - mctx := log.NewContext(ctx, log.GetLogger(ctx).With("mutator", m.Name())) - log.Debugf(mctx, "Apply") - - for _, d := range m.Apply(mctx, b) { - logdiag.LogDiag(mctx, d) - } - - if logdiag.HasError(ctx) { - break - } - } -} - type funcMutator struct { fn func(context.Context, *Bundle) } diff --git a/bundle/mutator_test.go b/bundle/mutator_test.go index 45473634119..bd98f835c76 100644 --- a/bundle/mutator_test.go +++ b/bundle/mutator_test.go @@ -5,10 +5,7 @@ import ( "testing" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/logdiag" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) type testMutator struct { @@ -52,79 +49,6 @@ func TestMutator(t *testing.T) { assert.Equal(t, 1, nested[1].applyCalled) } -// ApplySeqInScopeContext applies each mutator once, in order. -func TestApplySeqInScopeContext(t *testing.T) { - var order []string - makeMutator := func(name string) *testMutator { - return &testMutator{fn: func(ctx context.Context, b *Bundle) diag.Diagnostics { - order = append(order, name) - return nil - }} - } - first := makeMutator("first") - second := makeMutator("second") - - b := &Bundle{} - ctx := logdiag.InitContext(t.Context()) - logdiag.SetCollect(ctx, true) - ApplySeqInScopeContext(ctx, b, first, second) - - assert.Equal(t, 1, first.applyCalled) - assert.Equal(t, 1, second.applyCalled) - assert.Equal(t, []string{"first", "second"}, order) - assert.Empty(t, logdiag.FlushCollected(ctx)) -} - -// ApplySeqInScopeContext collects the diagnostics returned by each mutator and -// stops at the first one that logs an error, without applying later mutators. -func TestApplySeqInScopeContextStopsOnError(t *testing.T) { - failing := &testMutator{fn: func(ctx context.Context, b *Bundle) diag.Diagnostics { - return diag.Diagnostics{diag.Diagnostic{Severity: diag.Error, Summary: "boom"}} - }} - later := &testMutator{} - - b := &Bundle{} - ctx := logdiag.InitContext(t.Context()) - logdiag.SetCollect(ctx, true) - ApplySeqInScopeContext(ctx, b, failing, later) - - assert.Equal(t, 1, failing.applyCalled) - assert.Equal(t, 0, later.applyCalled, "mutator after an error must not run") - - diags := logdiag.FlushCollected(ctx) - require.Len(t, diags, 1) - assert.Equal(t, "boom", diags[0].Summary) -} - -// Unlike ApplySeqContext, ApplySeqInScopeContext does not open a mutator scope per -// mutator, so it must be called from within one. Changes made through Root.Mutate -// keep the typed and dynamic configuration in sync and survive without a per-mutator -// scope. This is the property ProcessRootIncludes relies on. -func TestApplySeqInScopeContextPreservesMutateChanges(t *testing.T) { - setHost := &testMutator{fn: func(ctx context.Context, b *Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "bundle", dyn.V(map[string]dyn.Value{"name": dyn.V("set-in-scope")})) - }) - require.NoError(t, err) - return nil - }} - - // Enclosing mutator that applies setHost in its own scope, mirroring how - // ProcessRootIncludes applies the per-file includes. - outer := &testMutator{fn: func(ctx context.Context, b *Bundle) diag.Diagnostics { - ApplySeqInScopeContext(ctx, b, setHost) - return nil - }} - - b := &Bundle{} - diags := Apply(t.Context(), b, outer) - require.NoError(t, diags.Error()) - - // Visible in both representations once the enclosing scope exits. - assert.Equal(t, "set-in-scope", b.Config.Bundle.Name) - assert.Equal(t, "set-in-scope", b.Config.Value().Get("bundle").Get("name").MustString()) -} - func TestSafeMutatorName(t *testing.T) { tests := []struct { name string @@ -136,11 +60,6 @@ func TestSafeMutatorName(t *testing.T) { mutator: funcMutator{fn: nil}, expected: "bundle.(funcMutator)", }, - { - name: "setDefault mutator", - mutator: SetDefaultMutator(dyn.NewPattern(dyn.Key("test")), "key", "value"), - expected: "bundle.(setDefault)", - }, { name: "funcMutator as pointer", mutator: &funcMutator{fn: nil}, diff --git a/bundle/phases/load.go b/bundle/phases/load.go index 6fa1106b3bf..b0c158d74a5 100644 --- a/bundle/phases/load.go +++ b/bundle/phases/load.go @@ -11,15 +11,22 @@ import ( // The load phase loads configuration from disk and performs // lightweight preprocessing (anything that can be done without network I/O). +// +// The configuration is loaded, merged with its includes and its target as a +// configuration tree, then converted once into the typed configuration +// (see [mutator.FinishLoading]); all later phases use the typed configuration. func Load(ctx context.Context, b *bundle.Bundle) { log.Info(ctx, "Phase: load") mutator.DefaultMutators(ctx, b) + mutator.FinishLoading(ctx, b) } func LoadDefaultTarget(ctx context.Context, b *bundle.Bundle) { log.Info(ctx, "Phase: load") + defer mutator.FinishLoading(ctx, b) + mutator.DefaultMutators(ctx, b) if logdiag.HasError(ctx) { return @@ -31,6 +38,8 @@ func LoadDefaultTarget(ctx context.Context, b *bundle.Bundle) { func LoadNamedTarget(ctx context.Context, b *bundle.Bundle, target string) { log.Info(ctx, "Phase: load") + defer mutator.FinishLoading(ctx, b) + mutator.DefaultMutators(ctx, b) if logdiag.HasError(ctx) { return diff --git a/bundle/phases/plan.go b/bundle/phases/plan.go index 4be81a102d2..5482b354061 100644 --- a/bundle/phases/plan.go +++ b/bundle/phases/plan.go @@ -13,7 +13,7 @@ import ( "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct/dresources" "github.com/databricks/cli/bundle/statemgmt" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" ) // PreDeployChecks is common set of mutators between "bundle plan" and "bundle deploy". @@ -52,30 +52,30 @@ func pipelineDeletionCascades(b *bundle.Bundle, action deployplan.Action) (bool, // checkForPreventDestroy checks if the resource has lifecycle.prevent_destroy set, but the plan calls for this resource to be recreated or destroyed. // If it does, it returns an error. func checkForPreventDestroy(b *bundle.Bundle, actions []deployplan.Action) error { - root := b.Config.Value() var errs []error for _, action := range actions { if action.ActionType != deployplan.Recreate && action.ActionType != deployplan.Delete { continue } - path, err := dyn.NewPathFromString(action.ResourceKey) + path, err := structpath.ParsePath(action.ResourceKey) if err != nil { return fmt.Errorf("failed to parse %q", action.ResourceKey) } - path = append(path, dyn.Key("lifecycle"), dyn.Key("prevent_destroy")) - - preventDestroyV, err := dyn.GetByPath(root, path) - if err != nil { + // Only resources have a lifecycle (not e.g. resources.jobs.foo.permissions). + if path.Len() != 3 { continue } - - preventDestroy, ok := preventDestroyV.AsBool() + res, ok := b.Config.Resources.GetResource(path.KeyAt(1), path.KeyAt(2)) if !ok { + continue + } + + if b.Config.IsReferenceAt(structpath.NewPath(path, "lifecycle", "prevent_destroy")) { return fmt.Errorf("internal error: prevent_destroy is not a boolean for %s", action.ResourceKey) } - if preventDestroy { + if res.GetLifecycle().HasPreventDestroy() { errs = append(errs, fmt.Errorf("%s has lifecycle.prevent_destroy set, but the plan calls for this resource to be recreated or destroyed. To avoid this error, disable lifecycle.prevent_destroy for %s", action.ResourceKey, action.ResourceKey)) } } diff --git a/bundle/phases/plan_test.go b/bundle/phases/plan_test.go index 772f7401f9b..6063cc6a0bd 100644 --- a/bundle/phases/plan_test.go +++ b/bundle/phases/plan_test.go @@ -7,9 +7,10 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct/dstate" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -77,20 +78,14 @@ func TestPipelineDeletionCascades(t *testing.T) { func TestCheckPreventDestroyForAllResources(t *testing.T) { for resourceType := range config.SupportedResources() { + // Snapshots are internal and their lifecycle is not part of the configuration (json:"-"). + if resourceType == "internal_immutable_snapshots" { + continue + } t.Run(resourceType, func(t *testing.T) { b := &bundle.Bundle{} - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources", dyn.NewValue(map[string]dyn.Value{ - resourceType: dyn.NewValue(map[string]dyn.Value{ - "test_resource": dyn.NewValue(map[string]dyn.Value{ - "lifecycle": dyn.NewValue(map[string]dyn.Value{ - "prevent_destroy": dyn.NewValue(true, nil), - }, nil), - }, nil), - }, nil), - }, nil)) - }) + err := b.Config.Set(structpath.MustParsePath("resources."+resourceType+".test_resource.lifecycle.prevent_destroy"), true) require.NoError(t, err) actions := []deployplan.Action{ @@ -111,17 +106,7 @@ func TestCheckPreventDestroyForAllResources(t *testing.T) { func TestCheckPreventDestroyForJob(t *testing.T) { b := &bundle.Bundle{} - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources", dyn.NewValue(map[string]dyn.Value{ - "jobs": dyn.NewValue(map[string]dyn.Value{ - "test_resource": dyn.NewValue(map[string]dyn.Value{ - "lifecycle": dyn.NewValue(map[string]dyn.Value{ - "prevent_destroy": dyn.NewValue(true, nil), - }, nil), - }, nil), - }, nil), - }, nil)) - }) + err := b.Config.Set(structpath.MustParsePath("resources.jobs.test_resource.lifecycle.prevent_destroy"), true) require.NoError(t, err) actions := []deployplan.Action{ @@ -140,17 +125,7 @@ func TestCheckPreventDestroyForJob(t *testing.T) { func TestCheckPreventDestroyForApp(t *testing.T) { b := &bundle.Bundle{} - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources", dyn.NewValue(map[string]dyn.Value{ - "apps": dyn.NewValue(map[string]dyn.Value{ - "test_resource": dyn.NewValue(map[string]dyn.Value{ - "lifecycle": dyn.NewValue(map[string]dyn.Value{ - "prevent_destroy": dyn.NewValue(true, nil), - }, nil), - }, nil), - }, nil), - }, nil)) - }) + err := b.Config.Set(structpath.MustParsePath("resources.apps.test_resource.lifecycle.prevent_destroy"), true) require.NoError(t, err) actions := []deployplan.Action{ @@ -167,14 +142,7 @@ func TestCheckPreventDestroyForApp(t *testing.T) { func TestCheckPreventDestroyNoError(t *testing.T) { b := &bundle.Bundle{} - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources", dyn.NewValue(map[string]dyn.Value{ - "jobs": dyn.NewValue(map[string]dyn.Value{ - "test_resource": dyn.NewValue(map[string]dyn.Value{}, nil), - }, nil), - }, nil)) - }) - require.NoError(t, err) + b.Config.Resources.Jobs = map[string]*resources.Job{"test_resource": {}} actions := []deployplan.Action{ { @@ -183,26 +151,14 @@ func TestCheckPreventDestroyNoError(t *testing.T) { }, } - err = checkForPreventDestroy(b, actions) + err := checkForPreventDestroy(b, actions) require.NoError(t, err) } func TestCheckForPreventDestroyWhenFirstHasNoPreventDestroy(t *testing.T) { b := &bundle.Bundle{} - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.Set(v, "resources", dyn.NewValue(map[string]dyn.Value{ - "jobs": dyn.NewValue(map[string]dyn.Value{ - "test_job": dyn.NewValue(map[string]dyn.Value{}, nil), - }, nil), - "apps": dyn.NewValue(map[string]dyn.Value{ - "test_app": dyn.NewValue(map[string]dyn.Value{ - "lifecycle": dyn.NewValue(map[string]dyn.Value{ - "prevent_destroy": dyn.NewValue(true, nil), - }, nil), - }, nil), - }, nil), - }, nil)) - }) + b.Config.Resources.Jobs = map[string]*resources.Job{"test_job": {}} + err := b.Config.Set(structpath.MustParsePath("resources.apps.test_app.lifecycle.prevent_destroy"), true) require.NoError(t, err) actions := []deployplan.Action{ diff --git a/bundle/phases/telemetry.go b/bundle/phases/telemetry.go index 4ce3cfc05a4..99592723c8d 100644 --- a/bundle/phases/telemetry.go +++ b/bundle/phases/telemetry.go @@ -15,8 +15,6 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/libraries" "github.com/databricks/cli/bundle/metrics" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/telemetry" "github.com/databricks/cli/libs/telemetry/protos" "github.com/databricks/databricks-sdk-go/service/jobs" @@ -235,13 +233,10 @@ func LogDeployTelemetry(ctx context.Context, b *bundle.Bundle, errMsg string) { b.Metrics.SetBoolValue(metrics.DeploymentHistoryEnabled, b.DeploymentBundle.StateDB.RecordsHistory()) resourcesCount := int64(0) - _, err := dyn.MapByPattern(b.Config.Value(), dyn.NewPattern(dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()), func(p dyn.Path, v dyn.Value) (dyn.Value, error) { + b.Config.Resources.IterateAllResources(func(string, string, config.ConfigResource) bool { resourcesCount++ - return v, nil + return true }) - if err != nil { - log.Debugf(ctx, "failed to count resources: %s", err) - } var jobsIds []string for _, job := range b.Config.Resources.Jobs { diff --git a/bundle/scripts/scripts.go b/bundle/scripts/scripts.go index cca108ec514..85fe77a7184 100644 --- a/bundle/scripts/scripts.go +++ b/bundle/scripts/scripts.go @@ -97,6 +97,13 @@ func executeHook(ctx context.Context, executor *exec.Executor, command config.Co } func getCommand(b *bundle.Bundle, hook config.ScriptHook) config.Command { + // The preinit script runs while the configuration is loading. + if b.Config.Loading() { + var command config.Command + _ = b.Config.DecodeLoading(&command, "experimental", "scripts", string(hook)) + return command + } + if b.Config.Experimental == nil || b.Config.Experimental.Scripts == nil { return "" } diff --git a/bundle/set_default.go b/bundle/set_default.go deleted file mode 100644 index 3100f67d57c..00000000000 --- a/bundle/set_default.go +++ /dev/null @@ -1,64 +0,0 @@ -package bundle - -import ( - "context" - "fmt" - - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/logdiag" -) - -type setDefault struct { - pattern dyn.Pattern - key dyn.Path - value any -} - -func SetDefaultMutator(pattern dyn.Pattern, key string, value any) Mutator { - return &setDefault{ - pattern: pattern, - key: dyn.NewPath(dyn.Key(key)), - value: value, - } -} - -func (m *setDefault) Name() string { - return fmt.Sprintf("SetDefaultMutator(%v, %v, %v)", m.pattern, m.key, m.value) -} - -func (m *setDefault) Apply(ctx context.Context, b *Bundle) diag.Diagnostics { - err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - return dyn.MapByPattern(v, m.pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { - _, err := dyn.GetByPath(v, m.key) - switch { - case dyn.IsNoSuchKeyError(err): - return dyn.SetByPath(v, m.key, dyn.V(m.value)) - default: - return v, err - } - }) - }) - if err != nil { - return diag.FromErr(err) - } - - return nil -} - -func SetDefault(ctx context.Context, b *Bundle, pattern string, value any) { - pat, err := dyn.NewPatternFromString(pattern) - if err != nil { - logdiag.LogError(ctx, fmt.Errorf("internal error: invalid pattern: %s: %w", pattern, err)) - return - } - - pat, key := pat.SplitKey() - if pat == nil || key == "" { - logdiag.LogError(ctx, fmt.Errorf("internal error: invalid pattern: %s", pattern)) - return - } - - m := SetDefaultMutator(pat, key, value) - ApplyContext(ctx, b, m) -} diff --git a/bundle/statemgmt/direct_migration.go b/bundle/statemgmt/direct_migration.go index 4fefd8504ac..1b69a57943a 100644 --- a/bundle/statemgmt/direct_migration.go +++ b/bundle/statemgmt/direct_migration.go @@ -21,7 +21,6 @@ import ( "github.com/databricks/cli/bundle/metrics" "github.com/databricks/cli/bundle/migrate" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/env" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" @@ -269,19 +268,11 @@ func convertTFStateToDirect(ctx context.Context, b *bundle.Bundle, tfState *migr // The config may use terraform-style references (${databricks_pipeline.x.id}). // BuildStateFromTF expects ${resources.*} references, so rewrite them first. - uninterpolatedRoot, err := reverseInterpolate(b.Config.Value()) + uninterpolatedConfig, err := reverseInterpolateConfig(&b.Config) if err != nil { return tempStatePath, false, nil, fmt.Errorf("failed to reverse interpolation: %w", err) } - var uninterpolatedConfig config.Root - err = uninterpolatedConfig.Mutate(func(_ dyn.Value) (dyn.Value, error) { - return uninterpolatedRoot, nil - }) - if err != nil { - return tempStatePath, false, nil, fmt.Errorf("failed to create uninterpolated config: %w", err) - } - adapters, err := dresources.InitAll(nil) if err != nil { return tempStatePath, false, nil, err @@ -296,7 +287,7 @@ func convertTFStateToDirect(ctx context.Context, b *bundle.Bundle, tfState *migr } // warnPrefix labels the conversion's warnings as coming from the background dry run. - hasWarnings, err := migrate.BuildStateFromTF(ctx, &uninterpolatedConfig, adapters, &stateDB, tfState.Attrs, tfState.IDs, warnPrefix) + hasWarnings, err := migrate.BuildStateFromTF(ctx, uninterpolatedConfig, adapters, &stateDB, tfState.Attrs, tfState.IDs, warnPrefix) if err != nil { return tempStatePath, hasWarnings, nil, err } @@ -310,5 +301,5 @@ func convertTFStateToDirect(ctx context.Context, b *bundle.Bundle, tfState *migr return tempStatePath, hasWarnings, nil, errors.New("state conversion failed") } - return tempStatePath, hasWarnings, &uninterpolatedConfig, nil + return tempStatePath, hasWarnings, uninterpolatedConfig, nil } diff --git a/bundle/statemgmt/reverse_interpolate.go b/bundle/statemgmt/reverse_interpolate.go index d133c0e0f7d..ce80662b00c 100644 --- a/bundle/statemgmt/reverse_interpolate.go +++ b/bundle/statemgmt/reverse_interpolate.go @@ -3,34 +3,103 @@ package statemgmt import ( "fmt" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/walk" "github.com/databricks/cli/bundle/deploy/terraform" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynvar" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" ) -// reverseInterpolate converts terraform-style resource references to bundle-style. +// reverseInterpolate converts terraform-style resource references in s to bundle-style. // Example: ${databricks_pipeline.my_etl.id} → ${resources.pipelines.my_etl.id} -func reverseInterpolate(root dyn.Value) (dyn.Value, error) { - return dynvar.Resolve(root, func(path dyn.Path) (dyn.Value, error) { +func reverseInterpolate(s string) (string, error) { + out, err := structvar.Resolve(map[string]structvar.Template{"": {Value: s}}, func(sp *structpath.PathNode) (structvar.View, error) { // Need at least 2 components: resource_type.resource_name - if len(path) < 2 { - return dyn.InvalidValue, dynvar.ErrSkipResolution + if sp.Len() < 2 { + return structvar.View{}, structvar.ErrSkipResolution } - resourceType := path[0].Key() + resourceType := sp.KeyAt(0) isAlreadyBundleFormat := resourceType == "resources" if isAlreadyBundleFormat { - return dyn.InvalidValue, dynvar.ErrSkipResolution + return structvar.View{}, structvar.ErrSkipResolution } bundleGroup, ok := terraform.TerraformToGroupName[resourceType] if !ok { - return dyn.InvalidValue, dynvar.ErrSkipResolution + return structvar.View{}, structvar.ErrSkipResolution } // Reconstruct path in bundle format: // databricks_pipeline.my_pipeline.id → resources.pipelines.my_pipeline.id - bundlePath := dyn.NewPath(dyn.Key("resources"), dyn.Key(bundleGroup)).Append(path[1:]...) - return dyn.V(fmt.Sprintf("${%s}", bundlePath.String())), nil + ref := fmt.Sprintf("${resources.%s.%s}", bundleGroup, sp.SkipPrefix(1)) + return structvar.NewView(&ref, nil, nil), nil }) + if err != nil { + return "", err + } + if v, ok := out[""]; ok { + rs, _ := v.AsString() + return rs, nil + } + return s, nil +} + +// reverseInterpolateConfig returns a copy of cfg in which terraform-style references +// are rewritten to bundle-style ones (see [reverseInterpolate]). cfg is not changed. +func reverseInterpolateConfig(cfg *config.Root) (*config.Root, error) { + out := &config.Root{} + if err := out.Assign(nil, cfg.View()); err != nil { + return nil, err + } + + var u reverseUpdates + walk.Strings(cfg, walk.WithReferences, func(n *walk.Node, s string) (string, bool) { + u.add(n.Path, s) + return "", false + }) + for p, ref := range cfg.References() { + u.add(func() *structpath.PathNode { return p }, ref) + } + if u.err != nil { + return nil, u.err + } + + for _, up := range u.updates { + if err := out.SetReference(up.path, up.s); err != nil { + return nil, err + } + } + return out, nil +} + +type reverseUpdate struct { + path *structpath.PathNode + s string +} + +// reverseUpdates collects the strings of a configuration that change when reverse +// interpolated, and the first error. +type reverseUpdates struct { + updates []reverseUpdate + err error +} + +// add records the reverse interpolation of s, a value at path, if it has references +// and changes. path is only called then. +func (u *reverseUpdates) add(path func() *structpath.PathNode, s string) { + if u.err != nil { + return + } + if _, ok := structvar.NewRef(s); !ok { + return + } + rs, err := reverseInterpolate(s) + if err != nil { + u.err = err + return + } + if rs != s { + u.updates = append(u.updates, reverseUpdate{path(), rs}) + } } diff --git a/bundle/statemgmt/reverse_interpolate_test.go b/bundle/statemgmt/reverse_interpolate_test.go index fe1779cd65b..8d898b35063 100644 --- a/bundle/statemgmt/reverse_interpolate_test.go +++ b/bundle/statemgmt/reverse_interpolate_test.go @@ -6,129 +6,86 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structpath" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestReverseInterpolatePreservesBConfigValue(t *testing.T) { - // This test verifies that our approach of getting b.Config.Value(), - // reverse interpolating it, and wrapping in a new config.Root - // does NOT mutate b.Config + // This test verifies that reverse interpolating a config returns a new + // config.Root and does NOT mutate the original. - b := &bundle.Bundle{ - Config: config.Root{ - Bundle: config.Bundle{ - Name: "test", - }, - }, - } + root, diags := config.LoadFromBytes("test.yml", []byte(` +bundle: + name: test +resources: + jobs: + my_job: + name: My Job + description: ${databricks_pipeline.my_pipeline.id} + max_concurrent_runs: ${databricks_job.other.id} +`)) + require.NoError(t, diags.Error()) + b := &bundle.Bundle{Config: *root} - err := b.Config.Mutate(func(_ dyn.Value) (dyn.Value, error) { - return dyn.V(map[string]dyn.Value{ - "bundle": dyn.V(map[string]dyn.Value{ - "name": dyn.V("test"), - }), - "resources": dyn.V(map[string]dyn.Value{ - "jobs": dyn.V(map[string]dyn.Value{ - "my_job": dyn.V(map[string]dyn.Value{ - "name": dyn.V("My Job"), - "depends_on": dyn.V("${databricks_pipeline.my_pipeline.id}"), - }), - }), - }), - }), nil - }) + originalJSON, err := json.Marshal(b.Config.View().AsAny()) require.NoError(t, err) - originalValue := b.Config.Value() - originalJSON, err := json.Marshal(originalValue.AsAny()) + uninterpolatedConfig, err := reverseInterpolateConfig(&b.Config) require.NoError(t, err) - interpolatedRoot := b.Config.Value() - - uninterpolatedRoot, err := reverseInterpolate(interpolatedRoot) - require.NoError(t, err) + uninterpolated := uninterpolatedConfig.View() + description, ok := uninterpolated.Lookup(structpath.MustParsePath("resources.jobs.my_job.description")).AsString() + require.True(t, ok) + assert.Equal(t, "${resources.pipelines.my_pipeline.id}", description, "should be bundle-style after reverse interpolation") - dependsOn, err := dyn.GetByPath(uninterpolatedRoot, dyn.MustPathFromString("resources.jobs.my_job.depends_on")) - require.NoError(t, err) - dependsOnStr, ok := dependsOn.AsString() + // References in fields that cannot hold a string are rewritten too. + maxRuns, ok := uninterpolated.Lookup(structpath.MustParsePath("resources.jobs.my_job.max_concurrent_runs")).AsString() require.True(t, ok) - assert.Equal(t, "${resources.pipelines.my_pipeline.id}", dependsOnStr, "should be bundle-style after reverse interpolation") + assert.Equal(t, "${resources.jobs.other.id}", maxRuns) - var uninterpolatedConfig config.Root - err = uninterpolatedConfig.Mutate(func(_ dyn.Value) (dyn.Value, error) { - return uninterpolatedRoot, nil - }) - require.NoError(t, err) + // Locations are kept. + assert.Equal(t, b.Config.GetLocationsOf(structpath.NewPath(nil, "resources", "jobs", "my_job", "description")), uninterpolatedConfig.GetLocationsOf(structpath.NewPath(nil, "resources", "jobs", "my_job", "description"))) - afterValue := b.Config.Value() - afterJSON, err := json.Marshal(afterValue.AsAny()) + afterJSON, err := json.Marshal(b.Config.View().AsAny()) require.NoError(t, err) + assert.Equal(t, string(originalJSON), string(afterJSON), "b.Config should not change") - assert.Equal(t, string(originalJSON), string(afterJSON), "b.Config.Value() should not change") - - originalDependsOn, err := dyn.GetByPath(afterValue, dyn.MustPathFromString("resources.jobs.my_job.depends_on")) - require.NoError(t, err) - originalDependsOnStr, ok := originalDependsOn.AsString() + originalDescription, ok := b.Config.View().Lookup(structpath.MustParsePath("resources.jobs.my_job.description")).AsString() require.True(t, ok) - assert.Equal(t, "${databricks_pipeline.my_pipeline.id}", originalDependsOnStr, "terraform-style reference should be preserved in b.Config") + assert.Equal(t, "${databricks_pipeline.my_pipeline.id}", originalDescription, "terraform-style reference should be preserved in b.Config") } func TestReverseInterpolate(t *testing.T) { tests := []struct { name string - input dyn.Value - expected dyn.Value + input string + expected string }{ { - name: "converts terraform-style job reference to bundle-style", - input: dyn.V(map[string]dyn.Value{ - "job_id": dyn.V("${databricks_job.my_job.id}"), - }), - expected: dyn.V(map[string]dyn.Value{ - "job_id": dyn.V("${resources.jobs.my_job.id}"), - }), + name: "converts terraform-style job reference to bundle-style", + input: "${databricks_job.my_job.id}", + expected: "${resources.jobs.my_job.id}", }, { - name: "leaves bundle-style references unchanged", - input: dyn.V(map[string]dyn.Value{ - "pipeline_id": dyn.V("${resources.pipelines.my_pipeline.id}"), - }), - expected: dyn.V(map[string]dyn.Value{ - "pipeline_id": dyn.V("${resources.pipelines.my_pipeline.id}"), - }), + name: "leaves bundle-style references unchanged", + input: "${resources.pipelines.my_pipeline.id}", + expected: "${resources.pipelines.my_pipeline.id}", }, { - name: "handles nested paths", - input: dyn.V(map[string]dyn.Value{ - "config": dyn.V(map[string]dyn.Value{ - "source": dyn.V("${databricks_pipeline.my_pipeline.url}"), - }), - }), - expected: dyn.V(map[string]dyn.Value{ - "config": dyn.V(map[string]dyn.Value{ - "source": dyn.V("${resources.pipelines.my_pipeline.url}"), - }), - }), + name: "handles nested paths", + input: "${databricks_pipeline.my_pipeline.url}", + expected: "${resources.pipelines.my_pipeline.url}", }, { - name: "skips unknown terraform resource types", - input: dyn.V(map[string]dyn.Value{ - "unknown": dyn.V("${unknown_resource.my_resource.id}"), - }), - expected: dyn.V(map[string]dyn.Value{ - "unknown": dyn.V("${unknown_resource.my_resource.id}"), - }), + name: "skips unknown terraform resource types", + input: "${unknown_resource.my_resource.id}", + expected: "${unknown_resource.my_resource.id}", }, { - name: "handles multiple references in one value", - input: dyn.V(map[string]dyn.Value{ - "combined": dyn.V("${databricks_job.job1.id}/${databricks_pipeline.pipeline1.id}"), - }), - expected: dyn.V(map[string]dyn.Value{ - "combined": dyn.V("${resources.jobs.job1.id}/${resources.pipelines.pipeline1.id}"), - }), + name: "handles multiple references in one value", + input: "${databricks_job.job1.id}/${databricks_pipeline.pipeline1.id}", + expected: "${resources.jobs.job1.id}/${resources.pipelines.pipeline1.id}", }, } @@ -136,7 +93,7 @@ func TestReverseInterpolate(t *testing.T) { t.Run(tt.name, func(t *testing.T) { result, err := reverseInterpolate(tt.input) require.NoError(t, err) - assert.Equal(t, tt.expected.AsAny(), result.AsAny()) + assert.Equal(t, tt.expected, result) }) } } diff --git a/bundle/statemgmt/state_load.go b/bundle/statemgmt/state_load.go index 3c6b7df07b4..6b92e11b532 100644 --- a/bundle/statemgmt/state_load.go +++ b/bundle/statemgmt/state_load.go @@ -3,7 +3,6 @@ package statemgmt import ( "context" "errors" - "fmt" "slices" "strings" @@ -12,7 +11,6 @@ import ( "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/statemgmt/resourcestate" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" ) type ( @@ -93,78 +91,54 @@ func applyState(ctx context.Context, b *bundle.Bundle, state ExportedResourcesMa return nil } -func ensureMap(v dyn.Value, path dyn.Path) (dyn.Value, error) { - item, _ := dyn.GetByPath(v, path) - if !item.IsValid() { - var err error - v, err = dyn.SetByPath(v, path, dyn.V(dyn.NewMapping())) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("internal error: failed to create %s: %w", path, err) +func StateToBundle(ctx context.Context, state ExportedResourcesMap, cfg *config.Root) error { + for resourceKey, attrs := range state { + // Parse resource key like "resources.jobs.foo" or "resources.jobs.foo.permissions" + parts := strings.Split(resourceKey, ".") + if len(parts) < 3 || parts[0] != "resources" { + continue // Skip invalid resource keys } - } - return v, nil -} -func StateToBundle(ctx context.Context, state ExportedResourcesMap, config *config.Root) error { - return config.Mutate(func(v dyn.Value) (dyn.Value, error) { - var err error - v, err = ensureMap(v, dyn.Path{dyn.Key("resources")}) - if err != nil { - return v, err + groupName := parts[1] + resourceName := parts[2] + + // Skip permissions for now as they are sub-resources + if len(parts) > 3 { + continue } - for resourceKey, attrs := range state { - // Parse resource key like "resources.jobs.foo" or "resources.jobs.foo.permissions" - parts := strings.Split(resourceKey, ".") - if len(parts) < 3 || parts[0] != "resources" { - continue // Skip invalid resource keys - } - - groupName := parts[1] - resourceName := parts[2] - - // Skip permissions for now as they are sub-resources - if len(parts) > 3 { - continue - } - - var err error - v, err = ensureMap(v, dyn.Path{dyn.Key("resources"), dyn.Key(groupName)}) - if err != nil { - return v, err - } - - path := dyn.Path{dyn.Key("resources"), dyn.Key(groupName), dyn.Key(resourceName)} - resource, err := dyn.GetByPath(v, path) - if !resource.IsValid() { - m := dyn.NewMapping() - m.SetLoc("id", nil, dyn.V(attrs.ID)) - m.SetLoc("modified_status", nil, dyn.V(resources.ModifiedStatusDeleted)) - v, err = dyn.SetByPath(v, path, dyn.V(m)) - if err != nil { - return dyn.InvalidValue, err - } - } else if err != nil { - return dyn.InvalidValue, err - } else { - v, err = dyn.SetByPath(v, dyn.Path{dyn.Key("resources"), dyn.Key(groupName), dyn.Key(resourceName), dyn.Key("id")}, dyn.V(attrs.ID)) - if err != nil { - return dyn.InvalidValue, err - } - } + if !hasID(groupName) { + continue } - return dyn.MapByPattern(v, dyn.Pattern{dyn.Key("resources"), dyn.AnyKey(), dyn.AnyKey()}, func(p dyn.Path, inner dyn.Value) (dyn.Value, error) { - idPath := dyn.Path{dyn.Key("id")} - statusPath := dyn.Path{dyn.Key("modified_status")} - id, _ := dyn.GetByPath(inner, idPath) - status, _ := dyn.GetByPath(inner, statusPath) - if !id.IsValid() && !status.IsValid() { - return dyn.SetByPath(inner, statusPath, dyn.V(resources.ModifiedStatusCreated)) - } - return inner, nil - }) + id, status, created, ok := cfg.Resources.GetOrCreateState(groupName, resourceName) + if !ok { + continue + } + if created { + *status = resources.ModifiedStatusDeleted + } + *id = attrs.ID + } + + cfg.Resources.IterateAllResourceStates(func(typ, _ string, id *string, status *resources.ModifiedStatus) bool { + if hasID(typ) && *id == "" && *status == "" { + *status = resources.ModifiedStatusCreated + } + return true }) + return nil +} + +// hasID reports whether resources of group can hold the deployed id and status. Groups +// that are not part of the configuration, and internal snapshots, cannot. +func hasID(group string) bool { + typ, ok := config.ResourcesTypes[group] + if !ok { + return false + } + _, ok = typ.FieldByName("ID") + return ok } func validateLoadedState(state ExportedResourcesMap, modes []LoadMode) error { diff --git a/bundle/tests/suggest_target_test.go b/bundle/tests/suggest_target_test.go index ca678a61c0a..18f7a5ae9dc 100644 --- a/bundle/tests/suggest_target_test.go +++ b/bundle/tests/suggest_target_test.go @@ -3,16 +3,15 @@ package config_tests import ( "testing" - "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/internal/bundletest" "github.com/stretchr/testify/require" ) func TestSuggestTargetIfWrongPassed(t *testing.T) { b := load(t, "target_overrides/workspace") - ctx := t.Context() - diags := bundle.Apply(ctx, b, mutator.SelectTarget("incorrect")) + diags := bundletest.ApplyLoading(t, b, mutator.SelectTarget("incorrect")) err := diags.Error() require.Error(t, err) require.Contains(t, err.Error(), "Available targets:") diff --git a/bundle/trampoline/python_dbr_warning.go b/bundle/trampoline/python_dbr_warning.go index a6f45e12311..b75188034e9 100644 --- a/bundle/trampoline/python_dbr_warning.go +++ b/bundle/trampoline/python_dbr_warning.go @@ -9,8 +9,8 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/libraries" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/log" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go" "golang.org/x/mod/semver" ) @@ -81,18 +81,18 @@ func hasIncompatibleWheelTasks(ctx context.Context, b *bundle.Bundle) diag.Diagn // So we can get the version from the cluster definition. // It's defined in a form of resources.clusters..id if strings.HasPrefix(task.ExistingClusterId, "${") { - p, ok := dynvar.PureReferenceToPath(task.ExistingClusterId) - if !ok || len(p) < 3 { + p, ok := structvar.PureReferenceToPath(task.ExistingClusterId) + if !ok || p.Len() < 3 { log.Warnf(ctx, "unable to parse cluster key from %s", task.ExistingClusterId) continue } - if p[0].Key() != "resources" || p[1].Key() != "clusters" { + if p.KeyAt(0) != "resources" || p.KeyAt(1) != "clusters" { log.Warnf(ctx, "incorrect variable reference for cluster id %s", task.ExistingClusterId) continue } - clusterKey := p[2].Key() + clusterKey := p.KeyAt(2) cluster, ok := b.Config.Resources.Clusters[clusterKey] if !ok { log.Warnf(ctx, "unable to find cluster with key %s", clusterKey) diff --git a/cmd/air/convert_to_dabs.go b/cmd/air/convert_to_dabs.go index 02b29a4d02f..910835a036a 100644 --- a/cmd/air/convert_to_dabs.go +++ b/cmd/air/convert_to_dabs.go @@ -13,9 +13,7 @@ import ( "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/spf13/cobra" ) @@ -112,10 +110,10 @@ does not contact the workspace.`, // convertToDabs builds the DABs bundle value and the loose launch artifacts for a // run config. It reads only what the run path's buildArtifacts reads, so the // mapping is unit-testable in isolation. Returns the bundle root as a -// map[string]dyn.Value (ready for yamlsaver) and the loose artifacts (command.sh + +// structyaml.Map (ready for structyaml.Save) and the loose artifacts (command.sh + // env/secret/param sidecars) to write under generated_artifacts/. It does not touch the // code_source; the emitted `tgz` artifact packages it at deploy. -func convertToDabs(ctx context.Context, cfg *runConfig, configPath, bundleDir string) (map[string]dyn.Value, []uploadItem, error) { +func convertToDabs(ctx context.Context, cfg *runConfig, configPath, bundleDir string) (structyaml.Map, []uploadItem, error) { // idempotency_token is intentionally not mapped: it dedups a single runs/submit // call, which has no analogue for a persistent, repeatedly-runnable bundle job. // @@ -259,14 +257,6 @@ func bundleCodeSourcePath(ctx context.Context, cfg *runConfig, configPath, bundl return localBundlePath(filepath.ToSlash(rel)), nil } -// nv builds a dyn.Value at "position" n: yamlsaver orders a map's keys by their -// Location line, so assigning ascending n values fixes the emitted key order. -// It routes through dyn.V so nested Go maps/slices are converted recursively, -// then stamps the ordering location. -func nv(v any, n int) dyn.Value { - return dyn.V(v).WithLocations([]diag.Location{{Line: n}}) -} - // localBundlePath renders a bundle-relative path with a leading "./" so bundle // deploy classifies it as a local artifact to upload (see IsLibraryLocal). It is // built from path.Join (forward slashes) so the emitted YAML is identical across @@ -275,10 +265,10 @@ func localBundlePath(p string) string { return "./" + p } -// buildBundleValue assembles the bundle root as an ordered map[string]dyn.Value. +// buildBundleValue assembles the bundle root as an ordered structyaml.Map. // codeSourcePath is the "./"-prefixed code_source dir relative to the bundle (empty // when the config has no code_source); command.sh is a bundle-local artifact. -func buildBundleValue(ctx context.Context, cfg *runConfig, configPath, codeSourcePath string, art *codeArtifact) map[string]dyn.Value { +func buildBundleValue(ctx context.Context, cfg *runConfig, configPath, codeSourcePath string, art *codeArtifact) structyaml.Map { name := cfg.ExperimentName // ai_runtime_task: experiment + one deployment (command_path + compute) + @@ -288,67 +278,57 @@ func buildBundleValue(ctx context.Context, cfg *runConfig, configPath, codeSourc // command_path is "./"-prefixed so bundle deploy treats it as LOCAL and uploads // it: libraries.IsLibraryLocal classifies a bare, extensionless path as a PyPI // package name, which would deploy a path the backend can't resolve. - compute := map[string]dyn.Value{ - "accelerator_type": nv(cfg.Compute.AcceleratorType, 1), - "accelerator_count": nv(cfg.Compute.NumAccelerators, 2), - } + compute := structyaml.M( + "accelerator_type", cfg.Compute.AcceleratorType, + "accelerator_count", cfg.Compute.NumAccelerators, + ) if cfg.Compute.PoolID != nil { - compute["provisioned_capacity_id"] = nv(*cfg.Compute.PoolID, 3) - } - deployment := map[string]dyn.Value{ - "command_path": nv(localBundlePath(path.Join(generatedArtifactsDir, commandScriptName)), 1), - "compute": nv(compute, 2), + compute.Add("provisioned_capacity_id", *cfg.Compute.PoolID) } + deployment := structyaml.M( + "command_path", localBundlePath(path.Join(generatedArtifactsDir, commandScriptName)), + "compute", compute, + ) - aiRuntimeTask := map[string]dyn.Value{ - "experiment": nv(name, 1), - "deployments": nv([]dyn.Value{dyn.V(deployment)}, 2), - } - line := 3 + aiRuntimeTask := structyaml.M( + "experiment", name, + "deployments", []any{deployment}, + ) if codeSourcePath != "" { // Points at the `tgz` artifact's built tarball; deploy uploads it and rewrites // this to the uploaded workspace path. - aiRuntimeTask["code_source_path"] = nv(codeSourcePath, line) - line++ + aiRuntimeTask.Add("code_source_path", codeSourcePath) } if cfg.MLflowRunName != nil { - aiRuntimeTask["mlflow_run"] = nv(*cfg.MLflowRunName, line) - line++ + aiRuntimeTask.Add("mlflow_run", *cfg.MLflowRunName) } if cfg.MLflowExperimentDirectory != nil { - aiRuntimeTask["mlflow_experiment_directory"] = nv(*cfg.MLflowExperimentDirectory, line) - line++ + aiRuntimeTask.Add("mlflow_experiment_directory", *cfg.MLflowExperimentDirectory) } if cfg.MLflowArtifactLocation != nil { - aiRuntimeTask["mlflow_artifact_location"] = nv(*cfg.MLflowArtifactLocation, line) - line++ + aiRuntimeTask.Add("mlflow_artifact_location", *cfg.MLflowArtifactLocation) } if cfg.Compute.PriorityClass != nil { - aiRuntimeTask["priority_class"] = nv(*cfg.Compute.PriorityClass, line) - line++ + aiRuntimeTask.Add("priority_class", *cfg.Compute.PriorityClass) } if cfg.Environment != nil && cfg.Environment.UnityCatalogImage != "" { - aiRuntimeTask["unity_catalog_image_path"] = nv(cfg.Environment.UnityCatalogImage, line) + aiRuntimeTask.Add("unity_catalog_image_path", cfg.Environment.UnityCatalogImage) } // Task wrapper: task_key + framework fields (retries/timeout) + env key + // the ai_runtime_task. Framework fields live here per the schema, not inside // ai_runtime_task. - task := map[string]dyn.Value{ - "task_key": nv(name, 1), - "environment_key": nv(aiRuntimeEnvironmentKey, 2), - } - taskLine := 3 maxRetries := cfg.maxRetries() - task["max_retries"] = nv(maxRetries, taskLine) - taskLine++ - task["retry_on_timeout"] = nv(maxRetries > 0, taskLine) - taskLine++ + task := structyaml.M( + "task_key", name, + "environment_key", aiRuntimeEnvironmentKey, + "max_retries", maxRetries, + "retry_on_timeout", maxRetries > 0, + ) if cfg.TimeoutMinutes != nil { - task["timeout_seconds"] = nv(cfg.timeoutSeconds(), taskLine) - taskLine++ + task.Add("timeout_seconds", cfg.timeoutSeconds()) } - task["ai_runtime_task"] = nv(aiRuntimeTask, taskLine) + task.Add("ai_runtime_task", aiRuntimeTask) // environments[]: version + the dependency set. The runtime installs deps from this // spec directly, so the full dependency set (whether authored inline or in a @@ -358,65 +338,57 @@ func buildBundleValue(ctx context.Context, cfg *runConfig, configPath, codeSourc // default channel) so a config without an explicit version still pins the version // the workload would have run with — not an empty spec. envVersion, deps := bundleEnvironmentDeps(ctx, cfg) - envSpec := map[string]dyn.Value{} + var envSpec structyaml.Map if strings.HasPrefix(envVersion, databricksAIPrefix) { - envSpec["base_environment"] = nv("workspace-base-environments/"+envVersion, 1) + envSpec.Add("base_environment", "workspace-base-environments/"+envVersion) } else { - envSpec["environment_version"] = nv(envVersion, 1) + envSpec.Add("environment_version", envVersion) } if len(deps) > 0 { - depVals := make([]dyn.Value, len(deps)) - for i, d := range deps { - depVals[i] = dyn.V(d) - } - envSpec["dependencies"] = nv(depVals, 2) - } - environment := map[string]dyn.Value{ - "environment_key": nv(aiRuntimeEnvironmentKey, 1), - "spec": nv(envSpec, 2), + envSpec.Add("dependencies", deps) } + environment := structyaml.M( + "environment_key", aiRuntimeEnvironmentKey, + "spec", envSpec, + ) - job := map[string]dyn.Value{ - "name": nv(name, 1), - "tasks": nv([]dyn.Value{dyn.V(task)}, 2), - "environments": nv([]dyn.Value{dyn.V(environment)}, 3), - } + job := structyaml.M( + "name", name, + "tasks", []any{task}, + "environments", []any{environment}, + ) // usage_policy_id is an already-resolved budget policy id, so it maps directly // to the job's budget_policy_id. (usage_policy_name needs server-side resolution // and is rejected in convertToDabs.) if cfg.UsagePolicyID != nil { - job["budget_policy_id"] = nv(*cfg.UsagePolicyID, 4) + job.Add("budget_policy_id", *cfg.UsagePolicyID) } - if perms := buildPermissionsValue(cfg.Permissions); perms.Kind() != dyn.KindInvalid { - job["permissions"] = nv(perms.MustSequence(), 5) + if perms := buildPermissionsValue(cfg.Permissions); perms != nil { + job.Add("permissions", perms) } - rootValue := map[string]dyn.Value{ - "bundle": nv(map[string]dyn.Value{ - "name": nv(name, 1), - }, 1), + rootValue := structyaml.M( + "bundle", structyaml.M("name", name), // sync.paths replaces the default of syncing the whole bundle root. The code // directory is omitted deliberately: the `tgz` artifact packages it, so syncing // it too would upload the tree twice. - "sync": nv(map[string]dyn.Value{ - "paths": nv([]dyn.Value{nv(generatedArtifactsDir, 1)}, 1), - }, 2), - "targets": nv(map[string]dyn.Value{ - dabsTargetName: nv(map[string]dyn.Value{ - "mode": nv("development", 1), - "default": nv(true, 2), - }, 1), - }, 4), - "resources": nv(map[string]dyn.Value{ - "jobs": nv(map[string]dyn.Value{ - bundleResourceKey(name): nv(job, 1), - }, 1), - }, 5), - } + "sync", structyaml.M("paths", []any{generatedArtifactsDir}), + ) // The `tgz` artifact that packages the code_source (nil only when there is none). if art != nil { - rootValue["artifacts"] = nv(buildArtifactsValue(art), 3) - } + rootValue.Add("artifacts", buildArtifactsValue(art)) + } + rootValue.Add("targets", structyaml.M( + dabsTargetName, structyaml.M( + "mode", "development", + "default", true, + ), + )) + rootValue.Add("resources", structyaml.M( + "jobs", structyaml.M( + bundleResourceKey(name), job, + ), + )) return rootValue } @@ -424,34 +396,23 @@ func buildBundleValue(ctx context.Context, cfg *runConfig, configPath, codeSourc // single `tgz` artifact whose `path` is the code-source root, carrying the git ref // and/or include subpaths, and whose `files` output is the tarball code_source_path // points at. -func buildArtifactsValue(art *codeArtifact) map[string]dyn.Value { - a := map[string]dyn.Value{ - "type": nv("tgz", 1), - "path": nv(art.path, 2), - } - fileLine := 3 +func buildArtifactsValue(art *codeArtifact) structyaml.Map { + a := structyaml.M( + "type", "tgz", + "path", art.path, + ) if art.gitCommit != nil || art.gitBranch != nil { - g := map[string]dyn.Value{} // commit wins over branch, matching the artifact builder. if art.gitCommit != nil { - g["commit"] = nv(*art.gitCommit, 1) + a.Add("git", structyaml.M("commit", *art.gitCommit)) } else { - g["branch"] = nv(*art.gitBranch, 1) + a.Add("git", structyaml.M("branch", *art.gitBranch)) } - a["git"] = nv(g, fileLine) - fileLine++ } // include is always set: the basename (whole dir) or basename-prefixed subpaths. - vals := make([]dyn.Value, len(art.include)) - for i, p := range art.include { - vals[i] = dyn.V(p) - } - a["include"] = nv(vals, fileLine) - fileLine++ - a["files"] = nv([]dyn.Value{ - dyn.V(map[string]dyn.Value{"source": nv(art.tgzPath, 1)}), - }, fileLine) - return map[string]dyn.Value{codeSourceArtifactKey: nv(a, 1)} + a.Add("include", art.include) + a.Add("files", []any{structyaml.M("source", art.tgzPath)}) + return structyaml.M(codeSourceArtifactKey, a) } // bundleEnvironmentDeps resolves the runtime version and the inline dependency @@ -476,7 +437,7 @@ func bundleResourceKey(name string) string { } // quoteJobKey rewrites the emitted job resource key as a quoted YAML key when the -// name would otherwise load as a non-string scalar. yamlsaver emits map keys +// name would otherwise load as a non-string scalar. structyaml emits map keys // unquoted, and the bundle loader rejects a key that types as something other than // a string ("12345" -> !!int, "true" -> !!bool) with "invalid key tag". Only the // job key needs this: every other key convert emits is a fixed schema field name. @@ -516,25 +477,22 @@ func yamlKeyNeedsQuoting(key string) bool { } // buildPermissionsValue maps run-config permissions to DABs job permissions -// (level → principal). Returns an invalid value when there are none. -func buildPermissionsValue(perms []permission) dyn.Value { - if len(perms) == 0 { - return dyn.InvalidValue - } - out := make([]dyn.Value, 0, len(perms)) +// (level → principal). Returns nil when there are none. +func buildPermissionsValue(perms []permission) []any { + var out []any for _, p := range perms { - m := map[string]dyn.Value{"level": nv(p.Level, 1)} + m := structyaml.M("level", p.Level) switch { case p.UserName != nil: - m["user_name"] = nv(*p.UserName, 2) + m.Add("user_name", *p.UserName) case p.GroupName != nil: - m["group_name"] = nv(*p.GroupName, 2) + m.Add("group_name", *p.GroupName) case p.ServicePrincipalName != nil: - m["service_principal_name"] = nv(*p.ServicePrincipalName, 2) + m.Add("service_principal_name", *p.ServicePrincipalName) } - out = append(out, dyn.V(m)) + out = append(out, m) } - return dyn.V(out) + return out } // writeBundle writes the bundle into dir: databricks.yml plus the loose launch @@ -580,9 +538,9 @@ func writeBundle(ctx context.Context, cfg *runConfig, configPath, dir string, fo return nil, fmt.Errorf("databricks.yml already exists in %s; pass --force to overwrite or remove it", dir) } } - // SaveAsYAML's force arg is passed true unconditionally: the collision check + // Save's force arg is passed true unconditionally: the collision check // above already decided whether overwriting is allowed. - if err := yamlsaver.NewSaver().SaveAsYAML(root, bundlePath, true); err != nil { + if err := structyaml.Save(bundlePath, root, true, nil); err != nil { return nil, err } if err := quoteJobKey(bundlePath, bundleResourceKey(cfg.ExperimentName)); err != nil { diff --git a/cmd/air/convert_to_dabs_test.go b/cmd/air/convert_to_dabs_test.go index cbffa6b224c..ac07cac17d5 100644 --- a/cmd/air/convert_to_dabs_test.go +++ b/cmd/air/convert_to_dabs_test.go @@ -3,10 +3,11 @@ package aircmd import ( "os" "path/filepath" + "strconv" "strings" "testing" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v3" @@ -86,17 +87,71 @@ func TestConvertToDabsCommandShape(t *testing.T) { assert.Error(t, cmd.Args(cmd, []string{"a", "b"})) } +// val is a value of the emitted bundle root. +type val struct{ v any } + +func (v val) MustString() string { return v.v.(string) } +func (v val) MustInt() int64 { return int64(v.v.(int)) } +func (v val) MustBool() bool { return v.v.(bool) } + +func (v val) MustSequence() []val { + if ss, ok := v.v.([]string); ok { + out := make([]val, len(ss)) + for i, s := range ss { + out[i] = val{s} + } + return out + } + var out []val + for _, e := range v.v.([]any) { + out = append(out, val{e}) + } + return out +} + +func (v val) Get(key string) val { + for _, p := range v.v.(structyaml.Map) { + if p.Key == key { + return val{p.Value} + } + } + return val{} +} + +// lookup reads a dotted path, with optional [index] suffixes, out of the emitted bundle root. +func lookup(root structyaml.Map, path string) (val, bool) { + v := val{root} + for part := range strings.SplitSeq(path, ".") { + key, idx, hasIdx := strings.Cut(strings.TrimSuffix(part, "]"), "[") + if _, ok := v.v.(structyaml.Map); !ok { + return val{}, false + } + if v = v.Get(key); v.v == nil { + return val{}, false + } + if hasIdx { + i, err := strconv.Atoi(idx) + seq, ok := v.v.([]any) + if err != nil || !ok || i >= len(seq) { + return val{}, false + } + v = val{seq[i]} + } + } + return v, true +} + // get is a small helper: read a dotted path out of the emitted bundle root. -func get(t *testing.T, root map[string]dyn.Value, path string) dyn.Value { +func get(t *testing.T, root structyaml.Map, path string) val { t.Helper() - v, err := dyn.GetByPath(dyn.V(root), dyn.MustPathFromString(path)) - require.NoError(t, err, "path %q should exist", path) + v, ok := lookup(root, path) + require.True(t, ok, "path %q should exist", path) return v } -func has(root map[string]dyn.Value, path string) bool { - _, err := dyn.GetByPath(dyn.V(root), dyn.MustPathFromString(path)) - return err == nil +func has(root structyaml.Map, path string) bool { + _, ok := lookup(root, path) + return ok } // A full config maps onto a schema-shaped bundle: bundle name, job/task keys, the @@ -543,11 +598,7 @@ func TestConvertToDabsSafeJobKey(t *testing.T) { root, _, err := convertToDabs(t.Context(), loaded, path, dir) require.NoError(t, err) // The key is the raw name; name/experiment keep the same value. - jobs, err := dyn.GetByPath(dyn.V(root), dyn.MustPathFromString("resources.jobs")) - require.NoError(t, err) - job := jobs.Get(name) - require.True(t, job.IsValid(), "job must be keyed by %q", name) - assert.Equal(t, name, job.Get("name").MustString()) + assert.Equal(t, name, get(t, root, "resources.jobs."+name+".name").MustString()) // The emitted YAML must load back with the key still a string. _, err = writeBundle(t.Context(), loaded, path, dir, true) diff --git a/cmd/apps/import.go b/cmd/apps/import.go index f552c3182ce..c639867a788 100644 --- a/cmd/apps/import.go +++ b/cmd/apps/import.go @@ -25,12 +25,9 @@ import ( "github.com/databricks/cli/libs/apps/prompt" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/env" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/service/apps" @@ -456,22 +453,13 @@ func generateAppBundle(ctx context.Context, w *databricks.WorkspaceClient, app * } } - // Create the bundle configuration with explicit line numbers to control ordering // Use the app name for the bundle name bundleName := textutil.NormalizeString(app.Name) - bundleConfig := map[string]dyn.Value{ - "bundle": dyn.NewValue(map[string]dyn.Value{ - "name": dyn.NewValue(bundleName, []diag.Location{{Line: 1}}), - }, []diag.Location{{Line: 1}}), - "workspace": dyn.NewValue(map[string]dyn.Value{ - "host": dyn.NewValue(w.Config.Host, []diag.Location{{Line: 2}}), - }, []diag.Location{{Line: 10}}), - "resources": dyn.NewValue(map[string]dyn.Value{ - "apps": dyn.V(map[string]dyn.Value{ - appKey: v, - }), - }, []diag.Location{{Line: 20}}), - } + bundleConfig := structyaml.M( + "bundle", structyaml.M("name", bundleName), + "workspace", structyaml.M("host", w.Config.Host), + "resources", structyaml.M("apps", structyaml.M(appKey, v)), + ) // Download the app source files err = downloader.FlushToDisk(ctx, false) @@ -481,8 +469,7 @@ func generateAppBundle(ctx context.Context, w *databricks.WorkspaceClient, app * // Save databricks.yml databricksYml := filepath.Join(".", "databricks.yml") - saver := yamlsaver.NewSaver() - err = saver.SaveAsYAML(bundleConfig, databricksYml, false) + err = structyaml.Save(databricksYml, bundleConfig, false, nil) if err != nil { return "", fmt.Errorf("failed to save databricks.yml: %w", err) } @@ -545,7 +532,7 @@ func addBlankLinesBetweenTopLevelKeys(filename string) error { } // inlineAppConfigFile reads app.yml or app.yaml, inlines it into the app value, and returns the filename -func inlineAppConfigFile(appValue *dyn.Value) (string, error) { +func inlineAppConfigFile(appValue *structyaml.Map) (string, error) { // Check for app.yml first, then app.yaml var appConfigFile string var appConfigData []byte @@ -574,62 +561,33 @@ func inlineAppConfigFile(appValue *dyn.Value) (string, error) { return "", fmt.Errorf("failed to parse %s: %w", appConfigFile, err) } - // Get the current app value as a map - appMap, ok := appValue.AsMap() - if !ok { - return "", errors.New("app value is not a map") - } - - // Build the new app map with the config section - newPairs := make([]dyn.Pair, 0, len(appMap.Pairs())+2) - - // Copy existing pairs - newPairs = append(newPairs, appMap.Pairs()...) - // Create config section - configMap := make(map[string]dyn.Value) + var config structyaml.Map // Add command if present if cmd, ok := appConfig["command"]; ok { - cmdValue, err := convert.FromTyped(cmd, dyn.NilValue) - if err != nil { - return "", fmt.Errorf("failed to convert command: %w", err) - } - configMap["command"] = cmdValue + config.Add("command", structyaml.Value(cmd)) } // Add env if present if env, ok := appConfig["env"]; ok { - envValue, err := convert.FromTyped(env, dyn.NilValue) - if err != nil { - return "", fmt.Errorf("failed to convert env: %w", err) - } - configMap["env"] = envValue + config.Add("env", structyaml.Value(env)) } + // The config section and the top-level resources go before the existing fields. + var added structyaml.Map + // Add the config section if we have any items - if len(configMap) > 0 { - newPairs = append(newPairs, dyn.Pair{ - Key: dyn.V("config"), - Value: dyn.V(configMap), - }) + if len(config) > 0 { + added.Add("config", config) } // Add resources at top level if present if resources, ok := appConfig["resources"]; ok { - resourcesValue, err := convert.FromTyped(resources, dyn.NilValue) - if err != nil { - return "", fmt.Errorf("failed to convert resources: %w", err) - } - newPairs = append(newPairs, dyn.Pair{ - Key: dyn.V("resources"), - Value: resourcesValue, - }) + added.Add("resources", structyaml.Value(resources)) } - // Create the new app value with the config section - newMapping := dyn.NewMappingFromPairs(newPairs) - *appValue = dyn.NewValue(newMapping, appValue.Locations()) + *appValue = append(added, *appValue...) return appConfigFile, nil } diff --git a/cmd/apps/import_test.go b/cmd/apps/import_test.go index b50ec937991..30e9c8d239f 100644 --- a/cmd/apps/import_test.go +++ b/cmd/apps/import_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -16,17 +16,15 @@ func TestInlineAppConfigFile(t *testing.T) { tests := []struct { name string setupFiles map[string]string - inputValue dyn.Value + inputValue structyaml.Map expectedFile string expectedConfig map[string]any expectError bool }{ { - name: "no app config file", - setupFiles: map[string]string{}, - inputValue: dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - }), + name: "no app config file", + setupFiles: map[string]string{}, + inputValue: structyaml.M("name", "test-app"), expectedFile: "", expectedConfig: map[string]any{"name": "test-app"}, }, @@ -38,9 +36,7 @@ env: - name: FOO value: bar`, }, - inputValue: dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - }), + inputValue: structyaml.M("name", "test-app"), expectedFile: "app.yml", expectedConfig: nil, // Will check manually }, @@ -53,9 +49,7 @@ env: - name: TEST value: value`, }, - inputValue: dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - }), + inputValue: structyaml.M("name", "test-app"), expectedFile: "app.yml", expectedConfig: nil, // Will check manually }, @@ -68,9 +62,7 @@ resources: serving_endpoint: name: my-endpoint`, }, - inputValue: dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - }), + inputValue: structyaml.M("name", "test-app"), expectedFile: "app.yml", expectedConfig: nil, // Will check manually }, @@ -80,9 +72,7 @@ resources: "app.yml": `resources: - name: SERVING_ENDPOINT`, }, - inputValue: dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - }), + inputValue: structyaml.M("name", "test-app"), expectedFile: "app.yml", expectedConfig: nil, // Will check manually }, @@ -92,9 +82,7 @@ resources: "app.yml": `command: ["python", "app.py"] env: []`, }, - inputValue: dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - }), + inputValue: structyaml.M("name", "test-app"), expectedFile: "app.yml", expectedConfig: nil, // Will check manually }, @@ -125,20 +113,17 @@ env: []`, // Verify the structure if expectedConfig is set if tt.expectedConfig != nil { - appMap := appValue.MustMap() result := make(map[string]any) - for _, pair := range appMap.Pairs() { - key := pair.Key.MustString() - result[key] = pair.Value.AsAny() + for _, pair := range appValue { + result[pair.Key] = pair.Value } assert.Equal(t, tt.expectedConfig, result) } else if tt.expectedFile != "" { // Just verify that config or resources were added - appMap := appValue.MustMap() var hasConfigOrResources bool - for _, pair := range appMap.Pairs() { - key := pair.Key.MustString() + for _, pair := range appValue { + key := pair.Key if key == "config" || key == "resources" { hasConfigOrResources = true break @@ -158,24 +143,12 @@ func TestInlineAppConfigFileErrors(t *testing.T) { err := os.WriteFile("app.yml", []byte("invalid: yaml: content:\n - broken"), 0o644) require.NoError(t, err) - appValue := dyn.V(map[string]dyn.Value{"name": dyn.V("test")}) + appValue := structyaml.M("name", "test") _, err = inlineAppConfigFile(&appValue) assert.Error(t, err) assert.Contains(t, err.Error(), "failed to parse") }) - t.Run("app value not a map", func(t *testing.T) { - t.Chdir(t.TempDir()) - - err := os.WriteFile("app.yml", []byte("command: [\"test\"]"), 0o644) - require.NoError(t, err) - - appValue := dyn.V("not a map") - _, err = inlineAppConfigFile(&appValue) - assert.Error(t, err) - assert.Contains(t, err.Error(), "app value is not a map") - }) - t.Run("unreadable app.yml", func(t *testing.T) { t.Chdir(t.TempDir()) @@ -202,7 +175,7 @@ func TestInlineAppConfigFileErrors(t *testing.T) { require.NoError(t, err) } - appValue := dyn.V(map[string]dyn.Value{"name": dyn.V("test")}) + appValue := structyaml.M("name", "test") _, err = inlineAppConfigFile(&appValue) assert.Error(t, err) assert.Contains(t, err.Error(), "failed to read") @@ -223,21 +196,19 @@ resources: name: test`), 0o644) require.NoError(t, err) - appValue := dyn.V(map[string]dyn.Value{ - "name": dyn.V("test-app"), - "description": dyn.V("existing description"), - }) + appValue := structyaml.M( + "name", "test-app", + "description", "existing description", + ) filename, err := inlineAppConfigFile(&appValue) require.NoError(t, err) assert.Equal(t, "app.yml", filename) // Verify structure - appMap := appValue.MustMap() result := make(map[string]any) - for _, pair := range appMap.Pairs() { - key := pair.Key.MustString() - result[key] = pair.Value.AsAny() + for _, pair := range appValue { + result[pair.Key] = pair.Value } // Should have original fields plus config and resources diff --git a/cmd/bundle/generate/alert.go b/cmd/bundle/generate/alert.go index cb78b24de1e..2e4c5150370 100644 --- a/cmd/bundle/generate/alert.go +++ b/cmd/bundle/generate/alert.go @@ -12,9 +12,8 @@ import ( "github.com/databricks/cli/bundle/generate" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/service/sql" @@ -141,13 +140,7 @@ After generation, you can deploy this alert to other targets using: return err } - result := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "alerts": dyn.V(map[string]dyn.Value{ - alertKey: v, - }), - }), - } + result := structyaml.M("resources", structyaml.M("alerts", structyaml.M(alertKey, v))) // Create config directory if needed if err := os.MkdirAll(configDir, 0o755); err != nil { @@ -156,11 +149,11 @@ After generation, you can deploy this alert to other targets using: // Save configuration file configPath := filepath.Join(configDir, alertKey+".alert.yml") - saver := yamlsaver.NewSaverWithStyle(map[string]yaml.Style{ + styles := map[string]yaml.Style{ "display_name": yaml.DoubleQuotedStyle, - }) + } - err = saver.SaveAsYAML(result, configPath, force) + err = structyaml.Save(configPath, result, force, styles) if err != nil { return err } diff --git a/cmd/bundle/generate/app.go b/cmd/bundle/generate/app.go index bec005408cd..0705dc3f0c2 100644 --- a/cmd/bundle/generate/app.go +++ b/cmd/bundle/generate/app.go @@ -8,9 +8,8 @@ import ( "github.com/databricks/cli/cmd/bundle/deployment" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go/service/apps" "github.com/spf13/cobra" @@ -107,13 +106,7 @@ per target environment.`, appKey = textutil.NormalizeString(app.Name) } - result := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "apps": dyn.V(map[string]dyn.Value{ - appKey: v, - }), - }), - } + result := structyaml.M("resources", structyaml.M("apps", structyaml.M(appKey, v))) err = downloader.FlushToDisk(ctx, force) if err != nil { @@ -122,8 +115,7 @@ per target environment.`, filename := filepath.Join(configDir, appKey+".app.yml") - saver := yamlsaver.NewSaver() - err = saver.SaveAsYAML(result, filename, force) + err = structyaml.Save(filename, result, force, nil) if err != nil { return err } diff --git a/cmd/bundle/generate/dashboard.go b/cmd/bundle/generate/dashboard.go index 9c4be81b391..d5058e60a5e 100644 --- a/cmd/bundle/generate/dashboard.go +++ b/cmd/bundle/generate/dashboard.go @@ -22,9 +22,8 @@ import ( "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/apierr" @@ -215,13 +214,7 @@ func (d *dashboard) saveConfiguration(ctx context.Context, b *bundle.Bundle, das return err } - result := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "dashboards": dyn.V(map[string]dyn.Value{ - key: v, - }), - }), - } + result := structyaml.M("resources", structyaml.M("dashboards", structyaml.M(key, v))) // Make sure the output directory exists. if err := os.MkdirAll(d.resourceDir, 0o755); err != nil { @@ -230,9 +223,9 @@ func (d *dashboard) saveConfiguration(ctx context.Context, b *bundle.Bundle, das // Save the configuration to the resource directory. resourcePath := filepath.Join(d.resourceDir, key+".dashboard.yml") - saver := yamlsaver.NewSaverWithStyle(map[string]yaml.Style{ + styles := map[string]yaml.Style{ "display_name": yaml.DoubleQuotedStyle, - }) + } // Attempt to make the path relative to the bundle root. rel, err := filepath.Rel(b.BundleRootPath, resourcePath) @@ -241,7 +234,7 @@ func (d *dashboard) saveConfiguration(ctx context.Context, b *bundle.Bundle, das } cmdio.LogString(ctx, "Writing configuration to "+filepath.ToSlash(rel)) - err = saver.SaveAsYAML(result, resourcePath, d.force) + err = structyaml.Save(resourcePath, result, d.force, styles) if err != nil { return err } diff --git a/cmd/bundle/generate/genie_space.go b/cmd/bundle/generate/genie_space.go index 7c520abc320..2107d62ffd8 100644 --- a/cmd/bundle/generate/genie_space.go +++ b/cmd/bundle/generate/genie_space.go @@ -18,9 +18,8 @@ import ( "github.com/databricks/cli/cmd/bundle/deployment" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/service/dashboards" @@ -130,13 +129,7 @@ func (g *genieSpace) saveConfiguration(ctx context.Context, b *bundle.Bundle, ge return err } - result := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "genie_spaces": dyn.V(map[string]dyn.Value{ - key: v, - }), - }), - } + result := structyaml.M("resources", structyaml.M("genie_spaces", structyaml.M(key, v))) // Make sure the output directory exists. if err := os.MkdirAll(g.resourceDir, 0o755); err != nil { @@ -145,9 +138,9 @@ func (g *genieSpace) saveConfiguration(ctx context.Context, b *bundle.Bundle, ge // Save the configuration to the resource directory. resourcePath := filepath.Join(g.resourceDir, key+".genie_space.yml") - saver := yamlsaver.NewSaverWithStyle(map[string]yaml.Style{ + styles := map[string]yaml.Style{ "title": yaml.DoubleQuotedStyle, - }) + } // Attempt to make the path relative to the bundle root. rel, err := filepath.Rel(b.BundleRootPath, resourcePath) @@ -156,7 +149,7 @@ func (g *genieSpace) saveConfiguration(ctx context.Context, b *bundle.Bundle, ge } cmdio.LogString(ctx, "Writing configuration to "+filepath.ToSlash(rel)) - err = saver.SaveAsYAML(result, resourcePath, g.force) + err = structyaml.Save(resourcePath, result, g.force, styles) if err != nil { return err } diff --git a/cmd/bundle/generate/job.go b/cmd/bundle/generate/job.go index db6839ade98..07066c0ec8a 100644 --- a/cmd/bundle/generate/job.go +++ b/cmd/bundle/generate/job.go @@ -12,9 +12,8 @@ import ( "github.com/databricks/cli/cmd/bundle/deployment" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/spf13/cobra" @@ -115,13 +114,7 @@ After generation, you can deploy this job to other targets using: jobKey = textutil.NormalizeString(job.Settings.Name) } - result := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "jobs": dyn.V(map[string]dyn.Value{ - jobKey: v, - }), - }), - } + result := structyaml.M("resources", structyaml.M("jobs", structyaml.M(jobKey, v))) err = downloader.FlushToDisk(ctx, force) if err != nil { @@ -141,13 +134,13 @@ After generation, you can deploy this job to other targets using: return fmt.Errorf("failed to rename file %s. DABs uses the resource type as a sub-extension for generated content, please rename it to %s, err: %w", oldFilename, filename, err) } - saver := yamlsaver.NewSaverWithStyle(map[string]yaml.Style{ + styles := map[string]yaml.Style{ // Including all JobSettings and nested fields which are map[string]string type "spark_conf": yaml.DoubleQuotedStyle, "custom_tags": yaml.DoubleQuotedStyle, "tags": yaml.DoubleQuotedStyle, - }) - err = saver.SaveAsYAML(result, filename, force) + } + err = structyaml.Save(filename, result, force, styles) if err != nil { return err } diff --git a/cmd/bundle/generate/pipeline.go b/cmd/bundle/generate/pipeline.go index cdde5984d40..d6de9ab9cc4 100644 --- a/cmd/bundle/generate/pipeline.go +++ b/cmd/bundle/generate/pipeline.go @@ -11,9 +11,8 @@ import ( "github.com/databricks/cli/cmd/bundle/deployment" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/cli/libs/textutil" "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/spf13/cobra" @@ -112,13 +111,7 @@ like catalogs, schemas, and compute configurations per target.`, pipelineKey = textutil.NormalizeString(pipeline.Name) } - result := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "pipelines": dyn.V(map[string]dyn.Value{ - pipelineKey: v, - }), - }), - } + result := structyaml.M("resources", structyaml.M("pipelines", structyaml.M(pipelineKey, v))) err = downloader.FlushToDisk(ctx, force) if err != nil { @@ -136,15 +129,13 @@ like catalogs, schemas, and compute configurations per target.`, return fmt.Errorf("failed to rename file %s. DABs uses the resource type as a sub-extension for generated content, please rename it to %s, err: %w", oldFilename, filename, err) } - saver := yamlsaver.NewSaverWithStyle( - // Including all CreatePipeline and nested fields which are map[string]string type - map[string]yaml.Style{ - "spark_conf": yaml.DoubleQuotedStyle, - "custom_tags": yaml.DoubleQuotedStyle, - "configuration": yaml.DoubleQuotedStyle, - }, - ) - err = saver.SaveAsYAML(result, filename, force) + // Including all CreatePipeline and nested fields which are map[string]string type + styles := map[string]yaml.Style{ + "spark_conf": yaml.DoubleQuotedStyle, + "custom_tags": yaml.DoubleQuotedStyle, + "configuration": yaml.DoubleQuotedStyle, + } + err = structyaml.Save(filename, result, force, styles) if err != nil { return err } diff --git a/cmd/bundle/run.go b/cmd/bundle/run.go index 17199a6cabf..4ca32fd92a1 100644 --- a/cmd/bundle/run.go +++ b/cmd/bundle/run.go @@ -21,10 +21,10 @@ import ( "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/execv" "github.com/databricks/cli/libs/flags" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structpath" "github.com/spf13/cobra" ) @@ -189,7 +189,7 @@ Example usage: // runner sees concrete values (e.g. an app's env vars referencing another // resource, or its source_code_path pointing at the immutable snapshot's // full_path, which lives only in the deployed state). - target := dyn.NewPath(dyn.Key("resources"), dyn.Key(ref.Description.PluralName), dyn.Key(ref.Key)) + target := structpath.NewPath(nil, "resources", ref.Description.PluralName, ref.Key) if err := b.DeploymentBundle.ResolveConfigAgainstState(&b.Config, target); err != nil { return err } diff --git a/cmd/bundle/utils/process.go b/cmd/bundle/utils/process.go index c16f8280a08..f58fb2c956a 100644 --- a/cmd/bundle/utils/process.go +++ b/cmd/bundle/utils/process.go @@ -28,10 +28,10 @@ import ( "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dms" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/logdiag" "github.com/databricks/cli/libs/retry" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/sync" "github.com/databricks/cli/libs/telemetry" "github.com/databricks/cli/libs/telemetry/protos" @@ -769,8 +769,7 @@ func isNewerVersion(stateVersion, currentVersion string) bool { func rejectDefinitions(ctx context.Context, b *bundle.Bundle) { if b.Config.Definitions != nil { - v := dyn.GetValue(b.Config.Value(), "definitions") - loc := v.Locations() + loc := b.Config.GetLocationsOf(structpath.NewPath(nil, "definitions")) filename := "input yaml" if len(loc) > 0 { filename = filepath.ToSlash(loc[0].File) diff --git a/cmd/bundle/validate.go b/cmd/bundle/validate.go index d28f4ab8d3b..7b0e1cfcaf7 100644 --- a/cmd/bundle/validate.go +++ b/cmd/bundle/validate.go @@ -10,7 +10,6 @@ import ( "github.com/databricks/cli/bundle/render" "github.com/databricks/cli/cmd/bundle/utils" "github.com/databricks/cli/cmd/root" - "github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/cli/libs/flags" "github.com/databricks/cli/libs/logdiag" "github.com/spf13/cobra" @@ -24,11 +23,7 @@ func renderJsonOutput(cmd *cobra.Command, b *bundle.Bundle) error { if err != nil { return err } - converted, err := convert.FromTyped(redactedRoot, b.Config.Value()) - if err != nil { - return err - } - buf, err := json.MarshalIndent(converted.AsAny(), "", " ") + buf, err := json.MarshalIndent(redactedRoot.View().AsAny(), "", " ") if err != nil { return err } diff --git a/cmd/pipelines/generate.go b/cmd/pipelines/generate.go index c865213c36a..cf362bd89f1 100644 --- a/cmd/pipelines/generate.go +++ b/cmd/pipelines/generate.go @@ -10,12 +10,9 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/libs/cmdio" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/yamlloader" - "github.com/databricks/cli/libs/dyn/yamlsaver" "github.com/databricks/cli/libs/logdiag" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/databricks/databricks-sdk-go/service/pipelines" "github.com/spf13/cobra" ) @@ -83,8 +80,7 @@ Use --existing-pipeline-dir to generate pipeline configuration from spark-pipeli return fmt.Errorf("failed to construct .pipeline.yml: %w", err) } - saver := yamlsaver.NewSaver() - err = saver.SaveAsYAML(resourcesMap, outputFile, force) + err = structyaml.Save(outputFile, resourcesMap, force, nil) if err != nil { return err } @@ -208,31 +204,21 @@ func parseSparkPipelineYAML(ctx context.Context, filePath string) (*sdpPipeline, } defer file.Close() - dv, err := yamlloader.LoadYAML(filePath, file) + out := sdpPipeline{} + _, diags, err := structvar.DecodeYAML(filePath, file, &out) if err != nil { return nil, fmt.Errorf("failed to load %s: %w", filePath, err) } - out := sdpPipeline{} - normalized, diags := convert.Normalize(&out, dv) - if diags.HasError() { - return nil, fmt.Errorf("failed to parse %s: %w", filePath, diags.Error()) - } - for _, diag := range diags { logdiag.LogDiag(ctx, diag) } - err = convert.ToTyped(&out, normalized) - if err != nil { - return nil, fmt.Errorf("failed to parse %s: %w", filePath, diags.Error()) - } - return &out, nil } // convertToResources converts a spark-pipeline.yml spec to DABs YAML format with "resources" property -func convertToResources(spec *sdpPipeline, resourceName, srcFolder string) (map[string]dyn.Value, error) { +func convertToResources(spec *sdpPipeline, resourceName, srcFolder string) (structyaml.Map, error) { // YAML paths are relative to directory containing YAML file, in this case: // DABs YAML is in "./resources/.pipeline.yml" // SDP YAML is in ".//spark-pipeline.yml" @@ -256,55 +242,29 @@ func convertToResources(spec *sdpPipeline, resourceName, srcFolder string) (map[ }, } - environmentDyn, err := convert.FromTyped(environment, dyn.NilValue) - if err != nil { - return nil, fmt.Errorf("failed to convert environments into dyn.Value: %w", err) - } - - librariesDyn, err := convertLibraries(relativePath, spec.Libraries) - if err != nil { - return nil, fmt.Errorf("failed to convert libraries into dyn.Value: %w", err) - } - - // maps are unordered, and saver is sorting keys by diag.Location - // this is helper function to monotonically assign locations as keys are created - var line int - nextLocation := func() []diag.Location { - line += 1 - return []diag.Location{{Line: line}} - } - - pipelineMap := map[string]dyn.Value{ - "name": dyn.V(spec.Name).WithLocations(nextLocation()), - "catalog": dyn.V(catalog).WithLocations(nextLocation()), - "schema": dyn.V(schema).WithLocations(nextLocation()), - "root_path": dyn.V(relativePath).WithLocations(nextLocation()), - "serverless": dyn.V(true).WithLocations(nextLocation()), - "libraries": librariesDyn.WithLocations(nextLocation()), - } + // Keys are written in the order they are added. + pipelineMap := structyaml.M( + "name", spec.Name, + "catalog", catalog, + "schema", schema, + "root_path", relativePath, + "serverless", true, + "libraries", convertLibraries(relativePath, spec.Libraries), + ) // configuration is optional field, skip if empty if spec.Configuration != nil { - dv, err := convert.FromTyped(spec.Configuration, dyn.NilValue) - if err != nil { - return nil, fmt.Errorf("failed to convert configuration into dyn.Value: %w", err) - } - - // NB: golang maps are unordered, and currently we don't preserve the order - pipelineMap["configuration"] = dv.WithLocations(nextLocation()) + pipelineMap.Add("configuration", structyaml.Value(spec.Configuration)) } - pipelineMap["environment"] = environmentDyn.WithLocations(nextLocation()) + pipelineMap.Add("environment", structyaml.Value(environment)) - resourcesMap := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "pipelines": dyn.V(map[string]dyn.Value{ - resourceName: dyn.V(pipelineMap), - }), - }), - } + resourcesMap := structyaml.M("resources", structyaml.M("pipelines", structyaml.M(resourceName, pipelineMap))) - _, diag := convert.Normalize(&config.Root{}, dyn.V(resourcesMap)) + _, diag, err := structvar.DecodeYAMLNode("", structyaml.Node(resourcesMap, nil), &config.Root{}, nil) + if err != nil { + return nil, err + } if len(diag) > 0 { return nil, fmt.Errorf("generated output doesn't match expected schema: %v", diag) } @@ -316,7 +276,7 @@ func convertToResources(spec *sdpPipeline, resourceName, srcFolder string) (map[ // // relativePath contains a path to append into SDP libraries path to make // them relative to generated DABs YAML -func convertLibraries(relativePath string, specLibraries []sdpPipelineLibrary) (dyn.Value, error) { +func convertLibraries(relativePath string, specLibraries []sdpPipelineLibrary) any { var libraries []pipelines.PipelineLibrary for _, lib := range specLibraries { @@ -329,16 +289,11 @@ func convertLibraries(relativePath string, specLibraries []sdpPipelineLibrary) ( } } - librariesDyn, err := convert.FromTyped(libraries, dyn.NilValue) - if err != nil { - return dyn.InvalidValue, fmt.Errorf("failed to convert libraries into dyn.Value: %w", err) - } - - // FromTyped returns NilValue if libraries is an empty array - if librariesDyn.Kind() == dyn.KindNil { - // we always want to leave empty array as a placeholder in generated YAML - return dyn.V([]dyn.Value{}), nil + // Value returns nil if libraries is an empty array + if v := structyaml.Value(libraries); v != nil { + return v } - return librariesDyn, nil + // we always want to leave empty array as a placeholder in generated YAML + return []any{} } diff --git a/cmd/pipelines/generate_test.go b/cmd/pipelines/generate_test.go index 7135031ccbb..c795d463164 100644 --- a/cmd/pipelines/generate_test.go +++ b/cmd/pipelines/generate_test.go @@ -5,9 +5,7 @@ import ( "path/filepath" "testing" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynassert" + "github.com/databricks/cli/libs/structs/structyaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -174,38 +172,27 @@ func TestConvertToResources(t *testing.T) { }, } - expected := map[string]dyn.Value{ - "resources": dyn.V(map[string]dyn.Value{ - "pipelines": dyn.V(map[string]dyn.Value{ - "my_pipeline": dyn.V(map[string]dyn.Value{ - "name": dyn.V("My Pipeline").WithLocations([]diag.Location{{Line: 1}}), - "catalog": dyn.V("${var.catalog}").WithLocations([]diag.Location{{Line: 2}}), - "schema": dyn.V("${var.schema}").WithLocations([]diag.Location{{Line: 3}}), - "root_path": dyn.V("../src/my_pipeline").WithLocations([]diag.Location{{Line: 4}}), - "serverless": dyn.V(true).WithLocations([]diag.Location{{Line: 5}}), - "libraries": dyn.V([]dyn.Value{ - dyn.V(map[string]dyn.Value{ - "glob": dyn.V(map[string]dyn.Value{ - "include": dyn.V("../src/my_pipeline/transformations/**"), - }), - }), - }).WithLocations([]diag.Location{{Line: 6}}), - "configuration": dyn.V(map[string]dyn.Value{ - "key0": dyn.V("value0"), - "key1": dyn.V("value1"), - }).WithLocations([]diag.Location{{Line: 7}}), - "environment": dyn.V(map[string]dyn.Value{ - "dependencies": dyn.V([]dyn.Value{ - dyn.V("--editable ${workspace.file_path}"), - }), - }).WithLocations([]diag.Location{{Line: 8}}), - }), - }), - }), - } + expected := structyaml.M( + "resources", structyaml.M( + "pipelines", structyaml.M( + "my_pipeline", structyaml.M( + "name", "My Pipeline", + "catalog", "${var.catalog}", + "schema", "${var.schema}", + "root_path", "../src/my_pipeline", + "serverless", true, + "libraries", []any{ + structyaml.M("glob", structyaml.M("include", "../src/my_pipeline/transformations/**")), + }, + "configuration", structyaml.M("key0", "value0", "key1", "value1"), + "environment", structyaml.M("dependencies", []any{"--editable ${workspace.file_path}"}), + ), + ), + ), + ) actual, err := convertToResources(&input, "my_pipeline", "src/my_pipeline") require.NoError(t, err) - dynassert.Equal(t, dyn.V(expected), dyn.V(actual)) + assert.Equal(t, expected, actual) } diff --git a/integration/assumptions/dashboard_assumptions_test.go b/integration/assumptions/dashboard_assumptions_test.go index 111482a6f81..e7d6bd44a2e 100644 --- a/integration/assumptions/dashboard_assumptions_test.go +++ b/integration/assumptions/dashboard_assumptions_test.go @@ -6,9 +6,8 @@ import ( "github.com/databricks/cli/integration/internal/acc" "github.com/databricks/cli/internal/testutil" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/merge" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/service/dashboards" "github.com/databricks/databricks-sdk-go/service/workspace" @@ -86,27 +85,21 @@ func TestDashboardAssumptions_WorkspaceImport(t *testing.T) { }) require.NoError(t, err) - // Convert the dashboard object to a [dyn.Value] to make comparison easier. - previous, err := convert.FromTyped(previousDashboard, dyn.NilValue) - require.NoError(t, err) - current, err := convert.FromTyped(currentDashboard, dyn.NilValue) - require.NoError(t, err) - // Collect updated and deleted paths. var updatedFieldPaths []string var deletedFieldPaths []string - _, err = merge.Override(previous, current, merge.OverrideVisitor{ - VisitDelete: func(basePath dyn.Path, left dyn.Value) error { - deletedFieldPaths = append(deletedFieldPaths, basePath.String()) + _, err = structvar.PlanOverride(structvar.NewView(previousDashboard, nil, nil), structvar.NewView(currentDashboard, nil, nil), structvar.OverrideVisitor{ + VisitDelete: func(path *structpath.PathNode, left structvar.View) error { + deletedFieldPaths = append(deletedFieldPaths, path.String()) return nil }, - VisitInsert: func(basePath dyn.Path, right dyn.Value) (dyn.Value, error) { + VisitInsert: func(path *structpath.PathNode, right structvar.View) error { assert.Fail(t, "unexpected insert operation") - return right, nil + return nil }, - VisitUpdate: func(basePath dyn.Path, left, right dyn.Value) (dyn.Value, error) { - updatedFieldPaths = append(updatedFieldPaths, basePath.String()) - return right, nil + VisitUpdate: func(path *structpath.PathNode, left, right structvar.View) error { + updatedFieldPaths = append(updatedFieldPaths, path.String()) + return nil }, }) require.NoError(t, err) diff --git a/libs/dyn/merge/elements_by_key.go b/libs/dyn/merge/elements_by_key.go index 6bf71d41ae5..7d7e35c8eb5 100644 --- a/libs/dyn/merge/elements_by_key.go +++ b/libs/dyn/merge/elements_by_key.go @@ -98,10 +98,6 @@ func ElementsByKey(key string, keyFunc func(dyn.Value) string) dyn.MapFunc { return elementsByKey{key, keyFunc, false}.Map } -func ElementsBySortedKey(key string, keyFunc func(dyn.Value) string) dyn.MapFunc { - return elementsByKey{key, keyFunc, true}.Map -} - func ElementsByKeyWithOverride(key string, keyFunc func(dyn.Value) string) dyn.MapFunc { return elementsByKey{key, keyFunc, false}.MapWithOverride } diff --git a/libs/dyn/visit_get.go b/libs/dyn/visit_get.go index d23c1f97427..101c38aff64 100644 --- a/libs/dyn/visit_get.go +++ b/libs/dyn/visit_get.go @@ -10,12 +10,6 @@ func Get(v Value, path string) (Value, error) { return GetByPath(v, p) } -// GetValue returns the value without the error (InvalidValue indicates that error did happen) -func GetValue(v Value, path string) Value { - val, _ := Get(v, path) - return val -} - // GetByPath returns the value inside the specified value at the specified path. // If the path doesn't exist, it returns InvalidValue and an error. func GetByPath(v Value, p Path) (Value, error) { diff --git a/libs/dyn/yamlsaver/order.go b/libs/dyn/yamlsaver/order.go deleted file mode 100644 index 439be8474a6..00000000000 --- a/libs/dyn/yamlsaver/order.go +++ /dev/null @@ -1,33 +0,0 @@ -package yamlsaver - -import "slices" - -// This struct is used to generate indexes for ordering of map keys. -// The ordering defined based on any predefined Order in `Order` field -// or running Order based on `index` -type Order struct { - index int - order []string -} - -func NewOrder(o []string) *Order { - return &Order{index: 0, order: o} -} - -// Returns an integer which represents the order of map key in resulting -// The lower the index, the earlier in the list the key is. -// If the order is not predefined, it uses running order and any subsequential call to -// order.Get returns an increasing index. -func (o *Order) Get(key string) int { - index := slices.Index(o.order, key) - // If the key is found in predefined order list - // We return a negative index which put the value at the top of the order compared to other - // not predefined keys. The earlier value in predefined list, the lower negative index value - if index != -1 { - return index - len(o.order) - } - - // Otherwise we just increase the order index - o.index += 1 - return o.index -} diff --git a/libs/dyn/yamlsaver/order_test.go b/libs/dyn/yamlsaver/order_test.go deleted file mode 100644 index ed2877f6c8c..00000000000 --- a/libs/dyn/yamlsaver/order_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package yamlsaver - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestOrderReturnsIncreasingIndex(t *testing.T) { - o := NewOrder([]string{}) - assert.Equal(t, 1, o.Get("a")) - assert.Equal(t, 2, o.Get("b")) - assert.Equal(t, 3, o.Get("c")) -} - -func TestOrderReturnsNegativeIndexForPredefinedKeys(t *testing.T) { - o := NewOrder([]string{"a", "b", "c"}) - assert.Equal(t, -3, o.Get("a")) - assert.Equal(t, -2, o.Get("b")) - assert.Equal(t, -1, o.Get("c")) - assert.Equal(t, 1, o.Get("d")) - assert.Equal(t, 2, o.Get("e")) - assert.Equal(t, 3, o.Get("f")) -} diff --git a/libs/dyn/yamlsaver/saver.go b/libs/dyn/yamlsaver/saver.go deleted file mode 100644 index 4c302e26f0d..00000000000 --- a/libs/dyn/yamlsaver/saver.go +++ /dev/null @@ -1,172 +0,0 @@ -package yamlsaver - -import ( - "cmp" - "fmt" - "io" - "os" - "path/filepath" - "slices" - "strconv" - - "github.com/databricks/cli/libs/dyn" - "go.yaml.in/yaml/v3" -) - -type saver struct { - nodesWithStyle map[string]yaml.Style -} - -func NewSaver() *saver { - return &saver{} -} - -func NewSaverWithStyle(nodesWithStyle map[string]yaml.Style) *saver { - return &saver{ - nodesWithStyle: nodesWithStyle, - } -} - -func (s *saver) SaveAsYAML(data any, filename string, force bool) error { - err := os.MkdirAll(filepath.Dir(filename), 0o755) - if err != nil { - return err - } - - // check that file exists - info, err := os.Stat(filename) - if err == nil { - if info.IsDir() { - return fmt.Errorf("%s is a directory", filename) - } - if !force { - return fmt.Errorf("%s already exists. Use --force to overwrite", filename) - } - } - - file, err := os.Create(filename) - if err != nil { - return err - } - defer file.Close() - - err = s.encode(data, file) - if err != nil { - return err - } - return nil -} - -func (s *saver) encode(data any, w io.Writer) error { - yamlNode, err := s.toYamlNode(dyn.V(data)) - if err != nil { - return err - } - enc := yaml.NewEncoder(w) - enc.SetIndent(2) - return enc.Encode(yamlNode) -} - -func (s *saver) toYamlNode(v dyn.Value) (*yaml.Node, error) { - return s.toYamlNodeWithStyle(v, yaml.Style(0)) -} - -func (s *saver) toYamlNodeWithStyle(v dyn.Value, style yaml.Style) (*yaml.Node, error) { - switch v.Kind() { - case dyn.KindMap: - m, _ := v.AsMap() - - // We're using location lines to define the order of keys in YAML. - // The location is set when we convert API response struct to config.Value representation - // See convert.convertMap for details - pairs := m.Pairs() - slices.SortStableFunc(pairs, func(a, b dyn.Pair) int { - return cmp.Compare(a.Value.Location().Line, b.Value.Location().Line) - }) - - var content []*yaml.Node - for _, pair := range pairs { - pk := pair.Key - pv := pair.Value - node := yaml.Node{Kind: yaml.ScalarNode, Value: pk.MustString(), Style: style} - var nestedNodeStyle yaml.Style - if customStyle, ok := s.hasStyle(pk.MustString()); ok { - nestedNodeStyle = customStyle - } else { - nestedNodeStyle = style - } - c, err := s.toYamlNodeWithStyle(pv, nestedNodeStyle) - if err != nil { - return nil, err - } - content = append(content, &node) - content = append(content, c) - } - - return &yaml.Node{Kind: yaml.MappingNode, Content: content, Style: style}, nil - case dyn.KindSequence: - seq, _ := v.AsSequence() - var content []*yaml.Node - for _, item := range seq { - node, err := s.toYamlNodeWithStyle(item, style) - if err != nil { - return nil, err - } - content = append(content, node) - } - return &yaml.Node{Kind: yaml.SequenceNode, Content: content, Style: style}, nil - case dyn.KindNil: - return &yaml.Node{Kind: yaml.ScalarNode, Value: "null", Style: style}, nil - case dyn.KindString: - // If the string is a scalar value (bool, int, float and etc.), we want to quote it. - if isScalarValueInString(v) { - return &yaml.Node{Kind: yaml.ScalarNode, Value: v.MustString(), Style: yaml.DoubleQuotedStyle}, nil - } - return &yaml.Node{Kind: yaml.ScalarNode, Value: v.MustString(), Style: style}, nil - case dyn.KindBool: - return &yaml.Node{Kind: yaml.ScalarNode, Value: strconv.FormatBool(v.MustBool()), Style: style}, nil - case dyn.KindInt: - return &yaml.Node{Kind: yaml.ScalarNode, Value: strconv.FormatInt(v.MustInt(), 10), Style: style}, nil - case dyn.KindFloat: - return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprint(v.MustFloat()), Style: style}, nil - case dyn.KindTime: - return &yaml.Node{Kind: yaml.ScalarNode, Value: v.MustTime().String(), Style: style}, nil - default: - // Panic because we only want to deal with known types. - panic(fmt.Sprintf("invalid kind: %d", v.Kind())) - } -} - -func (s *saver) hasStyle(key string) (yaml.Style, bool) { - style, ok := s.nodesWithStyle[key] - return style, ok -} - -func isScalarValueInString(v dyn.Value) bool { - if v.Kind() != dyn.KindString { - return false - } - - // Parse value of the string and check if it's a scalar value. - // If it's a scalar value, we want to quote it. - switch v.MustString() { - case "true", "false": - return true - case "": - return true - default: - _, err := parseNumber(v.MustString()) - return err == nil - } -} - -func parseNumber(s string) (any, error) { - if i, err := strconv.ParseInt(s, 0, 64); err == nil { - return i, nil - } - - if f, err := strconv.ParseFloat(s, 64); err == nil { - return f, nil - } - return nil, fmt.Errorf("invalid number: %s", s) -} diff --git a/libs/dyn/yamlsaver/saver_test.go b/libs/dyn/yamlsaver/saver_test.go deleted file mode 100644 index a4f50e0b8fb..00000000000 --- a/libs/dyn/yamlsaver/saver_test.go +++ /dev/null @@ -1,275 +0,0 @@ -package yamlsaver - -import ( - "testing" - - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.yaml.in/yaml/v3" -) - -func TestMarshalNilValue(t *testing.T) { - s := NewSaver() - nilValue := dyn.NilValue - v, err := s.toYamlNode(nilValue) - assert.NoError(t, err) - assert.Equal(t, "null", v.Value) -} - -func TestMarshalIntValue(t *testing.T) { - s := NewSaver() - intValue := dyn.V(1) - v, err := s.toYamlNode(intValue) - assert.NoError(t, err) - assert.Equal(t, "1", v.Value) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalFloatValue(t *testing.T) { - s := NewSaver() - floatValue := dyn.V(1.0) - v, err := s.toYamlNode(floatValue) - assert.NoError(t, err) - assert.Equal(t, "1", v.Value) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalBoolValue(t *testing.T) { - s := NewSaver() - boolValue := dyn.V(true) - v, err := s.toYamlNode(boolValue) - assert.NoError(t, err) - assert.Equal(t, "true", v.Value) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalTimeValue(t *testing.T) { - tm, err := dyn.NewTime("1970-01-01") - require.NoError(t, err) - - s := NewSaver() - timeValue := dyn.V(tm) - v, err := s.toYamlNode(timeValue) - assert.NoError(t, err) - assert.Equal(t, "1970-01-01", v.Value) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalSequenceValue(t *testing.T) { - s := NewSaver() - sequenceValue := dyn.NewValue( - []dyn.Value{ - dyn.NewValue("value1", []diag.Location{{File: "file", Line: 1, Column: 2}}), - dyn.NewValue("value2", []diag.Location{{File: "file", Line: 2, Column: 2}}), - }, - []diag.Location{{File: "file", Line: 1, Column: 2}}, - ) - v, err := s.toYamlNode(sequenceValue) - assert.NoError(t, err) - assert.Equal(t, yaml.SequenceNode, v.Kind) - assert.Equal(t, "value1", v.Content[0].Value) - assert.Equal(t, "value2", v.Content[1].Value) -} - -func TestMarshalStringValue(t *testing.T) { - s := NewSaver() - stringValue := dyn.V("value") - v, err := s.toYamlNode(stringValue) - assert.NoError(t, err) - assert.Equal(t, "value", v.Value) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalMapValue(t *testing.T) { - s := NewSaver() - mapValue := dyn.NewValue( - map[string]dyn.Value{ - "key3": dyn.NewValue("value3", []diag.Location{{File: "file", Line: 3, Column: 2}}), - "key2": dyn.NewValue("value2", []diag.Location{{File: "file", Line: 2, Column: 2}}), - "key1": dyn.NewValue("value1", []diag.Location{{File: "file", Line: 1, Column: 2}}), - }, - []diag.Location{{File: "file", Line: 1, Column: 2}}, - ) - - v, err := s.toYamlNode(mapValue) - assert.NoError(t, err) - assert.Equal(t, yaml.MappingNode, v.Kind) - assert.Equal(t, "key1", v.Content[0].Value) - assert.Equal(t, "value1", v.Content[1].Value) - - assert.Equal(t, "key2", v.Content[2].Value) - assert.Equal(t, "value2", v.Content[3].Value) - - assert.Equal(t, "key3", v.Content[4].Value) - assert.Equal(t, "value3", v.Content[5].Value) -} - -func TestMarshalNestedValues(t *testing.T) { - s := NewSaver() - mapValue := dyn.NewValue( - map[string]dyn.Value{ - "key1": dyn.NewValue( - map[string]dyn.Value{ - "key2": dyn.NewValue("value", []diag.Location{{File: "file", Line: 1, Column: 2}}), - }, - []diag.Location{{File: "file", Line: 1, Column: 2}}, - ), - }, - []diag.Location{{File: "file", Line: 1, Column: 2}}, - ) - v, err := s.toYamlNode(mapValue) - assert.NoError(t, err) - assert.Equal(t, yaml.MappingNode, v.Kind) - assert.Equal(t, "key1", v.Content[0].Value) - assert.Equal(t, yaml.MappingNode, v.Content[1].Kind) - assert.Equal(t, "key2", v.Content[1].Content[0].Value) - assert.Equal(t, "value", v.Content[1].Content[1].Value) -} - -func TestMarshalHexadecimalValueIsQuoted(t *testing.T) { - s := NewSaver() - hexValue := dyn.V(0x123) - v, err := s.toYamlNode(hexValue) - assert.NoError(t, err) - assert.Equal(t, "291", v.Value) - assert.Equal(t, yaml.Style(0), v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) - - stringValue := dyn.V("0x123") - v, err = s.toYamlNode(stringValue) - assert.NoError(t, err) - assert.Equal(t, "0x123", v.Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalBinaryValueIsQuoted(t *testing.T) { - s := NewSaver() - binaryValue := dyn.V(0b101) - v, err := s.toYamlNode(binaryValue) - assert.NoError(t, err) - assert.Equal(t, "5", v.Value) - assert.Equal(t, yaml.Style(0), v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) - - stringValue := dyn.V("0b101") - v, err = s.toYamlNode(stringValue) - assert.NoError(t, err) - assert.Equal(t, "0b101", v.Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalOctalValueIsQuoted(t *testing.T) { - s := NewSaver() - octalValue := dyn.V(0o123) - v, err := s.toYamlNode(octalValue) - assert.NoError(t, err) - assert.Equal(t, "83", v.Value) - assert.Equal(t, yaml.Style(0), v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) - - stringValue := dyn.V("0123") - v, err = s.toYamlNode(stringValue) - assert.NoError(t, err) - assert.Equal(t, "0123", v.Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalFloatValueIsQuoted(t *testing.T) { - s := NewSaver() - floatValue := dyn.V(1.0) - v, err := s.toYamlNode(floatValue) - assert.NoError(t, err) - assert.Equal(t, "1", v.Value) - assert.Equal(t, yaml.Style(0), v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) - - stringValue := dyn.V("1.0") - v, err = s.toYamlNode(stringValue) - assert.NoError(t, err) - assert.Equal(t, "1.0", v.Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestMarshalBoolValueIsQuoted(t *testing.T) { - s := NewSaver() - boolValue := dyn.V(true) - v, err := s.toYamlNode(boolValue) - assert.NoError(t, err) - assert.Equal(t, "true", v.Value) - assert.Equal(t, yaml.Style(0), v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) - - stringValue := dyn.V("true") - v, err = s.toYamlNode(stringValue) - assert.NoError(t, err) - assert.Equal(t, "true", v.Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Style) - assert.Equal(t, yaml.ScalarNode, v.Kind) -} - -func TestCustomStylingWithNestedMap(t *testing.T) { - s := NewSaverWithStyle(map[string]yaml.Style{ - "styled": yaml.DoubleQuotedStyle, - }) - - styledMap := dyn.NewValue( - map[string]dyn.Value{ - "key1": dyn.NewValue("value1", []diag.Location{{File: "file", Line: 1, Column: 2}}), - "key2": dyn.NewValue("value2", []diag.Location{{File: "file", Line: 2, Column: 2}}), - }, - []diag.Location{{File: "file", Line: -2, Column: 2}}, - ) - - unstyledMap := dyn.NewValue( - map[string]dyn.Value{ - "key3": dyn.NewValue("value3", []diag.Location{{File: "file", Line: 1, Column: 2}}), - "key4": dyn.NewValue("value4", []diag.Location{{File: "file", Line: 2, Column: 2}}), - }, - []diag.Location{{File: "file", Line: -1, Column: 2}}, - ) - - val := dyn.NewValue( - map[string]dyn.Value{ - "styled": styledMap, - "unstyled": unstyledMap, - }, - []diag.Location{{File: "file", Line: 1, Column: 2}}, - ) - - mv, err := s.toYamlNode(val) - assert.NoError(t, err) - - // Check that the styled map is quoted - v := mv.Content[1] - - assert.Equal(t, yaml.MappingNode, v.Kind) - assert.Equal(t, "key1", v.Content[0].Value) - assert.Equal(t, "value1", v.Content[1].Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Content[0].Style) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Content[1].Style) - - assert.Equal(t, "key2", v.Content[2].Value) - assert.Equal(t, "value2", v.Content[3].Value) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Content[2].Style) - assert.Equal(t, yaml.DoubleQuotedStyle, v.Content[3].Style) - - // Check that the unstyled map is not quoted - v = mv.Content[3] - - assert.Equal(t, yaml.MappingNode, v.Kind) - assert.Equal(t, "key3", v.Content[0].Value) - assert.Equal(t, "value3", v.Content[1].Value) - assert.Equal(t, yaml.Style(0), v.Content[0].Style) - assert.Equal(t, yaml.Style(0), v.Content[1].Style) - - assert.Equal(t, "key4", v.Content[2].Value) - assert.Equal(t, "value4", v.Content[3].Value) - assert.Equal(t, yaml.Style(0), v.Content[2].Style) - assert.Equal(t, yaml.Style(0), v.Content[3].Style) -} diff --git a/libs/dyn/yamlsaver/utils.go b/libs/dyn/yamlsaver/utils.go deleted file mode 100644 index 61f41790029..00000000000 --- a/libs/dyn/yamlsaver/utils.go +++ /dev/null @@ -1,94 +0,0 @@ -package yamlsaver - -import ( - "fmt" - "slices" - - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/convert" -) - -// Converts a struct to map. Skips any nil fields. -// It uses `skipFields` to skip unnecessary fields. -// Uses `order` to define the order of keys in resulting outout -func ConvertToMapValue(strct any, order *Order, skipFields []string, dst map[string]dyn.Value) (dyn.Value, error) { - ref := dyn.NilValue - mv, err := convert.FromTyped(strct, ref) - if err != nil { - return dyn.InvalidValue, err - } - - if mv.Kind() != dyn.KindMap { - return dyn.InvalidValue, fmt.Errorf("expected map, got %s", mv.Kind()) - } - - mv, err = sortMapAlphabetically(mv) - if err != nil { - return dyn.InvalidValue, err - } - - return skipAndOrder(mv, order, skipFields, dst) -} - -// Sort the map alphabetically by keys. This is used to produce stable output for generated YAML files. -func sortMapAlphabetically(mv dyn.Value) (dyn.Value, error) { - sortedMap := dyn.NewMapping() - mapV := mv.MustMap() - keys := mapV.Keys() - slices.SortStableFunc(keys, func(i, j dyn.Value) int { - iKey := i.MustString() - jKey := j.MustString() - if iKey < jKey { - return -1 - } - - if iKey > jKey { - return 1 - } - return 0 - }) - - for _, key := range keys { - value, _ := mapV.Get(key) - var err error - if value.Kind() == dyn.KindMap { - value, err = sortMapAlphabetically(value) - if err != nil { - return dyn.InvalidValue, err - } - } - sortedMap.SetLoc(key.MustString(), key.Locations(), value) - } - - return dyn.V(sortedMap), nil -} - -func skipAndOrder(mv dyn.Value, order *Order, skipFields []string, dst map[string]dyn.Value) (dyn.Value, error) { - for _, pair := range mv.MustMap().Pairs() { - k := pair.Key.MustString() - v := pair.Value - if v.Kind() == dyn.KindNil { - continue - } - - if slices.Contains(skipFields, k) { - continue - } - - // If the value is already defined in destination, it means it was - // manually set due to custom ordering or other customisation required - // So we're skipping processing it again - if _, ok := dst[k]; ok { - continue - } - - if order == nil { - dst[k] = v - } else { - dst[k] = dyn.NewValue(v.Value(), []diag.Location{{Line: order.Get(k)}}) - } - } - - return dyn.V(dst), nil -} diff --git a/libs/dyn/yamlsaver/utils_test.go b/libs/dyn/yamlsaver/utils_test.go deleted file mode 100644 index 8b056e0076f..00000000000 --- a/libs/dyn/yamlsaver/utils_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package yamlsaver - -import ( - "testing" - - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/dyn/dynassert" - "github.com/stretchr/testify/assert" -) - -func TestConvertToMap(t *testing.T) { - type test struct { - Name string `json:"name"` - Map map[string]string `json:"map"` - List []string `json:"list"` - LongNameField string `json:"long_name_field"` - ForceSendFields []string `json:"-"` - Format string `json:"format"` - } - - v := &test{ - Name: "test", - Map: map[string]string{ - "key2": "value2", - "key1": "value1", - }, - List: []string{"a", "b", "c"}, - ForceSendFields: []string{ - "Name", - }, - LongNameField: "long name goes here", - } - result, err := ConvertToMapValue(v, nil, []string{"format"}, map[string]dyn.Value{}) - assert.NoError(t, err) - dynassert.Equal(t, dyn.V(map[string]dyn.Value{ - "list": dyn.V( - []dyn.Value{ - dyn.V("a"), - dyn.V("b"), - dyn.V("c"), - }, - ), - "long_name_field": dyn.V("long name goes here"), - "map": dyn.V( - map[string]dyn.Value{ - "key1": dyn.V("value1"), - "key2": dyn.V("value2"), - }, - ), - "name": dyn.V( - "test", - ), - }), result) -} - -func TestConvertToMapValueWithOrder(t *testing.T) { - type test struct { - Name string `json:"name"` - Map map[string]string `json:"map"` - List []string `json:"list"` - LongNameField string `json:"long_name_field"` - ForceSendFields []string `json:"-"` - Format string `json:"format"` - } - - v := &test{ - Name: "test", - Map: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - List: []string{"a", "b", "c"}, - ForceSendFields: []string{ - "Name", - }, - LongNameField: "long name goes here", - } - result, err := ConvertToMapValue(v, NewOrder([]string{"list", "name", "map"}), []string{"format"}, map[string]dyn.Value{}) - assert.NoError(t, err) - - dynassert.Equal(t, dyn.V(map[string]dyn.Value{ - "list": dyn.NewValue( - []dyn.Value{ - dyn.V("a"), - dyn.V("b"), - dyn.V("c"), - }, - []diag.Location{{Line: -3}}, - ), - "name": dyn.NewValue( - "test", - []diag.Location{{Line: -2}}, - ), - "map": dyn.NewValue( - map[string]dyn.Value{ - "key1": dyn.V("value1"), - "key2": dyn.V("value2"), - }, - []diag.Location{{Line: -1}}, - ), - "long_name_field": dyn.NewValue("long name goes here", []diag.Location{{Line: 1}}), - }), result) -} diff --git a/libs/flags/json_flag.go b/libs/flags/json_flag.go index 3ea4834a5f2..775ea346c70 100644 --- a/libs/flags/json_flag.go +++ b/libs/flags/json_flag.go @@ -8,9 +8,9 @@ import ( "reflect" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn/convert" - "github.com/databricks/cli/libs/dyn/jsonloader" + "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/marshal" + "go.yaml.in/yaml/v3" ) type JsonFlag struct { @@ -48,27 +48,18 @@ func (j *JsonFlag) Unmarshal(v any) diag.Diagnostics { return nil } - dv, err := jsonloader.LoadJSON(j.raw, j.source) + node, err := structvar.ParseJSON(j.source, j.raw) if err != nil { return diag.FromErr(err) } - // First normalize the input data. - // It will convert all the values to the correct types. + // Convert the input to the types of the target, in a fresh value: fields set by + // other flags are already in v and must be kept. // For example string literals for booleans and integers will be converted to the correct types. - nv, diags := convert.Normalize(v, dv) - if diags.HasError() { - return diags - } - - if !nv.IsValid() { - kind := reflect.TypeOf(v).Kind() - if kind == reflect.Pointer { - kind = reflect.TypeOf(v).Elem().Kind() - } - + sv, diags, err := structvar.DecodeYAMLNode(j.source, node, reflect.New(reflect.TypeOf(v).Elem()).Interface(), nil) + if err != nil { var expectedJsonType string - switch kind { + switch reflect.TypeOf(v).Elem().Kind() { case reflect.Struct, reflect.Map: expectedJsonType = "object" case reflect.Slice: @@ -80,37 +71,28 @@ func (j *JsonFlag) Unmarshal(v any) diag.Diagnostics { return diags.Append(diag.Diagnostic{ Severity: diag.Error, Summary: "Invalid command input", - Detail: fmt.Sprintf("expected JSON %s, received %s", expectedJsonType, dv.Kind()), + Detail: fmt.Sprintf("expected JSON %s, received %s", expectedJsonType, nodeKind(node)), }) } // Then marshal the normalized data to the output. // It will serialize all set data with the correct types. - data, err := json.Marshal(nv.AsAny()) + data, err := json.Marshal(sv.View().AsAny()) if err != nil { return diags.Extend(diag.FromErr(err)) } - kind := reflect.ValueOf(v).Kind() - if kind == reflect.Pointer { - kind = reflect.ValueOf(v).Elem().Kind() - } - - if kind == reflect.Struct { + if reflect.TypeOf(v).Elem().Kind() == reflect.Struct { // Finally unmarshal the normalized data to the output. // It will fill in the ForceSendFields field if the struct contains it. err = marshal.Unmarshal(data, v) - if err != nil { - return diags.Extend(diag.FromErr(err)) - } } else { // If the output is not a struct, just unmarshal the data to the output. err = json.Unmarshal(data, v) - if err != nil { - return diags.Extend(diag.FromErr(err)) - } } - + if err != nil { + return diags.Extend(diag.FromErr(err)) + } return diags } @@ -133,7 +115,7 @@ func (j *JsonFlag) Validate() error { if j.raw == nil { return nil } - _, err := jsonloader.LoadJSON(j.raw, j.source) + _, err := structvar.ParseJSON(j.source, j.raw) return err } @@ -170,3 +152,15 @@ func (j *JsonFlag) RejectWrappedJSON(outerKey, example string) error { } return fmt.Errorf("%s", msg) } + +// nodeKind returns the kind of the JSON value in node, as a user sees it. +func nodeKind(node *yaml.Node) string { + switch node.Kind { + case yaml.MappingNode: + return "map" + case yaml.SequenceNode: + return "sequence" + default: + return map[string]string{"!!str": "string", "!!bool": "bool", "!!int": "int", "!!float": "float", "!!null": "nil"}[node.Tag] + } +} diff --git a/libs/flags/json_flag_test.go b/libs/flags/json_flag_test.go index 675e1014922..2dc73942719 100644 --- a/libs/flags/json_flag_test.go +++ b/libs/flags/json_flag_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" @@ -202,7 +201,7 @@ func TestJsonUnmarshalRequestMismatch(t *testing.T) { Column: 6, }, }, - Paths: dyn.ToStructPaths(dyn.EmptyPath), + Paths: []*structpath.PathNode{nil}, }) } diff --git a/libs/structs/structaccess/bundle_test.go b/libs/structs/structaccess/bundle_test.go index 4de21ede392..12c06830dbc 100644 --- a/libs/structs/structaccess/bundle_test.go +++ b/libs/structs/structaccess/bundle_test.go @@ -1,4 +1,4 @@ -package structaccess +package structaccess_test import ( "reflect" @@ -6,6 +6,7 @@ import ( "github.com/databricks/cli/bundle/config" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/libs/structs/structaccess" "github.com/databricks/databricks-sdk-go/service/apps" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/require" @@ -45,37 +46,37 @@ func TestGet_ConfigRoot_JobTagsAccess(t *testing.T) { } // Access a value inside the tags map - v, err := GetByString(root, "resources.jobs.my_job.tags.env") + v, err := structaccess.GetByString(root, "resources.jobs.my_job.tags.env") require.NoError(t, err) require.Equal(t, "dev", v) - require.NoError(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags.env")) - require.NoError(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags.anything")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags.env.inner")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags1")) + require.NoError(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags.env")) + require.NoError(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags.anything")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags.env.inner")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tags1")) // Array indexing test (1) - v, err = GetByString(root, "resources.jobs.my_job.tasks[0].task_key") + v, err = structaccess.GetByString(root, "resources.jobs.my_job.tasks[0].task_key") require.NoError(t, err) require.Equal(t, "t1", v) - require.NoError(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].task_key")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].task_key.inner")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].task_key1")) + require.NoError(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].task_key")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].task_key.inner")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].task_key1")) // Array indexing test (2) - v, err = GetByString(root, "resources.jobs.my_job.tasks[0].notebook_task.notebook_path") + v, err = structaccess.GetByString(root, "resources.jobs.my_job.tasks[0].notebook_task.notebook_path") require.NoError(t, err) require.Equal(t, "/Workspace/Users/user@example.com/nb", v) - require.NoError(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].notebook_task.notebook_path")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].notebook_task.notebook_path.inner")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].notebook_task.notebook_path1")) + require.NoError(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].notebook_task.notebook_path")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].notebook_task.notebook_path.inner")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.jobs.my_job.tasks[0].notebook_task.notebook_path1")) // Test ambiguous field access: outer is ignored because it has bundle tag - v, err = GetByString(root, "resources.apps.my_app.url") + v, err = structaccess.GetByString(root, "resources.apps.my_app.url") require.NoError(t, err) require.Equal(t, "app_inner_url", v) - require.NoError(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.apps.my_app.url")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.apps.my_app.url.inner")) - require.Error(t, ValidateByString(reflect.TypeFor[config.Root](), "resources.apps.my_app.url1")) + require.NoError(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.apps.my_app.url")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.apps.my_app.url.inner")) + require.Error(t, structaccess.ValidateByString(reflect.TypeFor[config.Root](), "resources.apps.my_app.url1")) } // A bundle resource embeds a config struct that embeds the SDK request struct, so its @@ -86,29 +87,29 @@ func TestGetSet_DoublyEmbeddedField(t *testing.T) { project := &resources.PostgresProject{} //exhaustruct:ignore project.ProjectId = "p" - require.NoError(t, ValidateByString(reflect.TypeOf(project), "budget_policy_id")) + require.NoError(t, structaccess.ValidateByString(reflect.TypeOf(project), "budget_policy_id")) - require.NoError(t, SetByString(project, "budget_policy_id", "abc")) + require.NoError(t, structaccess.SetByString(project, "budget_policy_id", "abc")) require.Equal(t, "abc", project.BudgetPolicyId) - value, err := GetByString(project, "budget_policy_id") + value, err := structaccess.GetByString(project, "budget_policy_id") require.NoError(t, err) require.Equal(t, "abc", value) // An explicit empty value is recorded on ProjectSpec, which declares the field. - require.NoError(t, SetByString(project, "budget_policy_id", "")) + require.NoError(t, structaccess.SetByString(project, "budget_policy_id", "")) require.Contains(t, project.ProjectSpec.ForceSendFields, "BudgetPolicyId") require.NotContains(t, project.ForceSendFields, "BudgetPolicyId") - value, err = GetByString(project, "budget_policy_id") + value, err = structaccess.GetByString(project, "budget_policy_id") require.NoError(t, err) // The empty string, not nil: that is what separates an explicit "" from an absent field. require.Equal(t, any(""), value) // And dropping it again leaves the field absent. - require.NoError(t, SetByString(project, "budget_policy_id", nil)) + require.NoError(t, structaccess.SetByString(project, "budget_policy_id", nil)) require.NotContains(t, project.ProjectSpec.ForceSendFields, "BudgetPolicyId") - value, err = GetByString(project, "budget_policy_id") + value, err = structaccess.GetByString(project, "budget_policy_id") require.NoError(t, err) require.Nil(t, value) } diff --git a/libs/structs/structpath/path.go b/libs/structs/structpath/path.go index adf2be51331..e719c21003d 100644 --- a/libs/structs/structpath/path.go +++ b/libs/structs/structpath/path.go @@ -9,7 +9,7 @@ import ( ) // baseVarDef matches a path segment of a ${...} reference. It is the grammar of -// dynvar.BaseVarDef, which structpath cannot import; TestPureReferenceMatchesDynvar +// structvar.BaseVarDef, which structpath cannot import; TestPureReferenceMatchesStructvar // keeps them in agreement. const baseVarDef = `_*\p{L}+([-_]*[\p{L}\p{N}]+)*` @@ -191,6 +191,15 @@ func NewPathSlice(parts ...any) []*PathNode { return []*PathNode{NewPath(nil, parts...)} } +// Join appends the components of nodes (each node's own key, index or key-value, not +// its parents) to prefix. +func Join(prefix *PathNode, nodes ...*PathNode) *PathNode { + for _, n := range nodes { + prefix = &PathNode{prev: prefix, key: n.key, index: n.index, value: n.value} + } + return prefix +} + func NewKeyValue(prev *PathNode, key, value string) *PathNode { return &PathNode{ prev: prev, @@ -360,6 +369,37 @@ func NewPatternBracketStar(prev *PatternNode) *PatternNode { }) } +// wildcard is a NewPattern part that matches any key or element. +type wildcard int + +// Wildcards for [NewPattern]: AnyKey matches any map key or field (rendered ".*") and +// AnyIndex matches any sequence element (rendered "[*]"). +const ( + AnyKey wildcard = tagDotStar + AnyIndex wildcard = tagBracketStar +) + +// NewPattern appends parts to prev like [NewPath]; AnyKey and AnyIndex append wildcards. +func NewPattern(prev *PatternNode, parts ...any) *PatternNode { + for _, part := range parts { + switch v := part.(type) { + case string: + prev = NewPatternStringKey(prev, v) + case int: + prev = NewPatternIndex(prev, v) + case wildcard: + if v == AnyKey { + prev = NewPatternDotStar(prev) + } else { + prev = NewPatternBracketStar(prev) + } + default: + panic(fmt.Sprintf("structpath.NewPattern: unsupported part %#v", part)) + } + } + return prev +} + func NewPatternKeyValue(prev *PatternNode, key, value string) *PatternNode { return (*PatternNode)(NewKeyValue((*PathNode)(prev), key, value)) } @@ -712,7 +752,6 @@ func isValidField(s string) bool { } // PureReferenceToPath returns a PathNode if s is a pure variable reference, otherwise false. -// This function is similar to dynvar.PureReferenceToPath but returns a *PathNode instead of dyn.Path. func PureReferenceToPath(s string) (*PathNode, bool) { m := pureReference.FindStringSubmatch(s) if m == nil { @@ -986,3 +1025,13 @@ func (p *PatternNode) UnmarshalYAML(unmarshal func(any) error) error { *p = *parsed return nil } + +// KeyAt returns the string key of the i-th component of the path, or "" if it is not a key. +func (p *PathNode) KeyAt(i int) string { + nodes := p.AsSlice() + if i < 0 || i >= len(nodes) { + return "" + } + k, _ := nodes[i].StringKey() + return k +} diff --git a/libs/structs/structpath/path_test.go b/libs/structs/structpath/path_test.go index 40fc0282c2b..58cf6d050cb 100644 --- a/libs/structs/structpath/path_test.go +++ b/libs/structs/structpath/path_test.go @@ -1275,14 +1275,6 @@ paths: assert.Contains(t, err.Error(), "unexpected end of input") } -func TestMustParsePaths(t *testing.T) { - paths := MustParsePaths("bundle.terraform", "resources.jobs.foo.tasks[0]") - require.Len(t, paths, 2) - assert.Equal(t, "bundle.terraform", paths[0].String()) - assert.Equal(t, "resources.jobs.foo.tasks[0]", paths[1].String()) - assert.Empty(t, MustParsePaths()) -} - func TestNewPath(t *testing.T) { assert.Equal(t, "a.b['c d']", NewPath(NewStringKey(nil, "a"), "b", "c d").String()) assert.Equal(t, "resources.jobs.j.tasks[2].job_cluster_key", NewPath(nil, "resources", "jobs", "j", "tasks", 2, "job_cluster_key").String()) @@ -1291,9 +1283,34 @@ func TestNewPath(t *testing.T) { assert.Panics(t, func() { NewPath(nil, int64(1)) }) } +func TestJoin(t *testing.T) { + p := MustParsePath("tasks[task_key='x'].libraries[0]['a.b']") + assert.Equal(t, "resources.jobs.j.tasks[task_key='x'].libraries[0]['a.b']", Join(MustParsePath("resources.jobs.j"), p.AsSlice()...).String()) + assert.Equal(t, p.String(), Join(nil, p.AsSlice()...).String()) + assert.Equal(t, "a", Join(MustParsePath("a")).String()) +} + +func TestMustParsePaths(t *testing.T) { + paths := MustParsePaths("bundle.terraform", "resources.jobs.foo.tasks[0]") + require.Len(t, paths, 2) + assert.Equal(t, "bundle.terraform", paths[0].String()) + assert.Equal(t, "resources.jobs.foo.tasks[0]", paths[1].String()) + assert.Empty(t, MustParsePaths()) +} + func TestNewPathSlice(t *testing.T) { paths := NewPathSlice("resources", "jobs", "${var.env}_job") require.Len(t, paths, 1) assert.Equal(t, "resources.jobs['${var.env}_job']", paths[0].String()) assert.Equal(t, "sync.paths[3]", NewPathSlice("sync", "paths", 3)[0].String()) } + +func TestNewPattern(t *testing.T) { + p := NewPattern(nil, "resources", "jobs", AnyKey, "tasks", AnyIndex, "libraries") + assert.Equal(t, "resources.jobs.*.tasks[*].libraries", p.String()) + + ext := NewPattern(p, AnyIndex, "whl", 0) + assert.Equal(t, "resources.jobs.*.tasks[*].libraries[*].whl[0]", ext.String()) + + assert.Panics(t, func() { NewPattern(nil, 1.5) }) +} diff --git a/libs/structs/structpath/reference_test.go b/libs/structs/structpath/reference_test.go index 485b756c7fb..45360c24b4b 100644 --- a/libs/structs/structpath/reference_test.go +++ b/libs/structs/structpath/reference_test.go @@ -3,12 +3,12 @@ package structpath_test import ( "testing" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" "github.com/stretchr/testify/assert" ) -func TestPureReferenceMatchesDynvar(t *testing.T) { +func TestPureReferenceMatchesStructvar(t *testing.T) { for _, s := range []string{ "${var.foo}", "${resources.jobs.foo.tasks[1].env.key}", @@ -25,6 +25,6 @@ func TestPureReferenceMatchesDynvar(t *testing.T) { "plain_string", } { _, ok := structpath.PureReferenceToPath(s) - assert.Equal(t, dynvar.IsPureVariableReference(s), ok, s) + assert.Equal(t, structvar.IsPureVariableReference(s), ok, s) } } diff --git a/libs/structs/structvar/decode.go b/libs/structs/structvar/decode.go new file mode 100644 index 00000000000..233b48a948b --- /dev/null +++ b/libs/structs/structvar/decode.go @@ -0,0 +1,761 @@ +package structvar + +import ( + "encoding/json" + "fmt" + "io" + "maps" + "reflect" + "strconv" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" + "go.yaml.in/yaml/v3" +) + +// source is a configuration tree to decode from (a parsed YAML document or a view). +type source interface { + kind() Kind + locations() []diag.Location + // anchor reports whether the value is a YAML anchor (or an alias of one). + anchor() bool + // scalar returns the value of a string, bool, int (int or int64), float or time (string). + scalar() any + pairs() ([]sourcePair, error) + elems() ([]source, error) +} + +type sourcePair struct { + key string + // keyLoc is the location of the key, if any. + keyLoc diag.Location + value source +} + +// keyLocations returns the locations of the key for a diagnostic. +func (p sourcePair) keyLocations() []diag.Location { + if p.keyLoc == (diag.Location{}) { + return nil + } + return []diag.Location{p.keyLoc} +} + +// DecodeYAML decodes the YAML document in r (read from file) into dst, a pointer to a +// typed configuration value, and returns its references and locations. +// +// Values are converted to the field types the way configuration is normalized: scalars +// are converted where possible, pure variable references are recorded in the returned +// references for fields that cannot hold a string, and values that cannot be converted +// or fields that do not exist are dropped with a warning. An error is returned for +// invalid YAML. +func DecodeYAML(file string, r io.Reader, dst any) (*StructVar, diag.Diagnostics, error) { + node, err := ParseYAML(r) + if err != nil { + return nil, nil, err + } + return DecodeYAMLNode(file, node, dst, nil) +} + +// LocationMapper returns the locations to record for the value at path, given the +// locations it has in the YAML document. +type LocationMapper func(path *structpath.PathNode, locs []diag.Location) []diag.Location + +// DecodeYAMLNode is [DecodeYAML] for a parsed YAML document (nil if it is empty). +// If mapLocations is not nil, it is applied to the location of every value. +func DecodeYAMLNode(file string, node *yaml.Node, dst any, mapLocations LocationMapper) (*StructVar, diag.Diagnostics, error) { + sv := &StructVar{Value: dst} + v := reflect.ValueOf(dst).Elem() + if node == nil { + // An empty document is null. + v.SetZero() + return sv, nil, nil + } + src, err := newYAMLSource(file, node, nil) + if err != nil { + return nil, nil, err + } + // Report YAML errors anywhere in the document, also in values that are dropped. + if err := validate(src); err != nil { + return nil, nil, err + } + src.mapLocations = mapLocations + var d decoder + n, ok, err := d.decode(v, src, nil) + if err == nil && !ok { + // The value could not be converted to dst at all (e.g. a list for a struct). + err = fmt.Errorf("expected a %s, found a %s", kindOf(v.Type()), src.kind()) + } + sv.Refs = n.refs(nil, nil) + sv.Locations = n.locations() + return sv, d.diags, err +} + +// Assign sets the value described by src at path, replacing what was there, with the +// references and locations of src. The value is converted to the type at path like +// [DecodeYAML] converts values; the diagnostics explain values that were dropped. A +// dropped slice element or map entry is removed, like [DecodeYAML] does. +func (sv *StructVar) Assign(path *structpath.PathNode, src View) (diag.Diagnostics, error) { + var d decoder + var n *dnode + dropped := false + err := update(reflect.ValueOf(sv.Value), components(path), func(dst reflect.Value) (bool, error) { + var ok bool + var err error + n, ok, err = d.decode(dst, viewSource{src}, path) + if !ok { + dst.SetZero() + dropped = err == nil + return false, err + } + return n.isZeroValue(dst), err + }) + if err != nil { + return d.diags, err + } + if dropped && path != nil { + return d.diags, sv.Delete(path) + } + sv.Refs = n.refs(path, withoutRefs(sv.Refs, path)) + sv.Locations = sv.Locations.With(path, n.locations()) + return d.diags, nil +} + +func validate(s source) error { + switch s.kind() { + case KindMap: + pairs, err := s.pairs() + if err != nil { + return err + } + for _, p := range pairs { + if err := validate(p.value); err != nil { + return err + } + } + case KindSequence: + elems, err := s.elems() + if err != nil { + return err + } + for _, e := range elems { + if err := validate(e); err != nil { + return err + } + } + default: + } + return nil +} + +type decoder struct { + diags diag.Diagnostics +} + +// dnode is the decoded shape of a value: what the typed value does not hold. +type dnode struct { + kind Kind + locs []diag.Location + ref string + keys map[string]*dnode + elems []*dnode +} + +// locations returns the locations of the decoded value. +func (n *dnode) locations() *Locations { + if n == nil { + return nil + } + out := &Locations{locs: n.locs} + for k, c := range n.keys { + if out.keys == nil { + out.keys = make(map[string]*Locations, len(n.keys)) + } + out.keys[k] = c.locations() + } + for _, c := range n.elems { + out.elems = append(out.elems, c.locations()) + } + return out +} + +// refs returns refs (copied) plus the references of the decoded value at path. +func (n *dnode) refs(path *structpath.PathNode, refs map[string]string) map[string]string { + var out map[string]string + n.collectRefs(path, func(p *structpath.PathNode, ref string) { + if out == nil { + out = maps.Clone(refs) + if out == nil { + out = map[string]string{} + } + } + out[p.String()] = ref + }) + if out == nil { + return refs + } + return out +} + +func (n *dnode) collectRefs(path *structpath.PathNode, fn func(*structpath.PathNode, string)) { + if n == nil { + return + } + if n.ref != "" { + fn(path, n.ref) + } + for k, c := range n.keys { + c.collectRefs(structpath.NewStringKey(path, k), fn) + } + for i, c := range n.elems { + c.collectRefs(structpath.NewIndex(path, i), fn) + } +} + +func (d *decoder) warn(summary string, src source, path *structpath.PathNode) { + d.diags = d.diags.Append(diag.Diagnostic{ + Severity: diag.Warning, + Summary: summary, + Locations: []diag.Location{firstLocation(src)}, + Paths: []*structpath.PathNode{path}, + }) +} + +func firstLocation(src source) diag.Location { + locs := src.locations() + if len(locs) == 0 { + return diag.Location{} + } + return locs[0] +} + +func (d *decoder) typeMismatch(expected Kind, src source, path *structpath.PathNode) { + d.warn(fmt.Sprintf("expected %s, found %s", expected, src.kind()), src, path) +} + +func (d *decoder) nullWarning(expected Kind, src source, path *structpath.PathNode) { + d.warn(fmt.Sprintf("expected a %s value, found null", expected), src, path) +} + +func pureRefOf(src source) string { + if src.kind() != KindString { + return "" + } + s, _ := src.scalar().(string) + if IsPureVariableReference(s) { + return s + } + return "" +} + +// decode decodes src into dst (settable) and returns its meta. It reports false if the +// value cannot be decoded and is dropped (the diagnostics say why). +func (d *decoder) decode(dst reflect.Value, src source, path *structpath.PathNode) (*dnode, bool, error) { + typ := dst.Type() + if typ.Kind() == reflect.Pointer { + if src.kind() == KindNil { + n, ok, err := d.decode(reflect.New(typ.Elem()).Elem(), src, path) + if ok { + dst.SetZero() + } + return n, ok, err + } + p := reflect.New(typ.Elem()) + n, ok, err := d.decode(p.Elem(), src, path) + if ok { + dst.Set(p) + } + return n, ok, err + } + + n := &dnode{kind: src.kind(), locs: src.locations()} + ref := pureRefOf(src) + + switch typ.Kind() { + case reflect.Struct: + if IsSDKNativeType(typ) { + s, ok := d.decodeString(src, path) + if !ok { + return nil, false, nil + } + n.kind = KindString + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + buf, err := json.Marshal(s) + if err != nil { + return nil, false, err + } + return n, true, json.Unmarshal(buf, dst.Addr().Interface()) + } + return d.decodeStruct(dst, src, path, n, ref) + case reflect.Map: + switch src.kind() { + case KindMap: + pairs, err := src.pairs() + if err != nil { + return nil, false, err + } + out := reflect.MakeMapWithSize(typ, len(pairs)) + n.keys = make(map[string]*dnode, len(pairs)) + for _, p := range pairs { + e := reflect.New(typ.Elem()).Elem() + en, ok, err := d.decode(e, p.value, structpath.NewStringKey(path, p.key)) + if err != nil { + return nil, false, err + } + if !ok { + continue + } + out.SetMapIndex(reflect.ValueOf(p.key).Convert(typ.Key()), e) + n.keys[p.key] = en + } + dst.Set(out) + return n, true, nil + case KindNil: + dst.SetZero() + return n, true, nil + default: + } + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + d.typeMismatch(KindMap, src, path) + return nil, false, nil + case reflect.Slice: + switch src.kind() { + case KindSequence: + elems, err := src.elems() + if err != nil { + return nil, false, err + } + out := reflect.MakeSlice(typ, 0, len(elems)) + for _, ev := range elems { + e := reflect.New(typ.Elem()).Elem() + en, ok, err := d.decode(e, ev, structpath.NewIndex(path, out.Len())) + if err != nil { + return nil, false, err + } + if !ok { + continue + } + out = reflect.Append(out, e) + n.elems = append(n.elems, en) + } + dst.Set(out) + return n, true, nil + case KindNil: + dst.SetZero() + return n, true, nil + default: + } + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + d.typeMismatch(KindSequence, src, path) + return nil, false, nil + case reflect.String: + s, ok := d.decodeString(src, path) + if !ok { + return nil, false, nil + } + n.kind = KindString + dst.SetString(s) + return n, true, nil + case reflect.Bool: + return d.decodeBool(dst, src, path, n, ref) + case reflect.Int, reflect.Int32, reflect.Int64: + return d.decodeInt(dst, src, path, n, ref) + case reflect.Float32, reflect.Float64: + return d.decodeFloat(dst, src, path, n, ref) + case reflect.Interface: + v, err := d.decodeAny(src, n) + if err != nil { + return nil, false, err + } + if v == nil { + dst.SetZero() + } else { + dst.Set(reflect.ValueOf(v)) + } + return n, true, nil + default: + return nil, false, fmt.Errorf("unsupported type: %s", typ.Kind()) + } +} + +// isAnchorContainer reports whether v is a YAML anchor or a non-empty sequence or map +// composed entirely of anchor containers. Anchors define reusable blocks and must not +// trigger "unknown field" warnings, including when nested inside a container. +func isAnchorContainer(v source) bool { + if v.anchor() { + return true + } + var elements []source + switch v.kind() { + case KindSequence: + elements, _ = v.elems() + case KindMap: + pairs, _ := v.pairs() + for _, p := range pairs { + elements = append(elements, p.value) + } + default: + return false + } + if len(elements) == 0 { + return false + } + for _, e := range elements { + if !isAnchorContainer(e) { + return false + } + } + return true +} + +func (d *decoder) decodeStruct(dst reflect.Value, src source, path *structpath.PathNode, n *dnode, ref string) (*dnode, bool, error) { + switch src.kind() { + case KindMap: + case KindNil: + dst.SetZero() + return n, true, nil + default: + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + d.typeMismatch(KindMap, src, path) + return nil, false, nil + } + + pairs, err := src.pairs() + if err != nil { + return nil, false, err + } + + dst.SetZero() + info := GetStructInfo(dst.Type()) + n.keys = make(map[string]*dnode, len(pairs)) + for _, p := range pairs { + index, ok := info.Fields[p.key] + if !ok { + if isAnchorContainer(p.value) { + continue + } + // Special case: provide a more helpful message for "valueFrom" vs "value_from". + if _, hasValueFrom := info.Fields["value_from"]; p.key == "valueFrom" && hasValueFrom { + d.diags = d.diags.Append(diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Use 'value_from' instead of 'valueFrom'", + Detail: "The field 'valueFrom' should be 'value_from' (snake_case). The 'valueFrom' field will be ignored.", + Locations: p.keyLocations(), + Paths: []*structpath.PathNode{path}, + }) + continue + } + d.diags = d.diags.Append(diag.Diagnostic{ + Severity: diag.Warning, + Summary: "unknown field: " + p.key, + Locations: p.keyLocations(), + Paths: []*structpath.PathNode{path}, + }) + continue + } + + // Decode into a fresh value so a dropped value leaves the field (and any + // embedded struct pointers on the way to it) untouched. + ft := dst.Type().FieldByIndex(index).Type + fv := reflect.New(ft).Elem() + fn, ok, err := d.decode(fv, p.value, structpath.NewStringKey(path, p.key)) + if err != nil { + return nil, false, err + } + if !ok { + continue + } + GetOrNewFieldByIndex(dst, index).Set(fv) + n.keys[p.key] = fn + + // An explicitly set zero value must still serialize: add it to ForceSendFields. + if fn.isZeroValue(fv) { + addForceSend(dst, info, p.key) + } + } + return n, true, nil +} + +// isZeroValue mirrors dyn.Value.IsZero for the decoded value: nil, an empty map or +// sequence, or a zero scalar (a pure reference is not zero). +func (n *dnode) isZeroValue(v reflect.Value) bool { + switch n.kind { + case KindNil: + return true + case KindMap: + return len(n.keys) == 0 + case KindSequence: + return len(n.elems) == 0 + default: + } + if n.ref != "" { + return false + } + for v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface { + if v.IsNil() { + return true + } + v = v.Elem() + } + if v.Kind() == reflect.Struct { + // SDK native types are strings in the configuration tree. + s, ok := sdkNativeString(v, includeZero) + return !ok || s == "" + } + return v.IsZero() +} + +func (d *decoder) decodeString(src source, path *structpath.PathNode) (string, bool) { + switch src.kind() { + case KindString, KindTime: + return src.scalar().(string), true + case KindBool: + return strconv.FormatBool(src.scalar().(bool)), true + case KindInt: + return strconv.FormatInt(toInt64(src.scalar()), 10), true + case KindFloat: + return strconv.FormatFloat(src.scalar().(float64), 'f', -1, 64), true + case KindNil: + d.nullWarning(KindString, src, path) + return "", false + default: + d.typeMismatch(KindString, src, path) + return "", false + } +} + +func toInt64(v any) int64 { + switch v := v.(type) { + case int: + return int64(v) + case int64: + return v + default: + panic(fmt.Sprintf("unexpected int type %T", v)) + } +} + +func (d *decoder) decodeBool(dst reflect.Value, src source, path *structpath.PathNode, n *dnode, ref string) (*dnode, bool, error) { + switch src.kind() { + case KindBool: + dst.SetBool(src.scalar().(bool)) + return n, true, nil + case KindString: + // See https://yaml.org/type/bool.html. + switch src.scalar().(string) { + case "true", "True", "TRUE", "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": + dst.SetBool(true) + case "false", "False", "FALSE", "n", "N", "no", "No", "NO", "off", "Off", "OFF": + dst.SetBool(false) + default: + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + d.typeMismatch(KindBool, src, path) + return nil, false, nil + } + n.kind = KindBool + return n, true, nil + case KindNil: + d.nullWarning(KindBool, src, path) + return nil, false, nil + default: + d.typeMismatch(KindBool, src, path) + return nil, false, nil + } +} + +func (d *decoder) decodeInt(dst reflect.Value, src source, path *structpath.PathNode, n *dnode, ref string) (*dnode, bool, error) { + switch src.kind() { + case KindInt: + dst.SetInt(toInt64(src.scalar())) + return n, true, nil + case KindFloat: + f := src.scalar().(float64) + out := int64(f) + if f != float64(out) { + d.warn(fmt.Sprintf(`cannot accurately represent "%g" as integer due to precision loss`, f), src, path) + return nil, false, nil + } + dst.SetInt(out) + n.kind = KindInt + return n, true, nil + case KindString: + s := src.scalar().(string) + out, err := strconv.ParseInt(s, 10, 64) + if err != nil { + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + d.warn(fmt.Sprintf("cannot parse %q as an integer", s), src, path) + return nil, false, nil + } + dst.SetInt(out) + n.kind = KindInt + return n, true, nil + case KindNil: + d.nullWarning(KindInt, src, path) + return nil, false, nil + default: + d.typeMismatch(KindInt, src, path) + return nil, false, nil + } +} + +func (d *decoder) decodeFloat(dst reflect.Value, src source, path *structpath.PathNode, n *dnode, ref string) (*dnode, bool, error) { + switch src.kind() { + case KindFloat: + dst.SetFloat(src.scalar().(float64)) + return n, true, nil + case KindInt: + i := toInt64(src.scalar()) + out := float64(i) + if i != int64(out) { + d.warn(fmt.Sprintf(`cannot accurately represent "%d" as floating point number due to precision loss`, i), src, path) + return nil, false, nil + } + dst.SetFloat(out) + n.kind = KindFloat + return n, true, nil + case KindString: + s := src.scalar().(string) + out, err := strconv.ParseFloat(s, 64) + if err != nil { + if ref != "" { + n.ref = ref + dst.SetZero() + return n, true, nil + } + d.warn(fmt.Sprintf("cannot parse %q as a floating point number", s), src, path) + return nil, false, nil + } + dst.SetFloat(out) + n.kind = KindFloat + return n, true, nil + case KindNil: + d.nullWarning(KindFloat, src, path) + return nil, false, nil + default: + d.typeMismatch(KindFloat, src, path) + return nil, false, nil + } +} + +// decodeAny returns the generic Go value for src (maps, slices, scalars) and fills in +// the meta of everything below n. Timestamps become the string they were written as. +func (d *decoder) decodeAny(src source, n *dnode) (any, error) { + switch src.kind() { + case KindMap: + pairs, err := src.pairs() + if err != nil { + return nil, err + } + out := make(map[string]any, len(pairs)) + n.keys = make(map[string]*dnode, len(pairs)) + for _, p := range pairs { + cn := &dnode{kind: p.value.kind(), locs: p.value.locations()} + v, err := d.decodeAny(p.value, cn) + if err != nil { + return nil, err + } + out[p.key] = v + n.keys[p.key] = cn + } + return out, nil + case KindSequence: + elems, err := src.elems() + if err != nil { + return nil, err + } + out := make([]any, len(elems)) + for i, e := range elems { + cn := &dnode{kind: e.kind(), locs: e.locations()} + v, err := d.decodeAny(e, cn) + if err != nil { + return nil, err + } + out[i] = v + n.elems = append(n.elems, cn) + } + return out, nil + case KindTime: + n.kind = KindString + return src.scalar(), nil + case KindNil: + return nil, nil + default: + return src.scalar(), nil + } +} + +// viewSource is a [source] backed by a view. +type viewSource struct { + v View +} + +func (s viewSource) kind() Kind { return s.v.Kind() } +func (s viewSource) locations() []diag.Location { return s.v.Locations() } +func (s viewSource) anchor() bool { return false } + +func (s viewSource) scalar() any { + if str, ok := s.v.AsString(); ok { + return str + } + return s.v.AsAny() +} + +func (s viewSource) pairs() ([]sourcePair, error) { + var out []sourcePair + for k, c := range s.v.MapItems() { + out = append(out, sourcePair{key: k, value: viewSource{c}}) + } + return out, nil +} + +func (s viewSource) elems() ([]source, error) { + var out []source + for _, c := range s.v.Sequence() { + out = append(out, viewSource{c}) + } + return out, nil +} + +// kindOf returns the kind of configuration tree values of type t. +func kindOf(t reflect.Type) Kind { + for t.Kind() == reflect.Pointer { + t = t.Elem() + } + switch t.Kind() { + case reflect.Struct, reflect.Map: + return KindMap + case reflect.Slice: + return KindSequence + case reflect.Bool: + return KindBool + case reflect.Int, reflect.Int32, reflect.Int64: + return KindInt + case reflect.Float32, reflect.Float64: + return KindFloat + default: + return KindString + } +} diff --git a/libs/structs/structvar/decode_dyn.go b/libs/structs/structvar/decode_dyn.go new file mode 100644 index 00000000000..1aa687e8970 --- /dev/null +++ b/libs/structs/structvar/decode_dyn.go @@ -0,0 +1,104 @@ +package structvar + +import ( + "fmt" + "reflect" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" +) + +// DecodeDyn decodes the configuration tree v into dst, a pointer to a typed +// configuration value, and returns its references and locations (those of every +// value in v, including the ones merges accumulated). +// +// Values are converted to the field types like [DecodeYAML] converts them; the +// diagnostics explain values that were dropped. +func DecodeDyn(v dyn.Value, dst any) (*StructVar, diag.Diagnostics, error) { + sv := &StructVar{Value: dst} + rv := reflect.ValueOf(dst).Elem() + if !v.IsValid() { + rv.SetZero() + return sv, nil, nil + } + var d decoder + n, ok, err := d.decode(rv, dynSource{v}, nil) + if err == nil && !ok { + err = fmt.Errorf("expected a %s, found a %s", kindOf(rv.Type()), dynSource{v}.kind()) + } + sv.Refs = n.refs(nil, nil) + sv.Locations = n.locations() + return sv, d.diags, err +} + +// dynSource is a [source] backed by a [dyn.Value]. +type dynSource struct { + v dyn.Value +} + +func (s dynSource) kind() Kind { + switch s.v.Kind() { + case dyn.KindMap: + return KindMap + case dyn.KindSequence: + return KindSequence + case dyn.KindString: + return KindString + case dyn.KindBool: + return KindBool + case dyn.KindInt: + return KindInt + case dyn.KindFloat: + return KindFloat + case dyn.KindTime: + return KindTime + case dyn.KindNil: + return KindNil + default: + return KindInvalid + } +} + +func (s dynSource) locations() []diag.Location { return s.v.Locations() } +func (s dynSource) anchor() bool { return s.v.IsAnchor() } + +func (s dynSource) scalar() any { + switch s.v.Kind() { + case dyn.KindTime: + return s.v.MustTime().String() + case dyn.KindInt: + // Like YAML values: int if it fits, int64 otherwise. + i, _ := s.v.AsInt() + return intValue(i) + case dyn.KindFloat: + // The decoder expects float64. + f, _ := s.v.AsFloat() + return f + default: + return s.v.AsAny() + } +} + +func (s dynSource) pairs() ([]sourcePair, error) { + m, ok := s.v.AsMap() + if !ok { + return nil, nil + } + out := make([]sourcePair, 0, m.Len()) + for _, p := range m.Pairs() { + out = append(out, sourcePair{key: p.Key.MustString(), keyLoc: p.Key.Location(), value: dynSource{p.Value}}) + } + return out, nil +} + +func (s dynSource) elems() ([]source, error) { + seq, ok := s.v.AsSequence() + if !ok { + return nil, nil + } + out := make([]source, len(seq)) + for i, e := range seq { + out[i] = dynSource{e} + } + return out, nil +} diff --git a/libs/structs/structvar/decode_json.go b/libs/structs/structvar/decode_json.go new file mode 100644 index 00000000000..e332aa9d863 --- /dev/null +++ b/libs/structs/structvar/decode_json.go @@ -0,0 +1,126 @@ +package structvar + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "slices" + "strconv" + + "github.com/databricks/cli/libs/diag" + "go.yaml.in/yaml/v3" +) + +// ParseJSON parses data into a YAML node tree that carries the positions of the values +// in data: the start of an object or array, the first character of a key and the end of +// a scalar (the decoder does not report where a scalar starts). +func ParseJSON(source string, data []byte) (*yaml.Node, error) { + decoder := json.NewDecoder(bytes.NewReader(data)) + // Use json.Number to avoid losing precision on int64 values above 2^53 (e.g. job and run IDs). + decoder.UseNumber() + + lines := newLineIndex(data) + node, err := decodeJSONValue(decoder, lines) + if err == nil && decoder.More() { + err = errors.New("unexpected additional content") + } + if err != nil { + if errors.Is(err, io.EOF) { + err = errors.New("unexpected end of JSON input") + } + line, column := lines.position(decoder.InputOffset()) + return nil, fmt.Errorf("error decoding JSON at %s: %w", diag.Location{File: source, Line: line, Column: column}, err) + } + return node, nil +} + +// lineIndex holds the offsets at which the lines of the input start. +type lineIndex []int64 + +func newLineIndex(data []byte) lineIndex { + lines := lineIndex{0} + for i, b := range data { + if b == '\n' { + lines = append(lines, int64(i+1)) + } + } + return lines +} + +// position returns the 1-based line and column of offset. +func (l lineIndex) position(offset int64) (line, column int) { + // The line is the last one starting at or before offset. + i, found := slices.BinarySearch(l, offset) + if !found { + i-- + } + return i + 1, int(offset-l[i]) + 1 +} + +func newNode(lines lineIndex, offset int64, kind yaml.Kind, tag, value string) *yaml.Node { + line, column := lines.position(offset) + return &yaml.Node{Kind: kind, Tag: tag, Value: value, Line: line, Column: column} +} + +func decodeJSONValue(decoder *json.Decoder, lines lineIndex) (*yaml.Node, error) { + token, err := decoder.Token() + if err != nil { + return nil, err + } + offset := decoder.InputOffset() + + switch tok := token.(type) { + case json.Delim: + if tok == '{' { + node := newNode(lines, offset-1, yaml.MappingNode, "!!map", "") + for decoder.More() { + keyToken, err := decoder.Token() + if err != nil { + return nil, err + } + key, ok := keyToken.(string) + if !ok { + return nil, errors.New("expected string for object key") + } + // The decoder reports the end of the key: step back over the key and the closing quote. + keyNode := newNode(lines, decoder.InputOffset()-int64(len(key)+1), yaml.ScalarNode, "!!str", key) + value, err := decodeJSONValue(decoder, lines) + if err != nil { + return nil, err + } + node.Content = append(node.Content, keyNode, value) + } + // Consume the closing '}'. + _, err := decoder.Token() + return node, err + } + node := newNode(lines, offset-1, yaml.SequenceNode, "!!seq", "") + for decoder.More() { + value, err := decodeJSONValue(decoder, lines) + if err != nil { + return nil, err + } + node.Content = append(node.Content, value) + } + // Consume the closing ']'. + _, err := decoder.Token() + return node, err + case json.Number: + if _, err := tok.Int64(); err == nil { + return newNode(lines, offset, yaml.ScalarNode, "!!int", tok.String()), nil + } + // Integers that overflow int64 fall back to float64. + if _, err := tok.Float64(); err != nil { + return nil, fmt.Errorf("invalid number %q: %w", tok.String(), err) + } + return newNode(lines, offset, yaml.ScalarNode, "!!float", tok.String()), nil + case string: + return newNode(lines, offset, yaml.ScalarNode, "!!str", tok), nil + case bool: + return newNode(lines, offset, yaml.ScalarNode, "!!bool", strconv.FormatBool(tok)), nil + default: + return newNode(lines, offset, yaml.ScalarNode, "!!null", "null"), nil + } +} diff --git a/libs/structs/structvar/decode_yaml.go b/libs/structs/structvar/decode_yaml.go new file mode 100644 index 00000000000..0b3e85234ee --- /dev/null +++ b/libs/structs/structvar/decode_yaml.go @@ -0,0 +1,370 @@ +package structvar + +import ( + "errors" + "fmt" + "io" + "math" + "strconv" + "strings" + "time" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" + "go.yaml.in/yaml/v3" +) + +// LocationError is a YAML syntax error with the location it occurred at. +type LocationError struct { + Loc diag.Location + Summary string +} + +func (e *LocationError) Error() string { + return fmt.Sprintf("yaml (%s): %s", e.Loc, e.Summary) +} + +// yamlSource is a [source] backed by a YAML node. Aliases are followed, merge keys +// are applied, and scalars are resolved by their tag. +type yamlSource struct { + file string + node *yaml.Node + + // The node the value comes from, after following aliases. + target *yaml.Node + + // Aliases followed on the way from the document root to this value, to detect + // cyclic anchors. + aliases *aliasChain + + // If set, mapLocations is applied to the locations of the value at path. + mapLocations LocationMapper + path *structpath.PathNode + + // Resolved scalar. + k Kind + v any +} + +// ParseYAML parses the YAML document in r. It returns nil for an empty document. +func ParseYAML(r io.Reader) (*yaml.Node, error) { + var doc yaml.Node + err := yaml.NewDecoder(r).Decode(&doc) + if errors.Is(err, io.EOF) { + return nil, nil + } + if err != nil { + return nil, err + } + return doc.Content[0], nil +} + +// aliasChain is an immutable list of alias nodes. +type aliasChain struct { + node *yaml.Node + prev *aliasChain +} + +func (c *aliasChain) contains(n *yaml.Node) bool { + for ; c != nil; c = c.prev { + if c.node == n { + return true + } + } + return false +} + +func newYAMLSource(file string, node *yaml.Node, aliases *aliasChain) (*yamlSource, error) { + s := &yamlSource{file: file, node: node, target: node, aliases: aliases} + loc := s.loc(node) + for s.target.Kind == yaml.AliasNode { + // The same alias may be reached again through another path, which is not a + // cycle; only an alias inside its own expansion is. + if s.aliases.contains(s.target) { + return nil, yamlErrorf(loc, "cyclic reference to anchor %q", s.target.Value) + } + s.aliases = &aliasChain{node: s.target, prev: s.aliases} + s.target = s.target.Alias + } + + switch s.target.Kind { + case yaml.MappingNode: + s.k = KindMap + case yaml.SequenceNode: + s.k = KindSequence + case yaml.ScalarNode: + if err := s.resolveScalar(loc); err != nil { + return nil, err + } + default: + return nil, yamlErrorf(loc, "unknown node kind: %v", s.target.Kind) + } + return s, nil +} + +func yamlErrorf(loc diag.Location, format string, args ...any) error { + return fmt.Errorf("yaml (%s): %s", loc, fmt.Sprintf(format, args...)) +} + +func (s *yamlSource) loc(n *yaml.Node) diag.Location { + return diag.Location{File: s.file, Line: n.Line, Column: n.Column} +} + +func intValue(i64 int64) any { + // Use regular int type instead of int64 if possible. + if i64 >= math.MinInt32 && i64 <= math.MaxInt32 { + return int(i64) + } + return i64 +} + +func (s *yamlSource) resolveScalar(loc diag.Location) error { + n := s.target + switch st := n.ShortTag(); st { + case "!!str": + s.k, s.v = KindString, n.Value + case "!!bool": + switch strings.ToLower(n.Value) { + case "true": + s.k, s.v = KindBool, true + case "false": + s.k, s.v = KindBool, false + default: + return yamlErrorf(loc, "invalid bool value: %v", n.Value) + } + case "!!int": + // Try to parse the integer value in base 10. Trim leading zeros to avoid + // octal parsing of the "0" prefix (YAML 1.2 spec example 2.19). + i64, err := strconv.ParseInt(strings.TrimLeft(n.Value, "0"), 10, 64) + if err != nil { + // Let ParseInt figure out the base. + i64, err = strconv.ParseInt(n.Value, 0, 64) + } + if err != nil { + return yamlErrorf(loc, "invalid int value: %v", n.Value) + } + s.k, s.v = KindInt, intValue(i64) + case "!!float": + f64, err := strconv.ParseFloat(n.Value, 64) + if err == nil { + s.k, s.v = KindFloat, f64 + break + } + // Deal with infinity prefixes. + v := strings.ToLower(n.Value) + switch { + case strings.HasPrefix(v, "+"): + v = strings.TrimPrefix(v, "+") + f64 = math.Inf(1) + case strings.HasPrefix(v, "-"): + v = strings.TrimPrefix(v, "-") + f64 = math.Inf(-1) + default: + f64 = math.Inf(1) + } + switch v { + case ".inf": + s.k, s.v = KindFloat, f64 + case ".nan": + s.k, s.v = KindFloat, math.NaN() + default: + return yamlErrorf(loc, "invalid float value: %v", n.Value) + } + case "!!null": + s.k = KindNil + case "!!timestamp": + if !isTimestamp(n.Value) { + return yamlErrorf(loc, "invalid timestamp value: %v", n.Value) + } + // Timestamps are kept as the string they were written as. + s.k, s.v = KindTime, n.Value + default: + return yamlErrorf(loc, "unknown tag: %v", st) + } + return nil +} + +func isTimestamp(s string) bool { + for _, layout := range []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only + } { + if _, err := time.Parse(layout, s); err == nil { + return true + } + } + return false +} + +func (s *yamlSource) kind() Kind { + return s.k +} + +func (s *yamlSource) locations() []diag.Location { + // An alias has the location of the anchored value. + locs := []diag.Location{s.loc(s.target)} + if s.mapLocations != nil { + return s.mapLocations(s.path, locs) + } + return locs +} + +func (s *yamlSource) anchor() bool { + return s.node.Anchor != "" || s.target.Anchor != "" +} + +func (s *yamlSource) scalar() any { + return s.v +} + +// child returns the source of n, the value at path below this one (computed by path, +// only when locations are mapped). +func (s *yamlSource) child(n *yaml.Node, path func() *structpath.PathNode) (*yamlSource, error) { + c, err := newYAMLSource(s.file, n, s.aliases) + if err != nil { + return nil, err + } + if s.mapLocations != nil { + c.mapLocations = s.mapLocations + c.path = path() + } + return c, nil +} + +func (s *yamlSource) pairs() ([]sourcePair, error) { + node := s.target + loc := s.loc(node) + var merge *yaml.Node + var acc pairSet + for i := 0; i < len(node.Content); i += 2 { + key := node.Content[i] + val := node.Content[i+1] + + if key.Kind != yaml.ScalarNode { + return nil, yamlErrorf(loc, "key is not a scalar") + } + + switch st := key.ShortTag(); st { + case "!!str": + case "!!null": + // A literal unquoted "null" is treated as a null value by the YAML parser. + // However, when used as a key, it is treated as the string "null". + case "!!merge": + if merge != nil { + // The YAML merge key spec allows a single '<<' key per mapping. + return nil, &LocationError{ + Loc: s.loc(key), + Summary: "duplicate YAML merge key ('<<') is not allowed; to merge multiple maps, use a sequence: '<<: [*anchor1, *anchor2]'", + } + } + merge = val + continue + default: + return nil, yamlErrorf(loc, "invalid key tag: %v", st) + } + + v, err := s.child(val, func() *structpath.PathNode { return structpath.NewStringKey(s.path, key.Value) }) + if err != nil { + return nil, err + } + acc.set(sourcePair{key: key.Value, keyLoc: s.loc(key), value: v}) + } + + if merge == nil { + return acc.pairs, nil + } + + mloc := s.loc(merge) + merr := yamlErrorf(mloc, "map merge requires map or sequence of maps as the value") + + var mnodes []*yaml.Node + switch merge.Kind { + case yaml.SequenceNode: + mnodes = merge.Content + case yaml.AliasNode: + mnodes = []*yaml.Node{merge} + default: + return nil, merr + } + + // Merged maps come first; the entries of this mapping take precedence. + var out pairSet + for _, n := range mnodes { + // The merged mapping's values are at the paths of this mapping's. + ms, err := s.child(n, func() *structpath.PathNode { return s.path }) + if err != nil { + return nil, err + } + if ms.kind() != KindMap { + return nil, merr + } + mps, err := ms.pairs() + if err != nil { + return nil, err + } + for _, p := range mps { + out.set(p) + } + } + for _, p := range acc.pairs { + out.set(p) + } + return out.pairs, nil +} + +// setPair sets the pair in pairs: an existing key keeps its position and key location. +// pairSet is a list of pairs with unique keys, in the order the keys were first set. +type pairSet struct { + pairs []sourcePair + index map[string]int +} + +// pairSetIndexMin is the size from which a pairSet indexes its keys; smaller sets (most +// mappings) are searched linearly, which allocates nothing. +const pairSetIndexMin = 16 + +// set adds p, or replaces the value of the pair with the same key (keeping its key +// location and position). +func (s *pairSet) set(p sourcePair) { + if i, ok := s.find(p.key); ok { + s.pairs[i].value = p.value + return + } + if s.index != nil { + s.index[p.key] = len(s.pairs) + } + s.pairs = append(s.pairs, p) + if s.index == nil && len(s.pairs) >= pairSetIndexMin { + s.index = make(map[string]int, 2*len(s.pairs)) + for i, q := range s.pairs { + s.index[q.key] = i + } + } +} + +func (s *pairSet) find(key string) (int, bool) { + if s.index != nil { + i, ok := s.index[key] + return i, ok + } + for i := range s.pairs { + if s.pairs[i].key == key { + return i, true + } + } + return 0, false +} + +func (s *yamlSource) elems() ([]source, error) { + var out []source + for i, n := range s.target.Content { + v, err := s.child(n, func() *structpath.PathNode { return structpath.NewIndex(s.path, i) }) + if err != nil { + return nil, err + } + out = append(out, v) + } + return out, nil +} diff --git a/libs/structs/structvar/kind.go b/libs/structs/structvar/kind.go new file mode 100644 index 00000000000..a0775f98afe --- /dev/null +++ b/libs/structs/structvar/kind.go @@ -0,0 +1,44 @@ +package structvar + +import "fmt" + +// Kind is the kind of a value in the configuration tree. +type Kind int + +const ( + // Invalid is the zero value of Kind. + KindInvalid Kind = iota + KindMap + KindSequence + KindString + KindBool + KindInt + KindFloat + KindTime + KindNil +) + +func (k Kind) String() string { + switch k { + case KindInvalid: + return "invalid" + case KindMap: + return "map" + case KindSequence: + return "sequence" + case KindString: + return "string" + case KindBool: + return "bool" + case KindInt: + return "int" + case KindFloat: + return "float" + case KindTime: + return "time" + case KindNil: + return "nil" + default: + panic(fmt.Sprintf("invalid kind value: %d", k)) + } +} diff --git a/libs/structs/structvar/locations.go b/libs/structs/structvar/locations.go new file mode 100644 index 00000000000..e0e03d3edc6 --- /dev/null +++ b/libs/structs/structvar/locations.go @@ -0,0 +1,292 @@ +package structvar + +import ( + "iter" + "maps" + "slices" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" +) + +// Locations records the source locations of the values of a typed value, as a tree +// that follows the value: one node per map key or sequence index. +// +// Locations is immutable: operations that change it return a new tree that shares +// unchanged subtrees, so copies of a struct holding one can share it safely. +// A nil *Locations is an empty tree. +type Locations struct { + locs []diag.Location + keys map[string]*Locations + elems []*Locations +} + +// pathComponent is a key or an index of a path. +type pathComponent struct { + key string + index int + isKey bool +} + +// components returns the keys and indices of p. +func components(p *structpath.PathNode) []pathComponent { + var out []pathComponent + for _, n := range p.AsSlice() { + if k, ok := n.StringKey(); ok { + out = append(out, pathComponent{key: k, isKey: true}) + } else if i, ok := n.Index(); ok { + out = append(out, pathComponent{index: i}) + } + } + return out +} + +func (l *Locations) child(c pathComponent) *Locations { + if l == nil { + return nil + } + if c.isKey { + return l.keys[c.key] + } + if c.index < 0 || c.index >= len(l.elems) { + return nil + } + return l.elems[c.index] +} + +// Key returns the locations of map key or struct field k. +func (l *Locations) Key(k string) *Locations { + return l.child(pathComponent{key: k, isKey: true}) +} + +// Index returns the locations of sequence element i. +func (l *Locations) Index(i int) *Locations { + return l.child(pathComponent{index: i}) +} + +// KeyItems returns the keys of l that have locations recorded below them, and their trees. +func (l *Locations) KeyItems() iter.Seq2[string, *Locations] { + return func(yield func(string, *Locations) bool) { + if l == nil { + return + } + for k, c := range l.keys { + if c != nil && !yield(k, c) { + return + } + } + } +} + +// Elems returns the sequence indices of l that have locations recorded below them, and +// their trees. +func (l *Locations) Elems() iter.Seq2[int, *Locations] { + return func(yield func(int, *Locations) bool) { + if l == nil { + return + } + for i, c := range l.elems { + if c != nil && !yield(i, c) { + return + } + } + } +} + +// Get returns the locations recorded for the value itself. +func (l *Locations) Get() []diag.Location { + if l == nil { + return nil + } + return slices.Clone(l.locs) +} + +// Exact returns the locations of the value at path. Values without recorded locations +// (e.g. set by code) have none. +func (l *Locations) Exact(path *structpath.PathNode) []diag.Location { + return l.lookup(components(path)).Get() +} + +// Nearest returns the locations of the value at path or, if it has none, of the +// closest ancestor that has some. Use it to point diagnostics at the most specific +// place there is, not to decide where a value was defined. +func (l *Locations) Nearest(path *structpath.PathNode) []diag.Location { + var nearest []diag.Location + n := l + for _, c := range components(path) { + if n == nil { + break + } + if len(n.locs) > 0 { + nearest = n.locs + } + n = n.child(c) + } + if n != nil && len(n.locs) > 0 { + nearest = n.locs + } + return slices.Clone(nearest) +} + +func (l *Locations) lookup(path []pathComponent) *Locations { + n := l + for _, c := range path { + n = n.child(c) + if n == nil { + return nil + } + } + return n +} + +// Sub returns the locations of the value at path and below. +func (l *Locations) Sub(path *structpath.PathNode) *Locations { + return l.lookup(components(path)) +} + +// With returns a copy of l where the locations of the value at path and below are sub. +func (l *Locations) With(path *structpath.PathNode, sub *Locations) *Locations { + return l.with(components(path), sub) +} + +func (l *Locations) with(path []pathComponent, sub *Locations) *Locations { + if len(path) == 0 { + return sub + } + child := l.child(path[0]).with(path[1:], sub) + return l.withChild(path[0], child) +} + +func (l *Locations) clone() *Locations { + if l == nil { + return &Locations{} + } + c := *l + c.keys = maps.Clone(l.keys) + c.elems = slices.Clone(l.elems) + return &c +} + +func (l *Locations) withChild(c pathComponent, child *Locations) *Locations { + out := l.clone() + out.setChild(c, child) + return out +} + +// setChild sets the child c of l in place. Only use it on a node the caller owns, +// e.g. one just returned by [Locations.WithLocations], which builds a fresh node. +func (l *Locations) setChild(c pathComponent, child *Locations) { + if c.isKey { + if child == nil { + delete(l.keys, c.key) + return + } + if l.keys == nil { + l.keys = map[string]*Locations{} + } + l.keys[c.key] = child + return + } + if c.index < 0 { + return + } + for len(l.elems) <= c.index { + l.elems = append(l.elems, nil) + } + l.elems[c.index] = child +} + +// WithLocations returns a copy of l where the value itself has the locations locs. +func (l *Locations) WithLocations(locs []diag.Location) *Locations { + c := l.clone() + c.locs = slices.Clone(locs) + return c +} + +// WithSequence returns a copy of l where the elements of the sequence at path are +// rebuilt from the old ones: new element i has the locations of old elements sources[i] +// merged (see [MergeLocations]). Use it after reordering, merging, splitting or +// dropping sequence elements. +func (l *Locations) WithSequence(path *structpath.PathNode, sources [][]int) *Locations { + p := components(path) + n := l.lookup(p) + if n == nil { + return l + } + c := n.clone() + c.elems = make([]*Locations, len(sources)) + for i, src := range sources { + var merged *Locations + for j, k := range src { + if j == 0 { + merged = n.Index(k) + continue + } + merged = MergeLocations(merged, n.Index(k)) + } + c.elems[i] = merged + } + return l.with(p, c) +} + +// MergeLocations returns the locations of the value obtained by merging the value +// described by b into the one described by a, following merge.Merge: maps and +// sequences keep a's locations and accumulate b's, other values take b's and +// accumulate a's. +func MergeLocations(a, b *Locations) *Locations { + switch { + case a == nil: + return b + case b == nil: + return a + } + isComposite := len(a.keys) > 0 || len(a.elems) > 0 || len(b.keys) > 0 || len(b.elems) > 0 + if !isComposite { + return b.WithLocations(concatLocations(b.locs, a.locs)) + } + c := a.clone() + c.locs = concatLocations(a.locs, b.locs) + for k, bv := range b.keys { + if c.keys == nil { + c.keys = map[string]*Locations{} + } + c.keys[k] = MergeLocations(a.keys[k], bv) + } + c.elems = append(slices.Clone(a.elems), b.elems...) + return c +} + +func concatLocations(a, b []diag.Location) []diag.Location { + if len(b) == 0 { + return a + } + return append(slices.Clone(a), b...) +} + +// Map returns a copy of l where the locations of every value are replaced by fn(path, locations). +func (l *Locations) Map(fn func(path *structpath.PathNode, locs []diag.Location) []diag.Location) *Locations { + return l.mapLocations(nil, fn) +} + +func (l *Locations) mapLocations(path *structpath.PathNode, fn func(*structpath.PathNode, []diag.Location) []diag.Location) *Locations { + if l == nil { + return nil + } + c := l.clone() + c.locs = fn(path, slices.Clone(l.locs)) + for k, v := range c.keys { + c.keys[k] = v.mapLocations(structpath.NewStringKey(path, k), fn) + } + for i, v := range c.elems { + c.elems[i] = v.mapLocations(structpath.NewIndex(path, i), fn) + } + return c +} + +// child returns the path of this component below prefix. +func (c pathComponent) child(prefix *structpath.PathNode) *structpath.PathNode { + if c.isKey { + return structpath.NewStringKey(prefix, c.key) + } + return structpath.NewIndex(prefix, c.index) +} diff --git a/libs/structs/structvar/lookup.go b/libs/structs/structvar/lookup.go new file mode 100644 index 00000000000..b4f3e3f1465 --- /dev/null +++ b/libs/structs/structvar/lookup.go @@ -0,0 +1,157 @@ +package structvar + +import ( + "fmt" + "reflect" + "slices" + + "github.com/databricks/cli/libs/structs/structpath" +) + +// LookupError is returned by [View.LookupWithDefaults] when the path cannot be followed. +type LookupError struct { + msg string +} + +func (e *LookupError) Error() string { + return e.msg +} + +// KeyNotFoundError is returned by [View.LookupWithDefaults] for a missing key. +type KeyNotFoundError struct { + Path *structpath.PathNode + Suggestions []string +} + +func (e *KeyNotFoundError) Error() string { + return fmt.Sprintf("key not found at %q%s", e.Path, DidYouMean(e.Suggestions)) +} + +func indexedKind(c pathComponent) string { + if c.isKey { + return "map" + } + return "sequence" +} + +// LookupWithDefaults returns the value at path. Fields that are declared in the type +// but not set resolve to their zero value: e.g. ${bundle.git.origin_url} resolves to an +// empty string if a bundle isn't located in a Git repository (yet). +func (x View) LookupWithDefaults(path *structpath.PathNode) (View, error) { + var p *structpath.PathNode + for _, c := range components(path) { + p = c.child(p) + kind := x.Kind() + if c.isKey { + switch kind { + case KindMap: + case KindNil: + return View{}, &LookupError{fmt.Sprintf("expected a %s to index %q, found nil", indexedKind(c), p)} + default: + return View{}, &LookupError{fmt.Sprintf("expected a map to index %q, found %s", p, kind)} + } + child := x.Get(c.key) + if !child.IsValid() { + zero, ok := zeroField(x, c.key) + if !ok { + return View{}, &KeyNotFoundError{Path: p, Suggestions: SuggestKeys(lookupKeys(x), c.key)} + } + child = zero + } + x = child + continue + } + + switch kind { + case KindSequence: + case KindNil: + return View{}, &LookupError{fmt.Sprintf("expected a %s to index %q, found nil", indexedKind(c), p)} + default: + return View{}, &LookupError{fmt.Sprintf("expected a sequence to index %q, found %s", p, kind)} + } + child := x.Index(c.index) + if !child.IsValid() { + return View{}, &LookupError{fmt.Sprintf("index out of bounds at %q", p)} + } + x = child + } + return x, nil +} + +// structType returns the struct type of the value, if it is a struct. +func structType(x View) (reflect.Type, bool) { + t := x.Reflect().Type() + for t.Kind() == reflect.Pointer || t.Kind() == reflect.Interface { + if t.Kind() == reflect.Interface { + v := x.Reflect() + for v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface { + if v.IsNil() { + return nil, false + } + v = v.Elem() + } + t = v.Type() + continue + } + t = t.Elem() + } + return t, t.Kind() == reflect.Struct && !IsSDKNativeType(t) +} + +// fillableFieldType returns the type of the struct field with JSON name k, if a field of +// that type is given a zero value when it is not set (see convert.IncludeMissingFields). +func fillableFieldType(t reflect.Type, k string) (reflect.Type, bool) { + info := GetStructInfo(t) + index, ok := info.Fields[k] + if !ok { + return nil, false + } + ft := t.FieldByIndex(index).Type + for ft.Kind() == reflect.Pointer { + ft = ft.Elem() + } + switch ft.Kind() { + case reflect.Struct, reflect.Map, reflect.Slice, reflect.String, reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64: + return ft, true + default: + return nil, false + } +} + +// zeroField returns the zero value of the struct field k of x that is not set. +func zeroField(x View, k string) (View, bool) { + t, ok := structType(x) + if !ok { + return View{}, false + } + ft, ok := fillableFieldType(t, k) + if !ok { + return View{}, false + } + zero := reflect.New(ft) + switch ft.Kind() { + case reflect.Map: + zero.Elem().Set(reflect.MakeMap(ft)) + case reflect.Slice: + zero.Elem().Set(reflect.MakeSlice(ft, 0, 0)) + default: + } + return NewView(zero.Interface(), nil, nil), true +} + +// lookupKeys returns the keys a lookup in x can find, for "did you mean" suggestions. +func lookupKeys(x View) []string { + var keys []string + for k := range x.MapItems() { + keys = append(keys, k) + } + if t, ok := structType(x); ok { + for _, k := range GetStructInfo(t).FieldNames { + if _, ok := fillableFieldType(t, k); ok && !slices.Contains(keys, k) { + keys = append(keys, k) + } + } + } + return keys +} diff --git a/libs/structs/structvar/merge.go b/libs/structs/structvar/merge.go new file mode 100644 index 00000000000..a80d50c8010 --- /dev/null +++ b/libs/structs/structvar/merge.go @@ -0,0 +1,225 @@ +package structvar + +import ( + "fmt" + "maps" + "reflect" + "slices" + + "github.com/databricks/cli/libs/structs/structpath" +) + +// Merge merges the value described by src into the value at path, with the semantics +// of merge.Merge: maps are merged recursively, sequences are concatenated, primitive +// values are replaced, and a nil value on either side yields the other one. +// Locations accumulate like merge.Merge accumulates them. A failed merge changes nothing. +func (sv *StructVar) Merge(path *structpath.PathNode, src View) error { + dstView := sv.View().Lookup(path) + if err := CheckMerge(dstView, src); err != nil { + return err + } + + m := merger{refs: maps.Clone(sv.Refs)} + var locs *Locations + err := update(reflect.ValueOf(sv.Value), components(path), func(dst reflect.Value) (bool, error) { + dstView.v = dst + var err error + locs, err = m.merge(dst, dstView, src, path) + return isZeroScalar(src), err + }) + if err != nil { + return err + } + sv.Refs = m.refs + sv.Locations = sv.Locations.With(path, locs) + return nil +} + +// isZeroScalar reports whether src is a present zero scalar, which must be kept as an +// explicitly set value. +func isZeroScalar(src View) bool { + k := src.Kind() + return k != KindNil && k != KindMap && k != KindSequence && src.isZero() +} + +// isNil reports whether v is a nil pointer, map, slice or interface. +func isNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Interface: + return v.IsNil() + default: + return false + } +} + +// CheckMerge returns the error [StructVar.Merge] would return for merging src into dst. +func CheckMerge(dst, src View) error { + ak, bk := dst.Kind(), src.Kind() + if ak == KindInvalid || ak == KindNil || bk == KindInvalid || bk == KindNil { + return nil + } + switch ak { + case KindMap: + if bk != KindMap { + return fmt.Errorf("cannot merge map with %s", bk) + } + for k, sc := range src.MapItems() { + if err := CheckMerge(dst.Get(k), sc); err != nil { + return err + } + } + return nil + case KindSequence: + if bk != KindSequence { + return fmt.Errorf("cannot merge sequence with %s", bk) + } + return nil + default: + if ak != bk { + return fmt.Errorf("cannot merge %s with %s", ak, bk) + } + return nil + } +} + +type merger struct { + refs map[string]string +} + +// assign sets dst to src (see [StructVar.Assign]) and returns its locations. +func (m *merger) assign(dst reflect.Value, src View, path *structpath.PathNode) (*Locations, error) { + var d decoder + n, ok, err := d.decode(dst, viewSource{src}, path) + if err != nil { + return nil, err + } + if !ok { + dst.SetZero() + } + m.refs = n.refs(path, withoutRefs(m.refs, path)) + return n.locations(), nil +} + +// merge merges src into dst (settable; dstView is its view, invalid if absent). +func (m *merger) merge(dst reflect.Value, dstView, src View, path *structpath.PathNode) (*Locations, error) { + ak, bk := dstView.Kind(), src.Kind() + switch { + case bk == KindInvalid: + return dstView.loc, nil + case ak == KindInvalid: + return m.assign(dst, src, path) + case ak == KindNil: + locs, err := m.assign(dst, src, path) + return locs.WithLocations(concatLocations(src.loc.Get(), dstView.loc.Get())), err + case bk == KindNil: + return dstView.loc.WithLocations(concatLocations(dstView.loc.Get(), src.loc.Get())), nil + } + + switch ak { + case KindMap: + out := dstView.loc.WithLocations(concatLocations(dstView.loc.Get(), src.loc.Get())) + v := derefAlloc(dst) + for k, sc := range src.MapItems() { + c := pathComponent{key: k, isKey: true} + cp := structpath.NewStringKey(path, k) + var locs *Locations + err := updateChild(v, c, func(child reflect.Value) (bool, error) { + childView := dstView.Get(k) + childView.v = child + var err error + locs, err = m.merge(child, childView, sc, cp) + // A zero scalar, or a null that left the field empty, was written + // explicitly and must stay set. + return isZeroScalar(sc) || sc.Kind() == KindNil && isNil(child), err + }) + if err != nil { + return nil, err + } + out.setChild(c, locs) + } + return out, nil + case KindSequence: + out := dstView.loc.WithLocations(concatLocations(dstView.loc.Get(), src.loc.Get())) + v := derefAlloc(dst) + holder := v + if v.Kind() == reflect.Interface { + // A sequence held in an interface (e.g. a complex variable default) is + // appended to as a copy and stored back. + v = reflect.New(holder.Elem().Type()).Elem() + v.Set(holder.Elem()) + } + for _, sc := range src.Sequence() { + i := v.Len() + e := reflect.New(v.Type().Elem()).Elem() + locs, err := m.assign(e, sc, structpath.NewIndex(path, i)) + if err != nil { + return nil, err + } + v.Set(reflect.Append(v, e)) + out.setChild(pathComponent{index: i}, locs) + } + if holder.Kind() == reflect.Interface { + holder.Set(v) + } + return out, nil + default: + locs, err := m.assign(dst, src, path) + return locs.WithLocations(concatLocations(src.loc.Get(), dstView.loc.Get())), err + } +} + +// MergeElementsByKey merges the elements of the sequence at path that have the same +// key, like merge.ElementsByKey: elements are merged into the first one with the same +// key (in order), the key field of every element is set to its key, and the keys are +// sorted if sortKeys is set. +func (sv *StructVar) MergeElementsByKey(path *structpath.PathNode, keyField string, keyFn func(View) string, sortKeys bool) error { + seq := sv.View().Lookup(path) + if seq.Kind() != KindSequence { + return nil + } + + var keys []string + groups := map[string][]int{} + for i, e := range seq.Sequence() { + k := keyFn(e.Get(keyField)) + if _, ok := groups[k]; !ok { + keys = append(keys, k) + } + groups[k] = append(groups[k], i) + } + if len(keys) == 0 { + return nil + } + if sortKeys { + slices.Sort(keys) + } + + // Build the merged sequence separately: the first element of each group is + // assigned, the others are merged into it. + sliceType := seq.Reflect().Type() + for sliceType.Kind() == reflect.Pointer { + sliceType = sliceType.Elem() + } + tmp := &StructVar{Value: reflect.New(sliceType).Interface()} + for i, k := range keys { + elem := structpath.NewIndex(nil, i) + for j, idx := range groups[k] { + var err error + if j == 0 { + _, err = tmp.Assign(elem, seq.Index(idx)) + } else { + err = tmp.Merge(elem, seq.Index(idx)) + } + if err != nil { + return err + } + } + // The key field holds the key (e.g. a normalized one), keeping its location. + if err := tmp.Set(structpath.NewStringKey(elem, keyField), k); err != nil { + return err + } + } + tmp.Locations = tmp.Locations.WithLocations(seq.loc.Get()) + _, err := sv.Assign(path, tmp.View()) + return err +} diff --git a/libs/structs/structvar/override.go b/libs/structs/structvar/override.go new file mode 100644 index 00000000000..f44b5e08f5c --- /dev/null +++ b/libs/structs/structvar/override.go @@ -0,0 +1,163 @@ +package structvar + +import ( + "errors" + "fmt" + "reflect" + + "github.com/databricks/cli/libs/structs/structpath" +) + +// ErrOverrideUndoDelete can be returned by [OverrideVisitor.VisitDelete] to keep the +// deleted value. +var ErrOverrideUndoDelete = errors.New("undo delete operation") + +// OverrideVisitor is notified of the differences found by [PlanOverride]. +// Any error aborts the override. +type OverrideVisitor struct { + VisitDelete func(path *structpath.PathNode, left View) error + VisitInsert func(path *structpath.PathNode, right View) error + VisitUpdate func(path *structpath.PathNode, left, right View) error +} + +// OverridePlan is the result of [PlanOverride]; apply it with [StructVar.Override]. +type OverridePlan struct { + src View + locs *Locations + undos []undo +} + +type undo struct { + path *structpath.PathNode + value *StructVar +} + +// PlanOverride compares dst with src like merge.Override and reports the differences +// to the visitor. Applying the plan replaces dst with src, except that values unchanged +// in src keep their locations in dst, and values whose deletion the visitor undid are kept. +func PlanOverride(dst, src View, visitor OverrideVisitor) (*OverridePlan, error) { + plan := &OverridePlan{src: src} + locs, err := plan.override(nil, dst, src, visitor) + if err != nil { + return nil, err + } + plan.locs = locs + return plan, nil +} + +func (p *OverridePlan) override(path *structpath.PathNode, left, right View, visitor OverrideVisitor) (*Locations, error) { + lk, rk := left.Kind(), right.Kind() + if lk != rk { + return right.loc, visitor.VisitUpdate(path, left, right) + } + + switch lk { + case KindMap: + out := (*Locations)(nil).WithLocations(left.loc.Get()) + for k, lv := range left.MapItems() { + if right.Get(k).IsValid() { + continue + } + kp := structpath.NewStringKey(path, k) + err := visitor.VisitDelete(kp, lv) + if errors.Is(err, ErrOverrideUndoDelete) { + p.keep(kp, lv) + out.setChild(pathComponent{key: k, isKey: true}, lv.loc) + } else if err != nil { + return nil, err + } + } + for k, rv := range right.MapItems() { + kp := structpath.NewStringKey(path, k) + locs := rv.loc + if lv := left.Get(k); lv.IsValid() { + var err error + locs, err = p.override(kp, lv, rv, visitor) + if err != nil { + return nil, err + } + } else if err := visitor.VisitInsert(kp, rv); err != nil { + return nil, err + } + out.setChild(pathComponent{key: k, isKey: true}, locs) + } + return out, nil + case KindSequence: + out := (*Locations)(nil).WithLocations(left.loc.Get()) + var ls, rs []View + for _, v := range left.Sequence() { + ls = append(ls, v) + } + for _, v := range right.Sequence() { + rs = append(rs, v) + } + n := 0 + for i := range min(len(ls), len(rs)) { + locs, err := p.override(structpath.NewIndex(path, i), ls[i], rs[i], visitor) + if err != nil { + return nil, err + } + out.setChild(pathComponent{index: n}, locs) + n++ + } + for i := len(ls); i < len(rs); i++ { + if err := visitor.VisitInsert(structpath.NewIndex(path, i), rs[i]); err != nil { + return nil, err + } + out.setChild(pathComponent{index: n}, rs[i].loc) + n++ + } + for i := len(rs); i < len(ls); i++ { + err := visitor.VisitDelete(structpath.NewIndex(path, i), ls[i]) + if errors.Is(err, ErrOverrideUndoDelete) { + p.keep(structpath.NewIndex(path, n), ls[i]) + out.setChild(pathComponent{index: n}, ls[i].loc) + n++ + } else if err != nil { + return nil, err + } + } + return out, nil + case KindNil: + return left.loc, nil + case KindString, KindBool, KindInt, KindFloat: + if left.AsAny() == right.AsAny() || sameNumber(left, right) { + return left.loc, nil + } + return right.loc, visitor.VisitUpdate(path, left, right) + default: + return nil, fmt.Errorf("unexpected kind %s at %s", lk, path) + } +} + +func sameNumber(a, b View) bool { + if ai, ok := a.AsInt(); ok { + bi, ok := b.AsInt() + return ok && ai == bi + } + return false +} + +// keep records that the value at path (in the result) is the kept left value. It is +// copied now, because applying the plan overwrites the left side. +func (p *OverridePlan) keep(path *structpath.PathNode, v View) { + cp := &StructVar{Value: reflect.New(v.v.Type()).Interface()} + _, _ = cp.Assign(nil, v) + p.undos = append(p.undos, undo{path: path, value: cp}) +} + +// Override replaces sv.Value with the result of plan (see [PlanOverride]). +func (sv *StructVar) Override(plan *OverridePlan) error { + sv.Refs = nil + sv.Locations = nil + if _, err := sv.Assign(nil, plan.src); err != nil { + return err + } + for _, u := range plan.undos { + if _, err := sv.Assign(u.path, u.value.View()); err != nil { + return err + } + } + sv.Locations = plan.locs + return nil +} diff --git a/libs/structs/structvar/parity_walk_test.go b/libs/structs/structvar/parity_walk_test.go new file mode 100644 index 00000000000..f3111c64342 --- /dev/null +++ b/libs/structs/structvar/parity_walk_test.go @@ -0,0 +1,71 @@ +package structvar_test + +import ( + "testing" + + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestWalkVisitsParentsBeforeChildren(t *testing.T) { + root := config.Root{ + Bundle: config.Bundle{Name: "x"}, + Sync: config.Sync{Include: []string{"a", "b"}}, + } + + var visited []string + err := structvar.Walk(root.View(), func(p *structpath.PathNode, v structvar.View) error { + if s, ok := v.AsString(); ok { + visited = append(visited, p.String()+"="+s) + } + return nil + }) + require.NoError(t, err) + assert.Equal(t, []string{"bundle.name=x", "sync.include[0]=a", "sync.include[1]=b"}, visited) +} + +func TestWalkVisitsAllValues(t *testing.T) { + root, diags := config.LoadFromBytes("test.yml", []byte(` +resources: + jobs: + j: + name: n + max_concurrent_runs: ${var.x} + tags: + a: b +`)) + require.NoError(t, diags.Error()) + + var visited []string + err := structvar.Walk(root.View().Lookup(structpath.MustParsePath("resources.jobs.j")), func(p *structpath.PathNode, _ structvar.View) error { + visited = append(visited, p.String()) + return nil + }) + require.NoError(t, err) + assert.Equal(t, []string{"", "max_concurrent_runs", "name", "tags", "tags.a"}, visited) +} + +func TestSetReference(t *testing.T) { + root, diags := config.LoadFromBytes("test.yml", []byte(` +resources: + jobs: + j: + name: n + max_concurrent_runs: ${var.x} +`)) + require.NoError(t, diags.Error()) + + path := structpath.MustParsePath("resources.jobs.j.max_concurrent_runs") + require.NoError(t, root.SetReference(path, "${var.y}")) + s, ok := root.View().Lookup(path).AsString() + require.True(t, ok) + assert.Equal(t, "${var.y}", s) + + // A string field holds the reference itself. + name := structpath.MustParsePath("resources.jobs.j.name") + require.NoError(t, root.SetReference(name, "${var.y}")) + assert.Equal(t, "${var.y}", root.Resources.Jobs["j"].Name) +} diff --git a/libs/structs/structvar/ref.go b/libs/structs/structvar/ref.go new file mode 100644 index 00000000000..b619ae096d2 --- /dev/null +++ b/libs/structs/structvar/ref.go @@ -0,0 +1,165 @@ +package structvar + +import ( + "fmt" + "regexp" + "strings" + + "github.com/databricks/cli/libs/structs/structpath" +) + +// BaseVarDef matches a single dot-separated path segment in ${...} references. +// A segment may have leading underscores before the first letter, and may contain +// letters, digits, hyphens and underscores after that. +// Keep in sync with _base_var_def in python/databricks/bundles/core/_transform.py. +// Behavioral parity is enforced by testdata/reference_vectors.json. +const BaseVarDef = `_*\p{L}+([-_]*[\p{L}\p{N}]+)*` + +var re = regexp.MustCompile(fmt.Sprintf(`\$\{(%s(\.%s(\[[0-9]+\])*)*(\[[0-9]+\])*)\}`, BaseVarDef, BaseVarDef)) + +// Ref represents the variable references in a string. +type Ref struct { + // The string. + Str string + + // Matches of the variable reference in the string. + Matches [][]string + + // Byte offsets [start, end) of each entry in Matches within Str. Interpolation + // must substitute by offset: an escaped occurrence of the same reference text + // may appear earlier in Str, and a search-based replacement would hit that one. + Spans [][2]int +} + +// NewRef returns a new Ref if s contains one or more variable references. +// +// A reference preceded by "$" is treated as an escaped literal and is not +// included in the returned Ref. For example, "$${foo}" contains no references +// because the "${foo}" part is escaped by the leading "$". +// +// Examples of a valid variable references: +// - "${a.b}" +// - "${a.b.c}" +// - "${a.b[0].c}" +// - "${a} ${b} ${c}" +func NewRef(s string) (Ref, bool) { + // Find all matches with their byte positions so we can detect escaped references. + indices := re.FindAllStringSubmatchIndex(s, -1) + if len(indices) == 0 { + return Ref{}, false + } + + // Convert position-based matches to string matches, filtering out references + // escaped with a leading "$" (e.g. "$${foo}" should not match "${foo}"). + var m [][]string + var spans [][2]int + for _, idx := range indices { + if isEscapedAt(s, idx[0]) { + continue + } + match := make([]string, len(idx)/2) + for i := range len(idx) / 2 { + if idx[2*i] >= 0 { + match[i] = s[idx[2*i]:idx[2*i+1]] + } + } + m = append(m, match) + spans = append(spans, [2]int{idx[0], idx[1]}) + } + + if len(m) == 0 { + return Ref{}, false + } + + return Ref{ + Str: s, + Matches: m, + Spans: spans, + }, true +} + +// isEscapedAt reports whether the reference starting at byte offset start in s is +// escaped by an immediately preceding "$". +func isEscapedAt(s string, start int) bool { + return start > 0 && s[start-1] == '$' +} + +// Unescape turns escaped references into literal ones ("$${x}" -> "${x}"). Call it +// when building a value to send to the API: the extra "$" exists only to hide the +// reference from bundle variable resolution. +func Unescape(s string) string { + return strings.ReplaceAll(s, "$${", "${") +} + +// ReplaceRef substitutes every unescaped occurrence of reference (a full "${...}" +// string) in s with value. Escaped occurrences are left as they are, so +// strings.ReplaceAll is not a valid substitute: it would also rewrite the literal. +func ReplaceRef(s, reference, value string) string { + var sb strings.Builder + for pos := 0; ; { + i := strings.Index(s[pos:], reference) + if i < 0 { + sb.WriteString(s[pos:]) + return sb.String() + } + start := pos + i + sb.WriteString(s[pos:start]) + if isEscapedAt(s, start) { + sb.WriteString(reference) + } else { + sb.WriteString(value) + } + pos = start + len(reference) + } +} + +// IsPure returns true if the variable reference contains a single +// variable reference and nothing more. We need this so we can +// interpolate values of non-string types (i.e. it can be substituted). +func (v Ref) IsPure() bool { + // Need single match, equal to the incoming string. + if len(v.Matches) == 0 || len(v.Matches[0]) == 0 { + panic("invalid variable reference; expect at least one match") + } + return v.Matches[0][0] == v.Str +} + +func (v Ref) References() []string { + var out []string + for _, m := range v.Matches { + out = append(out, m[1]) + } + return out +} + +func IsPureVariableReference(s string) bool { + return len(s) > 0 && re.FindString(s) == s +} + +// ContainsVariableReference reports whether s contains at least one reference that +// is not escaped. Callers use it to decide whether a string still needs resolution, +// so an escaped "$${foo}" must not count: it is a literal, and treating it as +// pending leaves the value permanently unresolved. +func ContainsVariableReference(s string) bool { + for _, loc := range re.FindAllStringIndex(s, -1) { + if !isEscapedAt(s, loc[0]) { + return true + } + } + return false +} + +// PureReferenceToPath returns the path s refers to if s is a pure variable reference. +func PureReferenceToPath(s string) (*structpath.PathNode, bool) { + ref, ok := NewRef(s) + if !ok || !ref.IsPure() { + return nil, false + } + + p, err := structpath.ParsePath(ref.References()[0]) + if err != nil { + return nil, false + } + + return p, true +} diff --git a/libs/structs/structvar/ref_test.go b/libs/structs/structvar/ref_test.go new file mode 100644 index 00000000000..107c612f0b3 --- /dev/null +++ b/libs/structs/structvar/ref_test.go @@ -0,0 +1,108 @@ +package structvar + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewRefSpans(t *testing.T) { + // Spans must point at the unescaped occurrence, not the escaped one that + // appears earlier with identical text. + ref, ok := NewRef("$${a.b} ${a.b}") + require.True(t, ok) + require.Len(t, ref.Matches, 1) + require.Len(t, ref.Spans, 1) + assert.Equal(t, "${a.b}", ref.Str[ref.Spans[0][0]:ref.Spans[0][1]]) + assert.Equal(t, 8, ref.Spans[0][0]) +} + +func TestContainsVariableReference(t *testing.T) { + tests := map[string]bool{ + "${a.b}": true, + "x ${a.b} y": true, + "$${a.b}": false, + "$${a.b} $${c.d}": false, + "$${a.b} ${c.d}": true, + "no refs": false, + "": false, + } + for in, want := range tests { + assert.Equal(t, want, ContainsVariableReference(in), "input %q", in) + } +} + +func TestUnescape(t *testing.T) { + assert.Equal(t, "${a.b}", Unescape("$${a.b}")) + assert.Equal(t, "${a} ${b}", Unescape("$${a} $${b}")) + assert.Equal(t, "${a.b}", Unescape("${a.b}"), "already unescaped is unchanged") + assert.Equal(t, "no refs", Unescape("no refs")) +} + +func TestReplaceRef(t *testing.T) { + tests := []struct { + in string + want string + }{ + {"${a.b}", "V"}, + {"$${a.b}", "$${a.b}"}, + {"$${a.b} ${a.b}", "$${a.b} V"}, + {"${a.b} $${a.b}", "V $${a.b}"}, + {"${a.b}-${a.b}", "V-V"}, + {"$${a.b} ${a.b} $${a.b}", "$${a.b} V $${a.b}"}, + {"untouched ${c.d}", "untouched ${c.d}"}, + } + for _, tt := range tests { + assert.Equal(t, tt.want, ReplaceRef(tt.in, "${a.b}", "V"), "input %q", tt.in) + } +} + +func TestPureReferenceToPath(t *testing.T) { + tests := []struct { + name string + input string + expected string + ok bool + }{ + { + name: "simple reference", + input: "${resources.jobs.foo.id}", + expected: "resources.jobs.foo.id", + ok: true, + }, + { + name: "simple reference", + input: "${resources.jobs.foo.tasks[1].env.key}", + expected: "resources.jobs.foo.tasks[1].env.key", + ok: true, + }, + { + name: "complex nested reference", + input: "${var.resources.jobs['my_job'].tasks[0]}", + // we use regex from dyn module which only support integers inside brackets: + // expected: "resources.jobs['my_job'].tasks[0]", + }, + { + name: "not a pure reference", + input: "prefix_${var.field}", + }, + { + name: "not a variable reference", + input: "plain_string", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pathNode, ok := PureReferenceToPath(tt.input) + assert.Equal(t, tt.ok, ok) + if tt.ok { + assert.NotNil(t, pathNode) + assert.Equal(t, tt.expected, pathNode.String()) + } else { + assert.Nil(t, pathNode) + } + }) + } +} diff --git a/libs/structs/structvar/ref_vectors_test.go b/libs/structs/structvar/ref_vectors_test.go new file mode 100644 index 00000000000..ea58cc03fba --- /dev/null +++ b/libs/structs/structvar/ref_vectors_test.go @@ -0,0 +1,68 @@ +package structvar + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type referenceVectorsFile struct { + Vectors []referenceVector `json:"vectors"` +} + +type referenceVector struct { + ID string `json:"id"` + Input string `json:"input"` + Match bool `json:"match"` + References []string `json:"references,omitempty"` + Pure *bool `json:"pure,omitempty"` + Path *string `json:"path,omitempty"` + PathOK *bool `json:"path_ok,omitempty"` +} + +func loadReferenceVectors(t *testing.T) []referenceVector { + t.Helper() + + b, err := os.ReadFile(filepath.Join("testdata", "reference_vectors.json")) + require.NoError(t, err) + + var file referenceVectorsFile + require.NoError(t, json.Unmarshal(b, &file)) + require.NotEmpty(t, file.Vectors) + + return file.Vectors +} + +func TestReferenceVectors(t *testing.T) { + for _, v := range loadReferenceVectors(t) { + t.Run(v.ID, func(t *testing.T) { + ref, ok := NewRef(v.Input) + assert.Equal(t, v.Match, ok, "NewRef match") + + if v.Match { + require.NotEmpty(t, ref.References()) + if v.References != nil { + assert.Equal(t, v.References, ref.References(), "References") + } + } + + if v.Pure != nil { + assert.Equal(t, *v.Pure, IsPureVariableReference(v.Input), "IsPureVariableReference") + } + + switch { + case v.Path != nil: + path, ok := PureReferenceToPath(v.Input) + require.True(t, ok, "PureReferenceToPath") + assert.Equal(t, *v.Path, path.String()) + case v.PathOK != nil: + _, ok := PureReferenceToPath(v.Input) + assert.Equal(t, *v.PathOK, ok, "PureReferenceToPath") + } + }) + } +} diff --git a/libs/structs/structvar/resolve.go b/libs/structs/structvar/resolve.go new file mode 100644 index 00000000000..3cb8ec44683 --- /dev/null +++ b/libs/structs/structvar/resolve.go @@ -0,0 +1,200 @@ +package structvar + +import ( + "errors" + "fmt" + "maps" + "slices" + "strings" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" +) + +// ErrSkipResolution is returned by a [Lookup] to leave a reference in place. +var ErrSkipResolution = errors.New("skip resolution") + +// ReferenceError is returned for a reference to a path that does not exist. +// Suggestions are carried as data so callers (which can import libs/diag) format them. +type ReferenceError struct { + Reference string // original reference text, e.g. "var.hst" + Suggestions []string // corrected references, e.g. ["var.host", "var.hosts"] +} + +func (e *ReferenceError) Error() string { + return fmt.Sprintf("reference does not exist: ${%s}", e.Reference) +} + +// Template is a string with variable references and the locations it was written at. +type Template struct { + Value string + Locations []diag.Location +} + +// Lookup returns the value a reference path refers to. It returns +// ErrSkipResolution to leave the reference in place. +type Lookup func(path *structpath.PathNode) (View, error) + +// Resolve resolves the references in templates (keyed by path; the keys appear in +// cycle errors) using lookup, with the semantics of dynvar.Resolve: a referenced value +// that is a reference itself is resolved first, cycles are an error, a pure reference +// is replaced by the referenced value (with the location of the reference), and other +// references are interpolated into the string. It returns the views of the resolved +// values; templates that resolve to themselves are omitted. +func Resolve(templates map[string]Template, lookup Lookup) (map[string]View, error) { + r := resolver{lookup: lookup, lookups: map[string]lookupResult{}} + out := map[string]View{} + + // Resolve in key order, so the cycle detected is deterministic. + for _, key := range slices.Sorted(maps.Keys(templates)) { + t := templates[key] + ref, ok := NewRef(t.Value) + if !ok { + continue + } + v, err := r.resolveRef(ref, t.Locations, []string{key}) + if err != nil { + return nil, err + } + if s, ok := v.AsString(); ok && s == t.Value { + continue + } + out[key] = v + } + return out, nil +} + +type lookupResult struct { + v View + err error +} + +type resolver struct { + lookup Lookup + lookups map[string]lookupResult +} + +// stringView returns a view of the string s with locations locs. +func stringView(s string, locs []diag.Location) View { + return NewView(&s, nil, (*Locations)(nil).WithLocations(locs)) +} + +func (r *resolver) resolveRef(ref Ref, locs []diag.Location, seen []string) (View, error) { + deps := ref.References() + + // Resolve each of the dependencies, then interpolate them in the ref. + resolved := make([]View, len(deps)) + complete := true + + for j, dep := range deps { + // Cycle detection. + if slices.Contains(seen, dep) { + return View{}, fmt.Errorf( + "cycle detected in field resolution: %s", + strings.Join(append(seen, dep), " -> "), + ) + } + + v, err := r.resolveKey(dep, append(seen, dep)) + + // If we should skip resolution of this key, index j holds an invalid view. + if errors.Is(err, ErrSkipResolution) { + complete = false + continue + } else if err != nil { + return View{}, err + } + + resolved[j] = v + } + + // A pure reference is replaced by the value, which keeps its type. It takes the + // location of the reference, so relative paths resolve relative to where a + // variable is used, not where it is defined. + if ref.IsPure() && complete { + return resolved[0].WithLocations(locs), nil + } + + // Not pure; perform string interpolation. Substitute by byte offset: the same + // reference may also appear escaped ("$${foo} ${foo}"). + var sb strings.Builder + consumed := 0 + for j := range ref.Matches { + // Leave references that were skipped in place. + if !resolved[j].IsValid() { + continue + } + + s, ok := resolved[j].AsString() + if !ok { + // Only allow primitive types to be converted to string. + switch kind := resolved[j].Kind(); kind { + case KindBool, KindInt, KindFloat, KindNil: + s = fmt.Sprint(resolved[j].AsAny()) + default: + return View{}, fmt.Errorf("cannot interpolate non-primitive value of type %s into string", kind) + } + } + + start, end := ref.Spans[j][0], ref.Spans[j][1] + sb.WriteString(ref.Str[consumed:start]) + sb.WriteString(s) + consumed = end + } + sb.WriteString(ref.Str[consumed:]) + + return stringView(sb.String(), locs), nil +} + +func (r *resolver) resolveKey(key string, seen []string) (View, error) { + if v, ok := r.lookups[key]; ok { + return v.v, v.err + } + + p, err := structpath.ParsePath(key) + if err != nil { + return View{}, err + } + + v, err := r.lookup(p) + if err != nil { + if knf, ok := errors.AsType[*KeyNotFoundError](err); ok { + // Carry suggestions as data; the caller formats them. + err = &ReferenceError{Reference: key, Suggestions: suggestedReferences(knf, key)} + } + r.lookups[key] = lookupResult{err: err} + return View{}, err + } + + // If the value is a reference itself, resolve it. + if s, ok := v.AsString(); ok { + if ref, ok := NewRef(s); ok { + v, err = r.resolveRef(ref, v.Locations(), seen) + } + } + + r.lookups[key] = lookupResult{v: v, err: err} + return v, err +} + +// WithLocations returns the view with its own locations replaced by locs; the values +// below it keep theirs. +func (x View) WithLocations(locs []diag.Location) View { + x.loc = x.loc.WithLocations(locs) + return x +} + +// suggestedReferences returns drop-in replacement references for reference, rebuilt +// by swapping the key that was not found for each suggestion. +func suggestedReferences(err *KeyNotFoundError, reference string) []string { + if len(err.Suggestions) == 0 { + return nil + } + nodes := err.Path.AsSlice() + failedKey, _ := nodes[len(nodes)-1].StringKey() + refs := make([]string, len(err.Suggestions)) + for i, s := range err.Suggestions { + refs[i] = ReplaceKey(reference, failedKey, s) + } + return refs +} diff --git a/libs/structs/structvar/struct_info.go b/libs/structs/structvar/struct_info.go new file mode 100644 index 00000000000..e6c50c59433 --- /dev/null +++ b/libs/structs/structvar/struct_info.go @@ -0,0 +1,252 @@ +package structvar + +import ( + "reflect" + "slices" + "sync" + + "github.com/databricks/cli/libs/structs/structpath" + "github.com/databricks/cli/libs/structs/structtag" + sdkduration "github.com/databricks/databricks-sdk-go/common/types/duration" + sdkfieldmask "github.com/databricks/databricks-sdk-go/common/types/fieldmask" + sdktime "github.com/databricks/databricks-sdk-go/common/types/time" +) + +// StructInfo holds the type information of a struct for converting between +// the configuration tree and the typed value: JSON names, omitempty, ForceSendFields. +type StructInfo struct { + // FieldNames is ordered list of fields + FieldNames []string + + // Fields maps the JSON-name of the field to the field's index for use with [FieldByIndex]. + Fields map[string][]int + + // Tracks which fields do not have omitempty annotation + ForceEmpty map[string]bool + + // Maps JSON-name of the field to Golang struct name + GolangNames map[string]string + + // ForceSendFieldsIndex maps the JSON-name of the field to the index path (for + // use with [reflect.Value.FieldByIndex]) of the ForceSendFields slice that + // governs it: the one declared by the struct that also declares the field. + // The path is static per type, so we resolve it once here rather than walking + // the value at conversion time. It can be more than one element deep because a + // field's declaring struct may be embedded several levels down (e.g. + // PostgresProject -> PostgresProjectConfig -> ProjectSpec). A field whose + // declaring struct has no ForceSendFields has no entry. + ForceSendFieldsIndex map[string][]int + + // List holds the fields in FieldNames order, with everything traversal needs, so + // that visiting a struct does no lookups by name. + List []FieldInfo + + // ForceSendGroups holds the distinct ForceSendFields index paths; a struct with + // embedded structs can have several. + ForceSendGroups [][]int + + // ForceSendTargets maps, per ForceSendGroups entry, the Go field names listed in + // that ForceSendFields to their index in List. + ForceSendTargets []map[string]int +} + +// FieldInfo describes a field of a struct. +type FieldInfo struct { + Key string + Index []int + GolangName string + + // ForceEmpty is set for fields without omitempty. + ForceEmpty bool + + // Dot is set if Key can be written as a dotted path component (see + // [structpath.NewDotString]), which is true for every JSON name in practice. + Dot bool + + // ForceSendGroup is the index in [StructInfo.ForceSendGroups] of the + // ForceSendFields governing the field, or -1. + ForceSendGroup int +} + +var structInfoCache sync.Map // reflect.Type -> *StructInfo + +// GetStructInfo returns the [StructInfo] for the given type. +// It lazily populates a cache, so the first call for a given +// type is slower than subsequent calls for that same type. +func GetStructInfo(typ reflect.Type) *StructInfo { + if si, ok := structInfoCache.Load(typ); ok { + return si.(*StructInfo) + } + si, _ := structInfoCache.LoadOrStore(typ, buildStructInfo(typ)) + return si.(*StructInfo) +} + +// buildStructInfo populates a new [StructInfo] for the given type. +func buildStructInfo(typ reflect.Type) *StructInfo { + out := &StructInfo{ + Fields: make(map[string][]int), + ForceEmpty: make(map[string]bool), + GolangNames: make(map[string]string), + ForceSendFieldsIndex: make(map[string][]int), + } + + // Queue holds the indexes of the structs to visit. + // It is initialized with a single empty slice to visit the top level struct. + queue := [][]int{{}} + for i := 0; i < len(queue); i++ { + prefix := queue[i] + + // Traverse embedded anonymous types (if prefix is non-empty). + styp := typ + if len(prefix) > 0 { + styp = styp.FieldByIndex(prefix).Type + } + + // Dereference pointer type. + if styp.Kind() == reflect.Pointer { + styp = styp.Elem() + } + + // Index path to the ForceSendFields declared by this struct, if any. All + // fields declared directly by this struct are governed by it. The len==1 + // check excludes a ForceSendFields promoted from an embedded struct: that + // one governs the embedded struct's own fields, which we visit separately. + var forceSendFieldsIndex []int + if sf, ok := styp.FieldByName("ForceSendFields"); ok && len(sf.Index) == 1 { + forceSendFieldsIndex = append(slices.Clone(prefix), sf.Index...) + } + + nf := styp.NumField() + for j := range nf { + sf := styp.Field(j) + + // Recurse into anonymous fields. + if sf.Anonymous { + queue = append(queue, append(prefix, sf.Index...)) + continue + } + + jtag := structtag.JSONTag(sf.Tag.Get("json")) + name := jtag.Name() + if name == "" || name == "-" { + continue + } + + // Top level fields always take precedence. + // Therefore, if it is already set, we ignore it. + if _, ok := out.Fields[name]; ok { + continue + } + + out.FieldNames = append(out.FieldNames, name) + out.Fields[name] = append(prefix, sf.Index...) + if !jtag.OmitEmpty() && !jtag.OmitZero() { + out.ForceEmpty[name] = true + } + out.GolangNames[name] = sf.Name + + // The field is declared directly in this struct, so it is governed by + // this struct's ForceSendFields (if it has one). + group := -1 + if forceSendFieldsIndex != nil { + out.ForceSendFieldsIndex[name] = forceSendFieldsIndex + group = forceSendGroup(out, forceSendFieldsIndex) + } + out.List = append(out.List, FieldInfo{ + Key: name, + Index: out.Fields[name], + GolangName: sf.Name, + ForceEmpty: out.ForceEmpty[name], + Dot: structpath.NewStringKey(nil, name).IsDotString(), + ForceSendGroup: group, + }) + } + } + + out.ForceSendTargets = make([]map[string]int, len(out.ForceSendGroups)) + for i, f := range out.List { + if f.ForceSendGroup < 0 { + continue + } + m := out.ForceSendTargets[f.ForceSendGroup] + if m == nil { + m = map[string]int{} + out.ForceSendTargets[f.ForceSendGroup] = m + } + m[f.GolangName] = i + } + return out +} + +// forceSendGroup returns the index of the ForceSendFields path index in +// out.ForceSendGroups, adding it if needed. +func forceSendGroup(out *StructInfo, index []int) int { + for i, g := range out.ForceSendGroups { + if slices.Equal(g, index) { + return i + } + } + out.ForceSendGroups = append(out.ForceSendGroups, index) + return len(out.ForceSendGroups) - 1 +} + +// isForceSend reports whether the field named k is listed in the ForceSendFields +// that governs it (see StructInfo.ForceSendFieldsIndex). +func (s *StructInfo) IsForceSend(v reflect.Value, k string) bool { + index, ok := s.ForceSendFieldsIndex[k] + if !ok { + return false + } + fsf := FieldByIndex(v, index) + if !fsf.IsValid() { + return false + } + return containsString(fsf, s.GolangNames[k]) +} + +// fieldByIndex resolves the value at the given index path, dereferencing embedded +// pointer structs on the way. It returns an invalid value if a nil pointer is met. +func FieldByIndex(v reflect.Value, index []int) reflect.Value { + for i, x := range index { + if i > 0 { + if v.Kind() == reflect.Pointer && v.Type().Elem().Kind() == reflect.Struct { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + } + } + v = v.Field(x) + } + return v +} + +// GetOrNewFieldByIndex resolves the value at the given index path within an addressable +// struct, allocating intermediate structs embedded as pointer types along the way. +func GetOrNewFieldByIndex(v reflect.Value, index []int) reflect.Value { + for i, x := range index { + if i > 0 { + if v.Kind() == reflect.Pointer { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + v = v.Field(x) + } + return v +} + +// sdkNativeTypes are SDK types with custom JSON marshaling to and from a string. +// The configuration represents them as strings. +var sdkNativeTypes = []reflect.Type{ + reflect.TypeFor[sdkduration.Duration](), // Protobuf duration format (e.g., "300s") + reflect.TypeFor[sdktime.Time](), // RFC3339 timestamp format (e.g., "2023-12-25T10:30:00Z") + reflect.TypeFor[sdkfieldmask.FieldMask](), // Comma-separated paths (e.g., "name,age,email") +} + +// IsSDKNativeType reports whether typ is an SDK type represented as a string. +func IsSDKNativeType(typ reflect.Type) bool { + return slices.Contains(sdkNativeTypes, typ) +} diff --git a/libs/structs/structvar/structvar.go b/libs/structs/structvar/structvar.go index cc894aced7f..2f5bde6642e 100644 --- a/libs/structs/structvar/structvar.go +++ b/libs/structs/structvar/structvar.go @@ -8,7 +8,6 @@ import ( "strings" "sync" - "github.com/databricks/cli/libs/dyn/dynvar" "github.com/databricks/cli/libs/structs/structaccess" "github.com/databricks/cli/libs/structs/structpath" ) @@ -20,6 +19,9 @@ type StructVar struct { // Refs holds unresolved references. Key is serialized PathNode pointing inside a struct (e.g. "name") // and value is either pure or multiple references string: "${resources.foo.jobs.id}" or "${a} ${b}" Refs map[string]string `json:"vars,omitempty"` + + // Locations holds the source locations of the values, if known (configuration loaded from files). + Locations *Locations `json:"-"` } // StructVarJSON is the serialized form of StructVar for persisting in plan files. @@ -103,18 +105,18 @@ func (sv *StructVar) ResolveRef(reference string, value any) error { return fmt.Errorf("cannot set %s to %T (%#v): %w", pathNode.String(), value, value, err) } - newValue := dynvar.ReplaceRef(refValue, reference, valueStr) + newValue := ReplaceRef(refValue, reference, valueStr) // The struct gets the unescaped form ("$${x}" -> "${x}"), since that is what // is sent to the API. sv.Refs keeps the escaped form so the still-pending // check below can tell a literal apart from a real reference. - err = structaccess.Set(sv.Value, pathNode, dynvar.Unescape(newValue)) + err = structaccess.Set(sv.Value, pathNode, Unescape(newValue)) if err != nil { return fmt.Errorf("cannot update %s to string: %w", pathNode.String(), err) } // Check if fully resolved (no unescaped ${} patterns left) - if !dynvar.ContainsVariableReference(newValue) { + if !ContainsVariableReference(newValue) { delete(sv.Refs, pathKey) } else { sv.Refs[pathKey] = newValue diff --git a/libs/structs/structvar/structvar_test.go b/libs/structs/structvar/structvar_test.go index 9d4c2532ad6..f1c2095dc77 100644 --- a/libs/structs/structvar/structvar_test.go +++ b/libs/structs/structvar/structvar_test.go @@ -2,8 +2,11 @@ package structvar_test import ( "reflect" + "strings" "testing" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/structs/structvar" "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/stretchr/testify/assert" @@ -290,3 +293,126 @@ func TestToStructVarRequiresPointerType(t *testing.T) { require.NoError(t, err) assert.Equal(t, "test", sv.Value.(*TestObj).Name) } + +func TestMoveMetadata(t *testing.T) { + type obj struct { + Old map[string]TestObj `json:"old"` + New map[string]TestObj `json:"new"` + } + sv := structvar.NewStructVar(&obj{}, map[string]string{ + "old['a'].age": "${var.age}", + "older.age": "${var.other}", + "new['b'].age": "${var.replaced}", + }) + loc := diag.Location{File: "a.yml", Line: 1, Column: 1} + sv.Locations = sv.Locations.With(structpath.NewStringKey(nil, "old"), (*structvar.Locations)(nil).WithLocations([]diag.Location{loc})) + + sv.MoveMetadata(structpath.NewStringKey(nil, "old"), structpath.NewStringKey(nil, "new")) + + assert.Equal(t, map[string]string{ + "new['a'].age": "${var.age}", + "older.age": "${var.other}", + }, sv.Refs) + assert.Equal(t, []diag.Location{loc}, sv.Locations.Exact(structpath.NewStringKey(nil, "new"))) + assert.Nil(t, sv.Locations.Sub(structpath.NewStringKey(nil, "old"))) +} + +func TestCopyMetadata(t *testing.T) { + type obj struct { + Items map[string]TestObj `json:"items"` + } + dst := structvar.NewStructVar(&obj{}, map[string]string{ + "items.a.age": "${var.old}", + "items.b.age": "${var.kept}", + }) + src := structvar.NewStructVar(&obj{}, map[string]string{ + "items.a.name": "${var.new}", + "items.b.age": "${var.ignored}", + }) + loc := diag.Location{File: "a.yml", Line: 2, Column: 3} + a := structpath.NewPath(nil, "items", "a") + src.Locations = src.Locations.With(a, (*structvar.Locations)(nil).WithLocations([]diag.Location{loc})) + + dst.CopyMetadata(src, a) + + assert.Equal(t, map[string]string{ + "items.a.name": "${var.new}", + "items.b.age": "${var.kept}", + }, dst.Refs) + assert.Equal(t, []diag.Location{loc}, dst.Locations.Exact(a)) +} + +func TestDeleteSliceElement(t *testing.T) { + type obj struct { + Items []string `json:"items"` + } + sv := &structvar.StructVar{Value: &obj{Items: []string{"a", "b", "c"}}} + items := structpath.NewStringKey(nil, "items") + for i, name := range []string{"a", "b", "c"} { + loc := diag.Location{File: name, Line: i + 1} + sv.Locations = sv.Locations.With(structpath.NewIndex(items, i), (*structvar.Locations)(nil).WithLocations([]diag.Location{loc})) + } + sv.Refs = map[string]string{"items[2]": "${var.c}"} + + require.NoError(t, sv.Delete(structpath.NewIndex(items, 0))) + + assert.Equal(t, []string{"b", "c"}, sv.Value.(*obj).Items) + assert.Equal(t, []diag.Location{{File: "b", Line: 2}}, sv.Locations.Exact(structpath.NewIndex(items, 0))) + assert.Equal(t, []diag.Location{{File: "c", Line: 3}}, sv.Locations.Exact(structpath.NewIndex(items, 1))) + assert.Nil(t, sv.Locations.Exact(structpath.NewIndex(items, 2))) + assert.Equal(t, map[string]string{"items[1]": "${var.c}"}, sv.Refs) + + require.NoError(t, sv.Delete(structpath.NewIndex(items, 1))) + assert.Equal(t, []string{"b"}, sv.Value.(*obj).Items) + assert.Nil(t, sv.Locations.Exact(structpath.NewIndex(items, 1))) + assert.Empty(t, sv.Refs) +} + +func TestMergeNullKeepsFieldSet(t *testing.T) { + type inner struct { + Enabled bool `json:"enabled"` + } + type obj struct { + Name string `json:"name,omitempty"` + Queue *inner `json:"queue,omitempty"` + ForceSendFields []string `json:"-"` + } + + var dst obj + dstVar, _, err := structvar.DecodeYAML("dst.yml", strings.NewReader("name: x\n"), &dst) + require.NoError(t, err) + + var src obj + srcVar, _, err := structvar.DecodeYAML("src.yml", strings.NewReader("queue: null\n"), &src) + require.NoError(t, err) + + require.NoError(t, dstVar.Merge(nil, srcVar.View())) + + // An explicit null added by the merge stays set, as it does when it is decoded. + assert.Nil(t, dst.Queue) + assert.Equal(t, []string{"Queue"}, dst.ForceSendFields) +} + +func TestMergeNullIntoValueKeepsIt(t *testing.T) { + type inner struct { + Enabled bool `json:"enabled"` + } + type obj struct { + Queue *inner `json:"queue,omitempty"` + ForceSendFields []string `json:"-"` + } + + var dst obj + dstVar, _, err := structvar.DecodeYAML("dst.yml", strings.NewReader("queue:\n enabled: true\n"), &dst) + require.NoError(t, err) + + var src obj + srcVar, _, err := structvar.DecodeYAML("src.yml", strings.NewReader("queue: null\n"), &src) + require.NoError(t, err) + + require.NoError(t, dstVar.Merge(nil, srcVar.View())) + + // A null does not clear an existing value. + require.NotNil(t, dst.Queue) + assert.True(t, dst.Queue.Enabled) +} diff --git a/libs/structs/structvar/suggest.go b/libs/structs/structvar/suggest.go new file mode 100644 index 00000000000..2d2f4bccef7 --- /dev/null +++ b/libs/structs/structvar/suggest.go @@ -0,0 +1,118 @@ +package structvar + +import ( + "fmt" + "slices" + "strings" + + "github.com/databricks/cli/libs/structs/structpath" +) + +const maxSuggestionDistance = 2 + +// LevenshteinDistance computes the edit distance between two strings. +func LevenshteinDistance(a, b string) int { + if len(a) == 0 { + return len(b) + } + if len(b) == 0 { + return len(a) + } + + // Use a single row for the DP table. + prev := make([]int, len(b)+1) + for j := range len(b) + 1 { + prev[j] = j + } + + for i := range len(a) { + curr := make([]int, len(b)+1) + curr[0] = i + 1 + for j := range len(b) { + cost := 1 + if a[i] == b[j] { + cost = 0 + } + curr[j+1] = min( + curr[j]+1, // insertion + prev[j+1]+1, // deletion + prev[j]+cost, // substitution + ) + } + prev = curr + } + + return prev[len(b)] +} + +// SuggestKeys returns the keys whose edit distance from name is at most +// maxSuggestionDistance, ordered by increasing distance. It is used to build +// "did you mean" hints for a key that was not found in a map. +func SuggestKeys(keys []string, name string) []string { + type candidate struct { + key string + dist int + } + + var candidates []candidate + for _, key := range keys { + d := LevenshteinDistance(name, key) + if d <= maxSuggestionDistance { + candidates = append(candidates, candidate{key, d}) + } + } + + slices.SortStableFunc(candidates, func(a, b candidate) int { + return a.dist - b.dist + }) + + suggestions := make([]string, len(candidates)) + for i, c := range candidates { + suggestions[i] = c.key + } + return suggestions +} + +// DidYouMean formats a suggestion clause like `, did you mean "x"?` (or, for +// multiple candidates, `, did you mean one of: "x", "y"?`). It returns an empty +// string when there are no suggestions. +func DidYouMean(suggestions []string) string { + switch len(suggestions) { + case 0: + return "" + case 1: + return fmt.Sprintf(", did you mean %q?", suggestions[0]) + default: + quoted := make([]string, len(suggestions)) + for i, s := range suggestions { + quoted[i] = fmt.Sprintf("%q", s) + } + return fmt.Sprintf(", did you mean one of: %s?", strings.Join(quoted, ", ")) + } +} + +// ReplaceKey returns reference with the component matching failedKey swapped for +// replacement, or just replacement if reference can't be parsed or has no match. +func ReplaceKey(reference, failedKey, replacement string) string { + p, err := structpath.ParsePath(reference) + if err != nil { + return replacement + } + var out *structpath.PathNode + replaced := false + for _, n := range p.AsSlice() { + if k, ok := n.StringKey(); ok { + if k == failedKey && !replaced { + k = replacement + replaced = true + } + out = structpath.NewStringKey(out, k) + } else if i, ok := n.Index(); ok { + out = structpath.NewIndex(out, i) + } + } + if !replaced { + return replacement + } + return out.String() +} diff --git a/libs/structs/structvar/testdata/reference_vectors.json b/libs/structs/structvar/testdata/reference_vectors.json new file mode 100644 index 00000000000..c9c380ad7d0 --- /dev/null +++ b/libs/structs/structvar/testdata/reference_vectors.json @@ -0,0 +1,368 @@ +{ + "comment": "Shared contract for ${...} reference parsing. Consumed by libs/structs/structvar and python/databricks/bundles/core/_transform.py tests.", + "vectors": [ + { + "id": "ascii-simple", + "input": "${hello_world.world_world}", + "match": true, + "references": ["hello_world.world_world"], + "pure": true, + "path": "hello_world.world_world" + }, + { + "id": "ascii-hyphen-separators", + "input": "${helloworld.world-world}", + "match": true, + "references": ["helloworld.world-world"], + "pure": true, + "path": "helloworld.world-world" + }, + { + "id": "ascii-hyphen-in-segment", + "input": "${hello-world.world-world}", + "match": true, + "references": ["hello-world.world-world"], + "pure": true, + "path": "hello-world.world-world" + }, + { + "id": "ascii-underscore-separators", + "input": "${hello_world.world__world}", + "match": true, + "references": ["hello_world.world__world"], + "pure": true, + "path": "hello_world.world__world" + }, + { + "id": "ascii-double-hyphen", + "input": "${hello_world.world--world}", + "match": true, + "references": ["hello_world.world--world"], + "pure": true, + "path": "hello_world.world--world" + }, + { + "id": "ascii-mixed-separators-1", + "input": "${hello_world.world-_world}", + "match": true, + "references": ["hello_world.world-_world"], + "pure": true, + "path": "hello_world.world-_world" + }, + { + "id": "ascii-mixed-separators-2", + "input": "${hello_world.world_-world}", + "match": true, + "references": ["hello_world.world_-world"], + "pure": true, + "path": "hello_world.world_-world" + }, + { + "id": "unicode-accent-segment", + "input": "${café.bar}", + "match": true, + "references": ["café.bar"], + "pure": true, + "path": "café.bar" + }, + { + "id": "unicode-accent-trailing-segment", + "input": "${hello.café}", + "match": true, + "references": ["hello.café"], + "pure": true, + "path": "hello.café" + }, + { + "id": "unicode-cjk", + "input": "${变量.世界}", + "match": true, + "references": ["变量.世界"], + "pure": true, + "path": "变量.世界" + }, + { + "id": "unicode-cjk-numeral-letter", + "input": "${变量.九世界}", + "match": true, + "references": ["变量.九世界"], + "pure": true, + "path": "变量.九世界" + }, + { + "id": "unicode-arabic", + "input": "${العربية.key}", + "match": true, + "references": ["العربية.key"], + "pure": true, + "path": "العربية.key" + }, + { + "id": "unicode-multi-ref", + "input": "${变量.世界} ${café.bar}", + "match": true, + "references": ["变量.世界", "café.bar"], + "pure": false + }, + { + "id": "unicode-var-reference", + "input": "${var.变量}", + "match": true, + "references": ["var.变量"], + "pure": true, + "path": "var.变量" + }, + { + "id": "unicode-resource-reference", + "input": "${resources.jobs.工作.name}", + "match": true, + "references": ["resources.jobs.工作.name"], + "pure": true, + "path": "resources.jobs.工作.name" + }, + { + "id": "indexed-path", + "input": "${foo.bar.baz[0]}", + "match": true, + "references": ["foo.bar.baz[0]"], + "pure": true, + "path": "foo.bar.baz[0]" + }, + { + "id": "nested-indexed-path", + "input": "${foo.bar.baz[0][1].qux}", + "match": true, + "references": ["foo.bar.baz[0][1].qux"], + "pure": true, + "path": "foo.bar.baz[0][1].qux" + }, + { + "id": "indexed-unicode-path", + "input": "${变量.世界[0]}", + "match": true, + "references": ["变量.世界[0]"], + "pure": true, + "path": "变量.世界[0]" + }, + { + "id": "python-simple-variable", + "input": "${var.my_var}", + "match": true, + "references": ["var.my_var"], + "pure": true, + "path": "var.my_var" + }, + { + "id": "python-indexed-variable", + "input": "${var.my_var[0].foo}", + "match": true, + "references": ["var.my_var[0].foo"], + "pure": true, + "path": "var.my_var[0].foo" + }, + { + "id": "leading-underscore", + "input": "${_hello_world.world_world}", + "match": true, + "references": ["_hello_world.world_world"], + "pure": true, + "path": "_hello_world.world_world" + }, + { + "id": "resource-key-leading-underscore", + "input": "${resources.jobs._job.id}", + "match": true, + "references": ["resources.jobs._job.id"], + "pure": true, + "path": "resources.jobs._job.id" + }, + { + "id": "resource-key-leading-underscore-and-hyphens", + "input": "${resources.jobs._asset-bundle-deploymentjob.id}", + "match": true, + "references": ["resources.jobs._asset-bundle-deploymentjob.id"], + "pure": true, + "path": "resources.jobs._asset-bundle-deploymentjob.id" + }, + { + "id": "segment-trailing-hyphen", + "input": "${hello_world-.world_world}", + "match": false + }, + { + "id": "segment-trailing-underscore", + "input": "${hello_world-_.world_world}", + "match": false + }, + { + "id": "second-segment-trailing-hyphen", + "input": "${helloworld.world-world-}", + "match": false + }, + { + "id": "first-segment-trailing-hyphen", + "input": "${helloworld-.world-world}", + "match": false + }, + { + "id": "second-segment-leading-hyphen", + "input": "${helloworld.-world-world}", + "match": false + }, + { + "id": "leading-and-trailing-hyphen", + "input": "${-hello-world.-world-world-}", + "match": false + }, + { + "id": "invalid-separator-sequence", + "input": "${_-_._-_.id}", + "match": false + }, + { + "id": "leading-digit-ascii", + "input": "${0helloworld.world-world}", + "match": false + }, + { + "id": "leading-digit-second-segment", + "input": "${helloworld.9world-world}", + "match": false + }, + { + "id": "leading-digit-unicode", + "input": "${0café.bar}", + "match": false + }, + { + "id": "leading-digit-cjk", + "input": "${变量.9世界}", + "match": false + }, + { + "id": "leading-hyphen-cjk", + "input": "${变量.-世界}", + "match": false + }, + { + "id": "trailing-hyphen-accent", + "input": "${café-}", + "match": false + }, + { + "id": "nfd-decomposed-accent", + "comment": "NFD e + combining acute; not equivalent to NFC café", + "input": "${cafe\u0301.bar}", + "match": false + }, + { + "id": "emoji-in-segment", + "input": "${foo.🌍}", + "match": false + }, + { + "id": "emoji-leading-segment", + "input": "${🌍.bar}", + "match": false + }, + { + "id": "symbol-in-segment", + "input": "${foo.bar$}", + "match": false + }, + { + "id": "zero-width-space", + "input": "${foo.bar\u200b}", + "match": false + }, + { + "id": "fullwidth-digit", + "input": "${变量.9世界}", + "match": false + }, + { + "id": "empty-string", + "input": "", + "match": false, + "pure": false + }, + { + "id": "suffix-after-reference", + "input": "${foo.bar} suffix", + "match": true, + "references": ["foo.bar"], + "pure": false, + "path_ok": false + }, + { + "id": "prefix-before-reference", + "input": "prefix ${foo.bar}", + "match": true, + "references": ["foo.bar"], + "pure": false, + "path_ok": false + }, + { + "id": "adjacent-references", + "input": "${foo.one}${foo.two}", + "match": true, + "references": ["foo.one", "foo.two"], + "pure": false, + "path_ok": false + }, + { + "id": "multiple-variables-not-pure", + "input": "${var.my_var} ${var.my_var}", + "match": true, + "references": ["var.my_var", "var.my_var"], + "pure": false, + "path_ok": false + }, + { + "id": "unclosed-reference", + "input": "${foo.bar", + "match": false + }, + { + "id": "stray-closing-brace", + "input": "foo.bar}", + "match": false + }, + { + "id": "bare-path", + "input": "foo.bar", + "match": false + }, + { + "id": "wrong-braces", + "input": "{foo.bar}", + "match": false + }, + { + "id": "escaped-double-dollar", + "input": "$${foo.bar}", + "match": false + }, + { + "id": "escaped-only-multiple", + "input": "$${a.b} $${c.d}", + "match": false + }, + { + "id": "escaped-mixed-with-real", + "input": "${real.ref} $${escaped.ref}", + "match": true, + "references": ["real.ref"], + "pure": false, + "path_ok": false + }, + { + "id": "escaped-prefix-real-suffix", + "input": "$${escaped} text ${real.ref}", + "match": true, + "references": ["real.ref"], + "pure": false, + "path_ok": false + } + ] +} diff --git a/libs/structs/structvar/testdata/yaml/anchor_01.yml b/libs/structs/structvar/testdata/yaml/anchor_01.yml new file mode 100644 index 00000000000..b677d60b808 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_01.yml @@ -0,0 +1,12 @@ +# 1. Basic Anchor and Alias +defaults: &DEFAULTS + color: red + size: large + +shirt1: + <<: *DEFAULTS + pattern: striped + +shirt2: + <<: *DEFAULTS + pattern: plain diff --git a/libs/structs/structvar/testdata/yaml/anchor_02.yml b/libs/structs/structvar/testdata/yaml/anchor_02.yml new file mode 100644 index 00000000000..86650987f3d --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_02.yml @@ -0,0 +1,13 @@ +# 2. Merging Anchors +# Here, multiple anchors can be merged into a single item. +defaults: &DEFAULTS + color: red + size: large + +materials: &MATERIALS + primary: cotton + secondary: polyester + +shirt: + <<: [*DEFAULTS, *MATERIALS] + pattern: striped diff --git a/libs/structs/structvar/testdata/yaml/anchor_03.yml b/libs/structs/structvar/testdata/yaml/anchor_03.yml new file mode 100644 index 00000000000..f06b7999ef0 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_03.yml @@ -0,0 +1,10 @@ +# 3. Overriding Merged Anchor Values +# You can override values when merging. +defaults: &DEFAULTS + color: red + size: large + pattern: plain + +shirt: + <<: *DEFAULTS + color: blue diff --git a/libs/structs/structvar/testdata/yaml/anchor_04.yml b/libs/structs/structvar/testdata/yaml/anchor_04.yml new file mode 100644 index 00000000000..bbc4bc02707 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_04.yml @@ -0,0 +1,16 @@ +# 4. Nested Anchors +# This demonstrates the reuse of nested content. +address: &ADDRESS + city: San Francisco + state: CA + country: USA + +person1: + name: Alice + address: *ADDRESS + +person2: + name: Bob + address: + <<: *ADDRESS + city: Los Angeles diff --git a/libs/structs/structvar/testdata/yaml/anchor_05.yml b/libs/structs/structvar/testdata/yaml/anchor_05.yml new file mode 100644 index 00000000000..9a4c961b7c8 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_05.yml @@ -0,0 +1,15 @@ +# 5. Using Anchors for List Items +# You can use anchors for list items too. +features: &FEATURES + - wifi + - bluetooth + +phone1: + name: PhoneA + features: *FEATURES + +phone2: + name: PhoneB + features: + - camera + - *FEATURES diff --git a/libs/structs/structvar/testdata/yaml/anchor_06.yml b/libs/structs/structvar/testdata/yaml/anchor_06.yml new file mode 100644 index 00000000000..f27650fad47 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_06.yml @@ -0,0 +1,5 @@ +# 6. String Anchors +commonMessage: &msg "Hello, World!" + +greeting1: *msg +greeting2: *msg diff --git a/libs/structs/structvar/testdata/yaml/anchor_07.yml b/libs/structs/structvar/testdata/yaml/anchor_07.yml new file mode 100644 index 00000000000..b8f32214568 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_07.yml @@ -0,0 +1,10 @@ +# 7. Number Anchors +defaultAge: &age 25 + +person1: + name: Alice + age: *age + +person2: + name: Bob + age: *age diff --git a/libs/structs/structvar/testdata/yaml/anchor_08.yml b/libs/structs/structvar/testdata/yaml/anchor_08.yml new file mode 100644 index 00000000000..8b01e21f14b --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_08.yml @@ -0,0 +1,10 @@ +# 8. Boolean Anchors +isActive: &active true + +user1: + username: user1 + active: *active + +user2: + username: user2 + active: *active diff --git a/libs/structs/structvar/testdata/yaml/anchor_09.yml b/libs/structs/structvar/testdata/yaml/anchor_09.yml new file mode 100644 index 00000000000..8005bf445ef --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_09.yml @@ -0,0 +1,2 @@ +anchortest: &x + child: *x diff --git a/libs/structs/structvar/testdata/yaml/anchor_10.yml b/libs/structs/structvar/testdata/yaml/anchor_10.yml new file mode 100644 index 00000000000..013832ab3ac --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/anchor_10.yml @@ -0,0 +1,5 @@ +leaf: &leaf 1 +shared: &shared + value: *leaf +use1: *shared +use2: *shared diff --git a/libs/structs/structvar/testdata/yaml/empty.yml b/libs/structs/structvar/testdata/yaml/empty.yml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/structs/structvar/testdata/yaml/error_01.yml b/libs/structs/structvar/testdata/yaml/error_01.yml new file mode 100644 index 00000000000..11b96486e32 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/error_01.yml @@ -0,0 +1,6 @@ +# Use string anchor to extend a mapping. +str: &str "Hello world!" + +map: + <<: *str + key: value diff --git a/libs/structs/structvar/testdata/yaml/error_02.yml b/libs/structs/structvar/testdata/yaml/error_02.yml new file mode 100644 index 00000000000..5e9c6276d09 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/error_02.yml @@ -0,0 +1,6 @@ +# Use string anchor inside sequence to extend a mapping. +str: &str "Hello world!" + +map: + <<: [*str] + key: value diff --git a/libs/structs/structvar/testdata/yaml/error_03.yml b/libs/structs/structvar/testdata/yaml/error_03.yml new file mode 100644 index 00000000000..c4d3c397dd9 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/error_03.yml @@ -0,0 +1,4 @@ +# Extend a mapping with a literal string. +map: + <<: value + key: value diff --git a/libs/structs/structvar/testdata/yaml/mix_01.yml b/libs/structs/structvar/testdata/yaml/mix_01.yml new file mode 100644 index 00000000000..98706bf6f01 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/mix_01.yml @@ -0,0 +1,90 @@ +# Scalars + +# Strings can be unquoted +name: John Doe + +# Strings can be single quoted +single_quoted_string: 'This is a single quoted string' + +# Strings can be double quoted (allows for escape sequences) +double_quoted_string: "This is a double quoted string with an escaped newline \n and tab \t." + +# Multiline string with folded style (newlines become spaces) +folded_style: > + This is a very + long string that + spans several lines. + +# Multiline string with literal block style (newlines are preserved) +literal_style: | + Line 1 + Line 2 + Line 3 + +# Integers +age: 30 + +# Floating point +price: 19.99 + +# Boolean values +is_student: true +is_employed: false + +# Null value +middle_name: ~ # null can also be represented with 'null' + +# Timestamp +timestamp: 2023-01-01T12:00:00Z + +# Sequences (lists) + +# Inline list +fruits: [apple, banana, cherry] + +# Block style list +books: + - 'Moby Dick' + - '1984' + - 'Pride and Prejudice' + +# Mappings (dictionaries) + +# Inline dictionary +address: {street: '123 Main St', city: 'Anytown', zip: '12345'} + +# Block style dictionary +employee: + first_name: Jane + last_name: Smith + age: 40 + +# Edge cases and advanced features + +# Strings that look like other types must be quoted +looks_like_number: '12345' +looks_like_boolean: 'yes' +looks_like_null: 'null' +looks_like_timestamp: '2023-01-01T12:00:00Z' + +# Using anchors and aliases to reuse properties +base_address: &base + street: '456 Elm St' + city: 'Sometown' + +office_address: + <<: *base # Merge the base address into this mapping + suite: 500 + +# Nested structures +users: + - name: Alice + age: 28 + interests: + - reading + - cycling + - name: Bob + age: 35 + interests: + - cooking + - hiking diff --git a/libs/structs/structvar/testdata/yaml/mix_02.yml b/libs/structs/structvar/testdata/yaml/mix_02.yml new file mode 100644 index 00000000000..757d626dbfe --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/mix_02.yml @@ -0,0 +1,49 @@ +# Scalars with special characters + +# Commas and square brackets in strings should be enclosed in quotes +special_chars: "[This, string] has, special chars." + +# Strings starting with reserved indicators must be quoted +reserved_string: "@not_a_directive" +colon_string: "this: looks like a mapping, but it's not" + +# Explicit data type declaration +explicit_string: !!str 12345 + +# Sequences with nested mappings and lists + +teams: + - + name: Team A + members: + - Alice + - Bob + tasks: + - task1: + status: incomplete + due_date: 2023-01-15 + - task2: + status: complete + +# Complex mapping keys +? | + Multi-line key + which is unusual but valid +: multi-line key's value + +"complex key: with colon": complex key's value + +# Set (a mapping with null values) +set_example: + item1: ~ + item2: ~ + +# Merge multiple mappings (with override) +base_colors: &colors + red: "#FF0000" + blue: "#0000FF" + green: "#00FF00" + +theme: + <<: *colors + blue: "#001122" # Overriding the blue color from base_colors diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.1.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.1.yml new file mode 100644 index 00000000000..c9e26274c09 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.1.yml @@ -0,0 +1,5 @@ +# Example 2.1 Sequence of Scalars (ball players) + +- Mark McGwire +- Sammy Sosa +- Ken Griffey diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.10.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.10.yml new file mode 100644 index 00000000000..a3459dedfc8 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.10.yml @@ -0,0 +1,10 @@ +# Example 2.10 Node for “Sammy Sosa” appears twice in this document + +--- +hr: +- Mark McGwire +# Following node labeled SS +- &SS Sammy Sosa +rbi: +- *SS # Subsequent occurrence +- Ken Griffey diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.11.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.11.yml new file mode 100644 index 00000000000..e3c5c115e02 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.11.yml @@ -0,0 +1,10 @@ +# Example 2.11 Mapping between Sequences + +? - Detroit Tigers + - Chicago cubs +: - 2001-07-23 + +? [ New York Yankees, + Atlanta Braves ] +: [ 2001-07-02, 2001-08-12, + 2001-08-14 ] diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.12.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.12.yml new file mode 100644 index 00000000000..eb4a526f65b --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.12.yml @@ -0,0 +1,10 @@ +# Example 2.12 Compact Nested Mapping + +--- +# Products purchased +- item : Super Hoop + quantity: 1 +- item : Basketball + quantity: 4 +- item : Big Shoes + quantity: 1 diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.13.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.13.yml new file mode 100644 index 00000000000..e55abff107e --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.13.yml @@ -0,0 +1,6 @@ +# Example 2.13 In literals, newlines are preserved + +# ASCII Art +--- | + \//||\/|| + // || ||__ diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.14.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.14.yml new file mode 100644 index 00000000000..439fca30cb1 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.14.yml @@ -0,0 +1,6 @@ +# Example 2.14 In the folded scalars, newlines become spaces + +--- > + Mark McGwire's + year was crippled + by a knee injury. diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.15.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.15.yml new file mode 100644 index 00000000000..266e7ce4711 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.15.yml @@ -0,0 +1,10 @@ +# Example 2.15 Folded newlines are preserved for “more indented” and blank lines + +--- > + Sammy Sosa completed another + fine season with great stats. + + 63 Home Runs + 0.288 Batting Average + + What a year! diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.16.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.16.yml new file mode 100644 index 00000000000..6db6b0878f8 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.16.yml @@ -0,0 +1,9 @@ +# Example 2.16 Indentation determines scope + +name: Mark McGwire +accomplishment: > + Mark set a major league + home run record in 1998. +stats: | + 65 Home Runs + 0.278 Batting Average diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.17.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.17.yml new file mode 100644 index 00000000000..af0777abab5 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.17.yml @@ -0,0 +1,9 @@ +# Example 2.17 Quoted Scalars + +unicode: "Sosa did fine.\u263A" +control: "\b1998\t1999\t2000\n" +hex esc: "\x0d\x0a is \r\n" + +single: '"Howdy!" he cried.' +quoted: ' # Not a ''comment''.' +tie-fighter: '|\-*-/|' diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.18.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.18.yml new file mode 100644 index 00000000000..741bcd8c0c0 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.18.yml @@ -0,0 +1,8 @@ +# Example 2.18 Multi-line Flow Scalars + +plain: + This unquoted scalar + spans many lines. + +quoted: "So does this + quoted scalar.\n" diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.19.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.19.yml new file mode 100644 index 00000000000..6ed95e092aa --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.19.yml @@ -0,0 +1,15 @@ +# Example 2.19 Integers + +canonical: 12345 +decimal: +12345 +octal: 0o14 +hexadecimal: 0xC + +# Note: this example is not part of the spec but added for completeness. +# +# Octal numbers: +# - YAML 1.1: prefix is "0" +# - YAML 1.2: prefix is "0o" +# The "gopkg.in/yaml.v3" package accepts both for backwards compat. +# We accept only the YAML 1.2 prefix "0o". +octal11: 012345 diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.2.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.2.yml new file mode 100644 index 00000000000..29c16105e3b --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.2.yml @@ -0,0 +1,5 @@ +# Example 2.2 Mapping Scalars to Scalars (player statistics) + +hr: 65 # Home runs +avg: 0.278 # Batting average +rbi: 147 # Runs Batted In diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.20.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.20.yml new file mode 100644 index 00000000000..77a79a0c3e0 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.20.yml @@ -0,0 +1,7 @@ +# Example 2.20 Floating Point + +canonical: 1.23015e+3 +exponential: 12.3015e+02 +fixed: 1230.15 +negative infinity: -.inf +not a number: .nan diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.21.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.21.yml new file mode 100644 index 00000000000..cdb423c566e --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.21.yml @@ -0,0 +1,5 @@ +# Example 2.21 Miscellaneous + +null: +booleans: [ true, false ] +string: '012345' diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.22.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.22.yml new file mode 100644 index 00000000000..bef2addfa07 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.22.yml @@ -0,0 +1,6 @@ +# Example 2.22 Timestamps + +canonical: 2001-12-15T02:59:43.1Z +iso8601: 2001-12-14t21:59:43.10-05:00 +spaced: 2001-12-14 21:59:43.10 -5 +date: 2002-12-14 diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.23.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.23.yml new file mode 100644 index 00000000000..56e9898e21b --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.23.yml @@ -0,0 +1,15 @@ +# Example 2.23 Various Explicit Tags + +--- +not-date: !!str 2002-04-28 + +picture: !!binary | + R0lGODlhDAAMAIQAAP//9/X + 17unp5WZmZgAAAOfn515eXv + Pz7Y6OjuDg4J+fn5OTk6enp + 56enmleECcgggoBADs= + +application specific tag: !something | + The semantics of the tag + above may be different for + different documents. diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.24.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.24.yml new file mode 100644 index 00000000000..f7c11d0fb11 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.24.yml @@ -0,0 +1,16 @@ +# Example 2.24 Global Tags + +%TAG ! tag:clarkevans.com,2002: +--- !shape + # Use the ! handle for presenting + # tag:clarkevans.com,2002:circle +- !circle + center: &ORIGIN {x: 73, y: 129} + radius: 7 +- !line + start: *ORIGIN + finish: { x: 89, y: 102 } +- !label + start: *ORIGIN + color: 0xFFEEBB + text: Pretty vector drawing. diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.25.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.25.yml new file mode 100644 index 00000000000..73bac862755 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.25.yml @@ -0,0 +1,9 @@ +# Example 2.25 Unordered Sets + +# Sets are represented as a +# Mapping where each key is +# associated with a null value +--- !!set +? Mark McGwire +? Sammy Sosa +? Ken Griffey diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.26.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.26.yml new file mode 100644 index 00000000000..00863a6b854 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.26.yml @@ -0,0 +1,9 @@ +# Example 2.26 Ordered Mappings + +# Ordered maps are represented as +# A sequence of mappings, with +# each mapping having one key +--- !!omap +- Mark McGwire: 65 +- Sammy Sosa: 63 +- Ken Griffey: 58 diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.27.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.27.yml new file mode 100644 index 00000000000..fc9b460c299 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.27.yml @@ -0,0 +1,31 @@ +# Example 2.27 Invoice + +--- ! +invoice: 34843 +date : 2001-01-23 +bill-to: &id001 + given : Chris + family : Dumars + address: + lines: | + 458 Walkman Dr. + Suite #292 + city : Royal Oak + state : MI + postal : 48046 +ship-to: *id001 +product: +- sku : BL394D + quantity : 4 + description : Basketball + price : 450.00 +- sku : BL4438H + quantity : 1 + description : Super Hoop + price : 2392.00 +tax : 251.42 +total: 4443.52 +comments: + Late afternoon is best. + Backup contact is Nancy + Billsmer @ 338-4338. diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.28.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.28.yml new file mode 100644 index 00000000000..35369472967 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.28.yml @@ -0,0 +1,28 @@ +# Example 2.28 Log File + +--- +Time: 2001-11-23 15:01:42 -5 +User: ed +Warning: + This is an error message + for the log file +--- +Time: 2001-11-23 15:02:31 -5 +User: ed +Warning: + A slightly different error + message. +--- +Date: 2001-11-23 15:03:17 -5 +User: ed +Fatal: + Unknown variable "bar" +Stack: +- file: TopClass.py + line: 23 + code: | + x = MoreObject("345\n") +- file: MoreClass.py + line: 58 + code: |- + foo = bar diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.3.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.3.yml new file mode 100644 index 00000000000..70cbe07d391 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.3.yml @@ -0,0 +1,10 @@ +# Example 2.3 Mapping Scalars to Sequences (ball clubs in each league) + +american: +- Boston Red Sox +- Detroit Tigers +- New York Yankees +national: +- New York Mets +- Chicago Cubs +- Atlanta Braves diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.4.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.4.yml new file mode 100644 index 00000000000..cce28625f56 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.4.yml @@ -0,0 +1,10 @@ +# Example 2.4 Sequence of Mappings (players’ statistics) + +- + name: Mark McGwire + hr: 65 + avg: 0.278 +- + name: Sammy Sosa + hr: 63 + avg: 0.288 diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.5.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.5.yml new file mode 100644 index 00000000000..a585faee790 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.5.yml @@ -0,0 +1,5 @@ +# Example 2.5 Sequence of Sequences + +- [name , hr, avg ] +- [Mark McGwire, 65, 0.278] +- [Sammy Sosa , 63, 0.288] diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.6.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.6.yml new file mode 100644 index 00000000000..cc137e5d56d --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.6.yml @@ -0,0 +1,7 @@ +# Example 2.6 Mapping of Mappings + +Mark McGwire: {hr: 65, avg: 0.278} +Sammy Sosa: { + hr: 63, + avg: 0.288, + } diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.7.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.7.yml new file mode 100644 index 00000000000..35c2541d6cc --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.7.yml @@ -0,0 +1,12 @@ +# Example 2.7 Two Documents in a Stream (each with a leading comment) + +# Ranking of 1998 home runs +--- +- Mark McGwire +- Sammy Sosa +- Ken Griffey + +# Team ranking +--- +- Chicago Cubs +- St Louis Cardinals diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.8.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.8.yml new file mode 100644 index 00000000000..ae6e8bf279f --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.8.yml @@ -0,0 +1,12 @@ +# Example 2.8 Play by Play Feed from a Game + +--- +time: 20:03:20 +player: Sammy Sosa +action: strike (miss) +... +--- +time: 20:03:47 +player: Sammy Sosa +action: grand slam +... diff --git a/libs/structs/structvar/testdata/yaml/spec_example_2.9.yml b/libs/structs/structvar/testdata/yaml/spec_example_2.9.yml new file mode 100644 index 00000000000..75217b25ec9 --- /dev/null +++ b/libs/structs/structvar/testdata/yaml/spec_example_2.9.yml @@ -0,0 +1,10 @@ +# Example 2.9 Single Document with Two Comments + +--- +hr: # 1998 hr ranking +- Mark McGwire +- Sammy Sosa +# 1998 rbi ranking +rbi: +- Sammy Sosa +- Ken Griffey diff --git a/libs/structs/structvar/view.go b/libs/structs/structvar/view.go new file mode 100644 index 00000000000..fd01de64712 --- /dev/null +++ b/libs/structs/structvar/view.go @@ -0,0 +1,569 @@ +package structvar + +import ( + "encoding/json" + "iter" + "reflect" + "slices" + "strings" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" +) + +type option uint8 + +const ( + // includeZero keeps zero values: the value is behind a pointer, a map value, + // a slice element, or held in an interface. + includeZero option = 1 << iota + + // fromInterface is set for values held in an interface (e.g. variable defaults). + fromInterface +) + +// View is a read-only view of a typed value with its references and locations, as the +// configuration tree they describe. +// +// Presence is decided by the value and ForceSendFields only, never by locations: a +// value is present if it is not zero, is listed in its struct's ForceSendFields, holds +// a pure reference, or is behind a pointer, a map value or a slice element. Decoding +// records an explicitly written zero value in ForceSendFields, so it stays present. +// Unlike libs/dyn, a written zero value in a struct without ForceSendFields (e.g. +// `level: ""` of a permission) and an empty struct value (e.g. `presets: {}`) are absent. +// +// The zero View is invalid and represents an absent value. +type View struct { + v reflect.Value + + // Locations of the value. + loc *Locations + + // Path of the value and the references of the whole value the view was created for. + // The path is only tracked if there are references. + path *structpath.PathNode + refs map[string]string + + opts option + valid bool +} + +// NewView returns the view of the typed value v (typically a pointer to a struct) +// with references refs (keyed by path string) and locations. +func NewView(v any, refs map[string]string, locs *Locations) View { + return View{v: reflect.ValueOf(v), loc: locs, refs: refs, valid: true} +} + +// View returns the view of sv. +func (sv *StructVar) View() View { + return NewView(sv.Value, sv.Refs, sv.Locations) +} + +// ref returns the pure reference held by the value, if its type cannot hold a string. +func (x View) ref() string { + if len(x.refs) == 0 { + return "" + } + return x.refs[x.path.String()] +} + +// child returns a view of the child value v at key k (or index i). +func (x View) childKey(v reflect.Value, k string, opts option) View { + c := View{v: v, loc: x.loc.Key(k), refs: x.refs, opts: opts, valid: true} + if len(x.refs) > 0 { + c.path = structpath.NewStringKey(x.path, k) + } + return c +} + +func (x View) childIndex(v reflect.Value, i int, opts option) View { + c := View{v: v, loc: x.loc.Index(i), refs: x.refs, opts: opts, valid: true} + if len(x.refs) > 0 { + c.path = structpath.NewIndex(x.path, i) + } + return c +} + +// IsValid reports whether the value is present. +func (x View) IsValid() bool { + return x.valid +} + +// Locations returns the source locations of the value. +func (x View) Locations() []diag.Location { + if !x.valid { + return nil + } + return x.loc.Get() +} + +// Location returns the first source location of the value. +func (x View) Location() diag.Location { + locs := x.loc.Get() + if !x.valid || len(locs) == 0 { + return diag.Location{} + } + return locs[0] +} + +// Reflect returns the typed value the view is based on (it may be a pointer or interface). +// It is settable if it was reached through pointers or is the root pointer's element. +func (x View) Reflect() reflect.Value { + return x.v +} + +// Locs returns the locations of this value and the values below it. +func (x View) Locs() *Locations { + return x.loc +} + +// elemOptions returns the options for a map value or a slice element: they keep +// zero values. +func elemOptions(v reflect.Value) option { + if v.Kind() == reflect.Interface { + return includeZero | fromInterface + } + return includeZero +} + +// deref follows pointers and interfaces; it returns an invalid value for nil. +func (x View) deref() (reflect.Value, option) { + v, opts := x.v, x.opts + for { + switch v.Kind() { + case reflect.Pointer: + if v.IsNil() { + return reflect.Value{}, opts + } + v = v.Elem() + // A pointer to a zero value was intentionally set. + opts |= includeZero + case reflect.Interface: + if v.IsNil() { + return reflect.Value{}, opts + } + v = v.Elem() + default: + return v, opts + } + } +} + +// Kind returns the kind of the value in the configuration tree. +func (x View) Kind() Kind { + if !x.valid { + return KindInvalid + } + v, opts := x.deref() + if !v.IsValid() { + return KindNil + } + ref := x.ref() + switch v.Kind() { + case reflect.Struct: + if ref != "" { + return KindString + } + if IsSDKNativeType(v.Type()) { + if _, ok := sdkNativeString(v, opts); ok { + return KindString + } + return KindNil + } + // A struct is present if a field is, or if it is kept (e.g. behind a pointer). + // Locations never make a value present: an empty struct is absent. + if opts&includeZero != 0 || x.hasFields(v) { + return KindMap + } + return KindNil + case reflect.Map: + if ref != "" { + return KindString + } + if v.IsNil() { + return KindNil + } + return KindMap + case reflect.Slice: + if ref != "" { + return KindString + } + if v.IsNil() { + return KindNil + } + return KindSequence + case reflect.String, reflect.Bool, reflect.Int, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64: + if ref != "" { + return KindString + } + // A zero value is absent unless it is kept (forced, or behind a pointer). + if v.IsZero() && opts&includeZero == 0 { + return KindNil + } + switch v.Kind() { + case reflect.String: + return KindString + case reflect.Bool: + return KindBool + case reflect.Float32, reflect.Float64: + return KindFloat + default: + return KindInt + } + default: + return KindInvalid + } +} + +func sdkNativeString(v reflect.Value, opts option) (string, bool) { + if v.IsZero() && opts&includeZero == 0 { + return "", false + } + // SDK native types implement json.Marshaler and marshal to a JSON string. + buf, err := json.Marshal(v.Interface()) + if err != nil { + return "", false + } + var s string + if json.Unmarshal(buf, &s) != nil { + return "", false + } + if s == "" && opts&includeZero == 0 { + return "", false + } + return s, true +} + +// isZero mirrors [dyn.Value.IsZero] for the value. +func (x View) isZero() bool { + switch x.Kind() { + case KindInvalid, KindNil: + return true + case KindMap: + for range x.MapItems() { + return false + } + return true + case KindSequence: + v, _ := x.deref() + return v.Len() == 0 + default: + if x.ref() != "" { + return false + } + s, ok := x.AsString() + if ok { + return s == "" + } + v, _ := x.deref() + return v.IsZero() + } +} + +// field returns the view of the struct field k, and whether it is represented. +func (x View) field(info *StructInfo, sv reflect.Value, k string, fv reflect.Value, isForced bool) (View, bool) { + var opts option + if fv.Kind() == reflect.Interface { + opts = includeZero | fromInterface + } + child := x.childKey(fv, k, opts) + kind := child.Kind() + + // The field is not zero-valued, or it's forced. Locations do not make it present. + if kind == KindNil && !isForced { + return View{}, false + } + + // A zero field is absent unless forced (it may still be a reference: check the view too). + if fv.Kind() != reflect.Struct && fv.IsZero() && child.isZero() && !info.ForceEmpty[k] && !isForced { + return View{}, false + } + + // A forced field with a nil value is represented by its zero value. + if isForced && kind == KindNil { + child.opts |= includeZero + } + return child, true +} + +func (x View) hasFields(v reflect.Value) bool { + return !x.eachField(v, func(string, bool, View) bool { return false }) +} + +// eachField calls fn for the represented fields of the struct v, in declaration order, +// until fn returns false. It returns false if fn did. fn also gets whether the key can +// be a dotted path component, so callers building paths need not check it. +// +// The traversal helpers take a callback rather than returning an iterator so that +// walking a value does not allocate a closure per visited node. +func (x View) eachField(v reflect.Value, fn func(string, bool, View) bool) bool { + info := GetStructInfo(v.Type()) + + // Mark the fields listed in the struct's ForceSendFields, read once per visit. + var forcedBuf [128]bool + var forced []bool + if len(info.List) <= len(forcedBuf) { + forced = forcedBuf[:len(info.List)] + } else { + forced = make([]bool, len(info.List)) + } + for g, index := range info.ForceSendGroups { + names := FieldByIndex(v, index) + if !names.IsValid() { + continue + } + for j := range names.Len() { + if i, ok := info.ForceSendTargets[g][names.Index(j).String()]; ok { + forced[i] = true + } + } + } + + for i := range info.List { + f := &info.List[i] + fv := FieldByIndex(v, f.Index) + if !fv.IsValid() { + continue + } + isForced := forced[i] || !fv.IsZero() + if !isForced && x.isAbsentZeroField(fv, f.Key) { + continue + } + child, ok := x.field(info, v, f.Key, fv, isForced) + if ok && !fn(f.Key, f.Dot, child) { + return false + } + } + return true +} + +// containsString reports whether the []string value s (possibly invalid) contains name. +// It iterates with reflect: converting s with Interface would allocate. +func containsString(s reflect.Value, name string) bool { + if !s.IsValid() { + return false + } + for i := range s.Len() { + if s.Index(i).String() == name { + return true + } + } + return false +} + +// isAbsentZeroField reports whether the zero, unforced field fv (key k) is absent +// without building its view: [View.field] would find it absent, because it is neither +// a struct nor an interface (whose presence depends on their contents) and holds no +// reference. +func (x View) isAbsentZeroField(fv reflect.Value, k string) bool { + switch fv.Kind() { + case reflect.Struct, reflect.Interface: + return false + default: + } + return len(x.refs) == 0 || x.refs[structpath.NewStringKey(x.path, k).String()] == "" +} + +// eachMapItem calls fn for the entries of a map value (struct fields in declaration +// order, map entries in key order) until fn returns false. It returns false if fn did. +func (x View) eachMapItem(fn func(string, bool, View) bool) bool { + if x.ref() != "" { + return true + } + v, _ := x.deref() + switch { + case !v.IsValid(): + case v.Kind() == reflect.Struct && !IsSDKNativeType(v.Type()): + return x.eachField(v, fn) + case v.Kind() == reflect.Map: + keys := v.MapKeys() + slices.SortFunc(keys, func(a, b reflect.Value) int { + return strings.Compare(a.String(), b.String()) + }) + for _, key := range keys { + ev := v.MapIndex(key) + k := key.String() + if !fn(k, false, x.childKey(ev, k, elemOptions(ev))) { + return false + } + } + } + return true +} + +// eachElem calls fn for the elements of a sequence value until fn returns false. +// It returns false if fn did. +func (x View) eachElem(fn func(int, View) bool) bool { + if x.Kind() != KindSequence { + return true + } + v, _ := x.deref() + for i := range v.Len() { + ev := v.Index(i) + if !fn(i, x.childIndex(ev, i, elemOptions(ev))) { + return false + } + } + return true +} + +// MapItems returns the entries of a map value: struct fields in declaration order, +// map entries in key order. +func (x View) MapItems() iter.Seq2[string, View] { + return func(yield func(string, View) bool) { + x.eachMapItem(func(k string, _ bool, c View) bool { return yield(k, c) }) + } +} + +// Sequence returns the elements of a sequence value. +func (x View) Sequence() iter.Seq2[int, View] { + return func(yield func(int, View) bool) { x.eachElem(yield) } +} + +// Get returns the value of key k of a map value, or an invalid value. +func (x View) Get(k string) View { + if x.Kind() != KindMap { + return View{} + } + v, _ := x.deref() + switch v.Kind() { + case reflect.Struct: + info := GetStructInfo(v.Type()) + index, ok := info.Fields[k] + if !ok { + return View{} + } + fv := FieldByIndex(v, index) + if !fv.IsValid() { + return View{} + } + isForced := !fv.IsZero() || info.IsForceSend(v, k) + child, ok := x.field(info, v, k, fv, isForced) + if !ok { + return View{} + } + return child + case reflect.Map: + ev := v.MapIndex(reflect.ValueOf(k).Convert(v.Type().Key())) + if !ev.IsValid() { + return View{} + } + child := x.childKey(ev, k, elemOptions(ev)) + return child + default: + return View{} + } +} + +// Index returns element i of a sequence value, or an invalid value. +func (x View) Index(i int) View { + if x.Kind() != KindSequence { + return View{} + } + v, _ := x.deref() + if i < 0 || i >= v.Len() { + return View{} + } + ev := v.Index(i) + child := x.childIndex(ev, i, elemOptions(ev)) + return child +} + +// Lookup returns the value at path, or an invalid value if there is none. +func (x View) Lookup(path *structpath.PathNode) View { + return x.lookupComponents(components(path)) +} + +func (x View) lookupComponents(path []pathComponent) View { + for _, c := range path { + if c.isKey { + x = x.Get(c.key) + } else { + x = x.Index(c.index) + } + if !x.valid { + return x + } + } + return x +} + +// AsString returns the string value (a string, a pure reference, or an SDK native value). +func (x View) AsString() (string, bool) { + if x.Kind() != KindString { + return "", false + } + if ref := x.ref(); ref != "" { + return ref, true + } + v, opts := x.deref() + if v.Kind() == reflect.Struct { + return sdkNativeString(v, opts) + } + return v.String(), true +} + +// AsBool returns the bool value. +func (x View) AsBool() (bool, bool) { + if x.Kind() != KindBool { + return false, false + } + v, _ := x.deref() + return v.Bool(), true +} + +// AsInt returns the int value. +func (x View) AsInt() (int64, bool) { + if x.Kind() != KindInt { + return 0, false + } + v, _ := x.deref() + return v.Int(), true +} + +// AsFloat returns the float value. +func (x View) AsFloat() (float64, bool) { + if x.Kind() != KindFloat { + return 0, false + } + v, _ := x.deref() + return v.Float(), true +} + +// AsAny returns the value as a generic Go value (map[string]any, []any, string, +// bool, int64 or int, float64, or nil), like [dyn.Value.AsAny]. +func (x View) AsAny() any { + switch x.Kind() { + case KindMap: + out := map[string]any{} + for k, c := range x.MapItems() { + out[k] = c.AsAny() + } + return out + case KindSequence: + out := []any{} //nolint:gocritic // an empty sequence renders as [], not null + for _, c := range x.Sequence() { + out = append(out, c.AsAny()) + } + return out + case KindString: + s, _ := x.AsString() + return s + case KindBool: + b, _ := x.AsBool() + return b + case KindInt: + v, opts := x.deref() + // Values held in an interface keep the type the YAML loader produced (int), + // typed fields hold the normalized int64. + if opts&fromInterface != 0 && v.Type() == reflect.TypeFor[int]() { + return int(v.Int()) + } + return v.Int() + case KindFloat: + f, _ := x.AsFloat() + return f + default: + return nil + } +} diff --git a/libs/structs/structvar/walk.go b/libs/structs/structvar/walk.go new file mode 100644 index 00000000000..76b1b849a9e --- /dev/null +++ b/libs/structs/structvar/walk.go @@ -0,0 +1,52 @@ +package structvar + +import ( + "errors" + + "github.com/databricks/cli/libs/structs/structpath" +) + +// Walk calls fn for x and every value below it, parents before children, like +// [dyn.WalkReadOnly] visits a tree. Maps are visited in [View.MapItems] order. +// fn may return [ErrSkip] to skip the subtree of the visited value. +// +// fn may change the configuration at or below the visited path (e.g. with [Set]), +// but must not add or remove the keys and elements being iterated. +func Walk(x View, fn func(*structpath.PathNode, View) error) error { + return walk(x, nil, fn) +} + +func walk(x View, p *structpath.PathNode, fn func(*structpath.PathNode, View) error) error { + if err := fn(p, x); err != nil { + if errors.Is(err, ErrSkip) { + return nil + } + return err + } + + var err error + switch x.Kind() { + case KindMap: + x.eachMapItem(func(k string, dot bool, c View) bool { + var cp *structpath.PathNode + if dot { + cp = structpath.NewDotString(p, k) + } else { + cp = structpath.NewStringKey(p, k) + } + err = walk(c, cp, fn) + return err == nil + }) + case KindSequence: + x.eachElem(func(i int, c View) bool { + err = walk(c, structpath.NewIndex(p, i), fn) + return err == nil + }) + default: + } + return err +} + +// ErrSkip can be returned by the function passed to [Walk] to skip the subtree of the +// visited value. +var ErrSkip = errors.New("skip traversal of subtree") diff --git a/libs/structs/structvar/write.go b/libs/structs/structvar/write.go new file mode 100644 index 00000000000..e5c20c76e01 --- /dev/null +++ b/libs/structs/structvar/write.go @@ -0,0 +1,435 @@ +package structvar + +import ( + "errors" + "fmt" + "reflect" + "slices" + "strings" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structpath" +) + +// The methods in this file change sv.Value (a pointer) and keep sv.Refs and +// sv.Locations in sync. Values along the path are allocated as needed (nil pointers, +// nil maps), and map values are written back, so maps of structs can be changed too. +// Refs is replaced rather than modified, so copies of a StructVar can share it. + +// Set sets value at path, like setting it in the configuration tree: a zero value +// is kept as explicitly set (it is added to the governing ForceSendFields). The +// locations recorded for path are kept; references at and below path are dropped. +func (sv *StructVar) Set(path *structpath.PathNode, value any) error { + err := update(reflect.ValueOf(sv.Value), components(path), func(dst reflect.Value) (bool, error) { + rv := reflect.ValueOf(value) + if !rv.IsValid() { + dst.SetZero() + return false, nil + } + if err := setGoValue(dst, rv); err != nil { + return false, fmt.Errorf("cannot set %s: %w", path, err) + } + return rv.IsZero(), nil + }) + if err != nil { + return err + } + sv.Refs = withoutRefs(sv.Refs, path) + // The value keeps its own locations; those of the values it replaced below it are + // dropped. Rewriting the tree clones every ancestor (e.g. the map of all jobs), so + // it is skipped when there is nothing to drop. + cur := sv.Locations.Sub(path) + switch { + case cur == nil: + case value == nil: + sv.Locations = sv.Locations.With(path, nil) + case len(cur.keys) > 0 || len(cur.elems) > 0: + sv.Locations = sv.Locations.With(path, (*Locations)(nil).WithLocations(cur.Get())) + } + return nil +} + +func setGoValue(dst, src reflect.Value) error { + for src.Kind() == reflect.Interface || (src.Kind() == reflect.Pointer && dst.Kind() != reflect.Pointer) { + if src.IsNil() { + dst.SetZero() + return nil + } + src = src.Elem() + } + switch { + case src.Type().AssignableTo(dst.Type()): + dst.Set(src) + case dst.Kind() == reflect.Pointer && src.Type().AssignableTo(dst.Type().Elem()): + p := reflect.New(dst.Type().Elem()) + p.Elem().Set(src) + dst.Set(p) + case isScalarKind(src.Kind()) && isScalarKind(dst.Kind()) && src.Type().ConvertibleTo(dst.Type()) && (src.Kind() == reflect.String) == (dst.Kind() == reflect.String): + dst.Set(src.Convert(dst.Type())) + default: + return fmt.Errorf("cannot assign %s to %s", src.Type(), dst.Type()) + } + return nil +} + +func isScalarKind(k reflect.Kind) bool { + switch k { + case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: + return true + default: + return false + } +} + +// Delete removes the value at path: a map entry or slice element is deleted (the +// elements after it move down, with their locations and references), a struct field is +// zeroed. +func (sv *StructVar) Delete(path *structpath.PathNode) error { + p := components(path) + if len(p) == 0 { + return errors.New("cannot delete the root") + } + parent, last := p[:len(p)-1], p[len(p)-1] + shifted := false + err := walkTo(reflect.ValueOf(sv.Value), parent, func(v reflect.Value) error { + v = derefAlloc(v) + switch { + case v.Kind() == reflect.Map && last.isKey: + if !v.IsNil() { + v.SetMapIndex(reflect.ValueOf(last.key).Convert(v.Type().Key()), reflect.Value{}) + } + case v.Kind() == reflect.Slice && !last.isKey: + if last.index < 0 || last.index >= v.Len() { + return fmt.Errorf("index %d out of range at %s", last.index, path.Parent()) + } + n := v.Len() + sv.Refs = withoutRefs(sv.Refs, path) + sv.Locations = sv.Locations.With(path, nil) + shifted = true + reflect.Copy(v.Slice(last.index, n), v.Slice(last.index+1, n)) + v.Index(n - 1).SetZero() + v.SetLen(n - 1) + for i := last.index + 1; i < n; i++ { + sv.MoveMetadata(structpath.NewIndex(path.Parent(), i), structpath.NewIndex(path.Parent(), i-1)) + } + case v.Kind() == reflect.Struct && last.isKey: + info := GetStructInfo(v.Type()) + index, ok := info.Fields[last.key] + if !ok { + return fmt.Errorf("no field %q at %s", last.key, path.Parent()) + } + if f := FieldByIndex(v, index); f.IsValid() { + f.SetZero() + } + removeForceSend(v, info, last.key) + default: + return fmt.Errorf("cannot delete %s", path) + } + return nil + }) + if err != nil { + return err + } + if !shifted { + sv.Refs = withoutRefs(sv.Refs, path) + sv.Locations = sv.Locations.With(path, nil) + } + return nil +} + +// MoveMetadata moves the locations and references of the value at from and below it +// to to, replacing those at to. Values are not changed: the caller moves them. +func (sv *StructVar) MoveMetadata(from, to *structpath.PathNode) { + sv.CopyMetadataAt(from, to) + sv.Locations = sv.Locations.With(from, nil) + sv.Refs = withoutRefs(sv.Refs, from) +} + +// CopyMetadataAt copies the locations and references of the value at from and below +// it to to, replacing those at to. Values are not changed: the caller copies them. +func (sv *StructVar) CopyMetadataAt(from, to *structpath.PathNode) { + sv.Locations = sv.Locations.With(to, sv.Locations.Sub(from)) + if len(sv.Refs) == 0 { + return + } + fromKey := from.String() + refs := withoutRefs(sv.Refs, to) + for k, v := range sv.Refs { + if isUnder(k, fromKey) { + if moved, ok := rebaseKey(k, from, to); ok { + refs[moved] = v + } + } + } + sv.Refs = refs +} + +// CopyMetadata replaces the locations and references of the value at path and below it +// with those in src. Values are not changed: the caller copies them. +func (sv *StructVar) CopyMetadata(src *StructVar, path *structpath.PathNode) { + sv.Locations = sv.Locations.With(path, src.Locations.Sub(path)) + if len(sv.Refs) == 0 && len(src.Refs) == 0 { + return + } + prefix := path.String() + refs := withoutRefs(sv.Refs, path) + if len(refs) == 0 { + refs = map[string]string{} + } + for k, v := range src.Refs { + if isUnder(k, prefix) { + refs[k] = v + } + } + sv.Refs = refs +} + +// SetLocations sets the locations of the value at path and of every value below it, +// including values that have no locations yet (e.g. set in Go code). +func (sv *StructVar) SetLocations(path *structpath.PathNode, locs []diag.Location) { + x := sv.View().Lookup(path) + if !x.IsValid() { + return + } + sv.Locations = sv.Locations.With(path, locationsOf(x, locs)) +} + +func locationsOf(x View, locs []diag.Location) *Locations { + out := (*Locations)(nil).WithLocations(locs) + switch x.Kind() { + case KindMap: + for k, c := range x.MapItems() { + out.setChild(pathComponent{key: k, isKey: true}, locationsOf(c, locs)) + } + case KindSequence: + for i, c := range x.Sequence() { + out.setChild(pathComponent{index: i}, locationsOf(c, locs)) + } + default: + } + return out +} + +// UpdateSequence records that the elements of the sequence at path were rebuilt from +// the old ones: new element i comes from old elements sources[i] (see +// [Locations.WithSequence]). References of the old elements move along. +func (sv *StructVar) UpdateSequence(path *structpath.PathNode, sources [][]int) { + sv.Locations = sv.Locations.WithSequence(path, sources) + if len(sv.Refs) == 0 { + return + } + elems := path.String() + "[" + refs := make(map[string]string, len(sv.Refs)) + for k, v := range sv.Refs { + if !strings.HasPrefix(k, elems) { + refs[k] = v + } + } + for i, src := range sources { + to := structpath.NewIndex(path, i) + for _, old := range src { + from := structpath.NewIndex(path, old) + fromKey := from.String() + for k, v := range sv.Refs { + if isUnder(k, fromKey) { + if moved, ok := rebaseKey(k, from, to); ok { + refs[moved] = v + } + } + } + } + } + sv.Refs = refs +} + +// rebaseKey returns the refs key k, at or below from, moved to the same place below to. +func rebaseKey(k string, from, to *structpath.PathNode) (string, bool) { + p, err := structpath.ParsePath(k) + if err != nil { + return "", false + } + return structpath.Join(to, p.SkipPrefix(from.Len()).AsSlice()...).String(), true +} + +// isUnder reports whether the path string key is prefix or a path below it. +func isUnder(key, prefix string) bool { + if prefix == "" || key == prefix { + return true + } + if !strings.HasPrefix(key, prefix) { + return false + } + c := key[len(prefix)] + return c == '.' || c == '[' +} + +// withoutRefs returns a copy of refs without the references at and below path. +func withoutRefs(refs map[string]string, path *structpath.PathNode) map[string]string { + if len(refs) == 0 { + return refs + } + prefix := path.String() + out := make(map[string]string, len(refs)) + for k, v := range refs { + if !isUnder(k, prefix) { + out[k] = v + } + } + return out +} + +// SetReference sets the value at path to the reference ref. A string (or interface) +// field holds the reference itself; for other fields the typed value is set to zero, +// like decoding does, and the reference is recorded in Refs. +func (sv *StructVar) SetReference(path *structpath.PathNode, ref string) error { + isText := false + err := walkTo(reflect.ValueOf(sv.Value), components(path), func(dst reflect.Value) error { + switch dst.Kind() { + case reflect.String, reflect.Interface: + isText = true + case reflect.Pointer: + // The reference is recorded on an allocated zero value, so it is present. + dst.Set(reflect.New(dst.Type().Elem())) + default: + dst.SetZero() + } + return nil + }) + if err != nil { + return err + } + if isText { + return sv.Set(path, ref) + } + locs := sv.Locations.Exact(path) + // withoutRefs returns a fresh map unless there is nothing to filter. + refs := withoutRefs(sv.Refs, path) + if len(refs) == 0 { + refs = map[string]string{} + } + refs[path.String()] = ref + sv.Refs = refs + sv.Locations = sv.Locations.With(path, (*Locations)(nil).WithLocations(locs)) + return nil +} + +func addForceSend(v reflect.Value, info *StructInfo, k string) { + index, ok := info.ForceSendFieldsIndex[k] + if !ok { + return + } + fsf := GetOrNewFieldByIndex(v, index) + name := info.GolangNames[k] + if slices.Contains(fsf.Interface().([]string), name) { + return + } + fsf.Set(reflect.Append(fsf, reflect.ValueOf(name))) +} + +func removeForceSend(v reflect.Value, info *StructInfo, k string) { + index, ok := info.ForceSendFieldsIndex[k] + if !ok { + return + } + fsf := FieldByIndex(v, index) + if !fsf.IsValid() { + return + } + name := info.GolangNames[k] + names := fsf.Interface().([]string) + if i := slices.Index(names, name); i >= 0 { + fsf.Set(reflect.ValueOf(slices.Delete(slices.Clone(names), i, i+1))) + } +} + +// derefAlloc follows pointers, allocating nil ones, and returns the value they point to. +func derefAlloc(v reflect.Value) reflect.Value { + for v.Kind() == reflect.Pointer { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + return v +} + +// walkTo calls fn with the settable value at path. +func walkTo(v reflect.Value, path []pathComponent, fn func(reflect.Value) error) error { + if len(path) == 0 { + return fn(v) + } + return updateChild(derefAlloc(v), path[0], func(child reflect.Value) (bool, error) { + return false, walkTo(child, path[1:], fn) + }) +} + +// update calls fn with the settable value at path. If fn reports that it set a zero +// value into a struct field, the field is added to the governing ForceSendFields. +func update(v reflect.Value, path []pathComponent, fn func(reflect.Value) (bool, error)) error { + if len(path) == 0 { + if v.Kind() == reflect.Pointer && !v.CanSet() { + v = v.Elem() + } + _, err := fn(v) + return err + } + return walkTo(v, path[:len(path)-1], func(parent reflect.Value) error { + return updateChild(derefAlloc(parent), path[len(path)-1], fn) + }) +} + +// updateChild calls fn with the settable child c of v. Interfaces and map values are +// copied and written back; an index one past the end of a slice appends an element. +func updateChild(v reflect.Value, c pathComponent, fn func(reflect.Value) (bool, error)) error { + if v.Kind() == reflect.Interface { + if v.IsNil() { + return errors.New("cannot index nil value") + } + cp := reflect.New(v.Elem().Type()).Elem() + cp.Set(v.Elem()) + err := updateChild(derefAlloc(cp), c, fn) + v.Set(cp) + return err + } + + switch { + case c.isKey && v.Kind() == reflect.Struct: + info := GetStructInfo(v.Type()) + index, ok := info.Fields[c.key] + if !ok { + return fmt.Errorf("no field %q", c.key) + } + zero, err := fn(GetOrNewFieldByIndex(v, index)) + if err != nil { + return err + } + if zero { + addForceSend(v, info, c.key) + } + return nil + case c.isKey && v.Kind() == reflect.Map: + if v.IsNil() { + v.Set(reflect.MakeMap(v.Type())) + } + key := reflect.ValueOf(c.key).Convert(v.Type().Key()) + e := reflect.New(v.Type().Elem()).Elem() + if old := v.MapIndex(key); old.IsValid() { + e.Set(old) + } + _, err := fn(e) + v.SetMapIndex(key, e) + return err + case !c.isKey && v.Kind() == reflect.Slice: + if c.index == v.Len() { + v.Set(reflect.Append(v, reflect.New(v.Type().Elem()).Elem())) + } + if c.index < 0 || c.index >= v.Len() { + return fmt.Errorf("index %d out of range", c.index) + } + _, err := fn(v.Index(c.index)) + return err + default: + return fmt.Errorf("cannot index %s", v.Kind()) + } +} diff --git a/libs/structs/structvar/yaml_anchor_test.go b/libs/structs/structvar/yaml_anchor_test.go new file mode 100644 index 00000000000..f4fe8e9dfd1 --- /dev/null +++ b/libs/structs/structvar/yaml_anchor_test.go @@ -0,0 +1,115 @@ +package structvar_test + +import ( + "testing" + + "github.com/databricks/cli/libs/diag" + "github.com/stretchr/testify/assert" +) + +func loc(file string, line, column int) []diag.Location { + return []diag.Location{{File: file, Line: line, Column: column}} +} + +func TestYAMLAnchor01(t *testing.T) { + file := "testdata/yaml/anchor_01.yml" + v, sv := loadYAML(t, file) + + // The anchor is not reported as an unknown field, the other keys are. + type known struct{} + warnings := decodeFields[known](t, file) + assert.NotContains(t, warnings, "unknown field: defaults") + assert.Contains(t, warnings, "unknown field: shirt1") + assert.Contains(t, warnings, "unknown field: shirt2") + + assert.Equal(t, "striped", at(t, v, "shirt1", "pattern")) + assert.Equal(t, loc(file, 8, 12), locsAt(sv, "shirt1", "pattern")) +} + +func TestYAMLAnchor02(t *testing.T) { + file := "testdata/yaml/anchor_02.yml" + v, sv := loadYAML(t, file) + + assert.Equal(t, "red", at(t, v, "shirt", "color")) + assert.Equal(t, loc(file, 4, 10), locsAt(sv, "shirt", "color")) + + assert.Equal(t, "cotton", at(t, v, "shirt", "primary")) + assert.Equal(t, loc(file, 8, 12), locsAt(sv, "shirt", "primary")) + + assert.Equal(t, "striped", at(t, v, "shirt", "pattern")) + assert.Equal(t, loc(file, 13, 12), locsAt(sv, "shirt", "pattern")) +} + +func TestYAMLAnchor03(t *testing.T) { + file := "testdata/yaml/anchor_03.yml" + v, sv := loadYAML(t, file) + + // Assert the override took place. + assert.Equal(t, "blue", at(t, v, "shirt", "color")) + assert.Equal(t, loc(file, 10, 10), locsAt(sv, "shirt", "color")) +} + +func TestYAMLAnchor04(t *testing.T) { + file := "testdata/yaml/anchor_04.yml" + v, sv := loadYAML(t, file) + + assert.Equal(t, "San Francisco", at(t, v, "person1", "address", "city")) + assert.Equal(t, loc(file, 4, 9), locsAt(sv, "person1", "address", "city")) + + assert.Equal(t, "Los Angeles", at(t, v, "person2", "address", "city")) + assert.Equal(t, loc(file, 16, 11), locsAt(sv, "person2", "address", "city")) +} + +func TestYAMLAnchor05(t *testing.T) { + file := "testdata/yaml/anchor_05.yml" + v, sv := loadYAML(t, file) + + assert.Equal(t, "wifi", at(t, v, "phone1", "features", 0)) + assert.Equal(t, loc(file, 4, 5), locsAt(sv, "phone1", "features", 0)) + assert.Equal(t, "bluetooth", at(t, v, "phone1", "features", 1)) + assert.Equal(t, loc(file, 5, 5), locsAt(sv, "phone1", "features", 1)) +} + +func TestYAMLAnchor06(t *testing.T) { + file := "testdata/yaml/anchor_06.yml" + v, sv := loadYAML(t, file) + + assert.Equal(t, "Hello, World!", at(t, v, "greeting1")) + assert.Equal(t, loc(file, 2, 16), locsAt(sv, "greeting1")) +} + +func TestYAMLAnchor07(t *testing.T) { + file := "testdata/yaml/anchor_07.yml" + v, sv := loadYAML(t, file) + + assert.Equal(t, "Alice", at(t, v, "person1", "name")) + assert.Equal(t, loc(file, 5, 9), locsAt(sv, "person1", "name")) + + assert.Equal(t, 25, at(t, v, "person1", "age")) + assert.Equal(t, loc(file, 2, 13), locsAt(sv, "person1", "age")) +} + +func TestYAMLAnchor08(t *testing.T) { + file := "testdata/yaml/anchor_08.yml" + v, sv := loadYAML(t, file) + + assert.Equal(t, "user1", at(t, v, "user1", "username")) + assert.Equal(t, loc(file, 5, 13), locsAt(sv, "user1", "username")) + + assert.Equal(t, true, at(t, v, "user1", "active")) + assert.Equal(t, loc(file, 2, 11), locsAt(sv, "user1", "active")) +} + +func TestYAMLAnchor09(t *testing.T) { + file := "testdata/yaml/anchor_09.yml" + _, _, err := decodeFile(t, file) + assert.ErrorContains(t, err, `cyclic reference to anchor "x"`) +} + +func TestYAMLAnchor10(t *testing.T) { + file := "testdata/yaml/anchor_10.yml" + v, _ := loadYAML(t, file) + + assert.Equal(t, 1, at(t, v, "use1", "value")) + assert.Equal(t, 1, at(t, v, "use2", "value")) +} diff --git a/libs/structs/structvar/yaml_error_test.go b/libs/structs/structvar/yaml_error_test.go new file mode 100644 index 00000000000..1e1954e8c0e --- /dev/null +++ b/libs/structs/structvar/yaml_error_test.go @@ -0,0 +1,34 @@ +package structvar_test + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" +) + +func TestYAMLErrorMapMerge(t *testing.T) { + for _, file := range []string{ + "testdata/yaml/error_01.yml", + "testdata/yaml/error_02.yml", + "testdata/yaml/error_03.yml", + } { + input, err := os.ReadFile(file) + require.NoError(t, err) + + t.Run(file, func(t *testing.T) { + t.Run("reference", func(t *testing.T) { + var ref any + err = yaml.Unmarshal(input, &ref) + assert.ErrorContains(t, err, "map merge requires map or sequence of maps as the value") + }) + + t.Run("self", func(t *testing.T) { + _, _, err := decodeFile(t, file) + assert.ErrorContains(t, err, "map merge requires map or sequence of maps as the value") + }) + }) + } +} diff --git a/libs/structs/structvar/yaml_mix_test.go b/libs/structs/structvar/yaml_mix_test.go new file mode 100644 index 00000000000..b9529424d20 --- /dev/null +++ b/libs/structs/structvar/yaml_mix_test.go @@ -0,0 +1,48 @@ +package structvar_test + +import ( + "bytes" + "os" + "testing" + + "github.com/databricks/cli/libs/structs/structvar" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// decodeFields decodes the file into a struct that has only the given fields and +// returns the names of the unknown fields reported. Anchors must not be reported. +func decodeFields[T any](t *testing.T, file string) []string { + input, err := os.ReadFile(file) + require.NoError(t, err) + var dst T + _, diags, err := structvar.DecodeYAML(file, bytes.NewBuffer(input), &dst) + require.NoError(t, err) + var out []string + for _, d := range diags { + out = append(out, d.Summary) + } + return out +} + +// The old tests checked dyn.Value.IsAnchor. The decoder exposes anchors only through +// the "unknown field" warning, which is not reported for anchors. +func TestYAMLMix01(t *testing.T) { + file := "testdata/yaml/mix_01.yml" + loadYAML(t, file) + + type known struct{} + warnings := decodeFields[known](t, file) + assert.Contains(t, warnings, "unknown field: office_address") + assert.NotContains(t, warnings, "unknown field: base_address") +} + +func TestYAMLMix02(t *testing.T) { + file := "testdata/yaml/mix_02.yml" + loadYAML(t, file) + + type known struct{} + warnings := decodeFields[known](t, file) + assert.Contains(t, warnings, "unknown field: theme") + assert.NotContains(t, warnings, "unknown field: base_colors") +} diff --git a/libs/structs/structvar/yaml_spec_test.go b/libs/structs/structvar/yaml_spec_test.go new file mode 100644 index 00000000000..16eeb7e22d4 --- /dev/null +++ b/libs/structs/structvar/yaml_spec_test.go @@ -0,0 +1,803 @@ +package structvar_test + +import ( + "math" + "testing" + + "github.com/databricks/cli/libs/diag" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const NL = "\n" + +func TestYAMLSpecExample_2_1(t *testing.T) { + file := "testdata/yaml/spec_example_2.1.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + []ev{ + nv("Mark McGwire", []diag.Location{{File: file, Line: 3, Column: 3}}), + nv("Sammy Sosa", []diag.Location{{File: file, Line: 4, Column: 3}}), + nv("Ken Griffey", []diag.Location{{File: file, Line: 5, Column: 3}}), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_2(t *testing.T) { + file := "testdata/yaml/spec_example_2.2.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "hr": nv(65, []diag.Location{{File: file, Line: 3, Column: 6}}), + "avg": nv(0.278, []diag.Location{{File: file, Line: 4, Column: 6}}), + "rbi": nv(147, []diag.Location{{File: file, Line: 5, Column: 6}}), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_3(t *testing.T) { + file := "testdata/yaml/spec_example_2.3.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "american": nv( + []ev{ + nv("Boston Red Sox", []diag.Location{{File: file, Line: 4, Column: 3}}), + nv("Detroit Tigers", []diag.Location{{File: file, Line: 5, Column: 3}}), + nv("New York Yankees", []diag.Location{{File: file, Line: 6, Column: 3}}), + }, + []diag.Location{{File: file, Line: 4, Column: 1}}, + ), + "national": nv( + []ev{ + nv("New York Mets", []diag.Location{{File: file, Line: 8, Column: 3}}), + nv("Chicago Cubs", []diag.Location{{File: file, Line: 9, Column: 3}}), + nv("Atlanta Braves", []diag.Location{{File: file, Line: 10, Column: 3}}), + }, + []diag.Location{{File: file, Line: 8, Column: 1}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_4(t *testing.T) { + file := "testdata/yaml/spec_example_2.4.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + []ev{ + nv( + map[string]ev{ + "name": nv("Mark McGwire", []diag.Location{{File: file, Line: 4, Column: 9}}), + "hr": nv(65, []diag.Location{{File: file, Line: 5, Column: 9}}), + "avg": nv(0.278, []diag.Location{{File: file, Line: 6, Column: 9}}), + }, + []diag.Location{{File: file, Line: 4, Column: 3}}, + ), + nv( + map[string]ev{ + "name": nv("Sammy Sosa", []diag.Location{{File: file, Line: 8, Column: 9}}), + "hr": nv(63, []diag.Location{{File: file, Line: 9, Column: 9}}), + "avg": nv(0.288, []diag.Location{{File: file, Line: 10, Column: 9}}), + }, + []diag.Location{{File: file, Line: 8, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_5(t *testing.T) { + file := "testdata/yaml/spec_example_2.5.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + []ev{ + nv( + []ev{ + nv("name", []diag.Location{{File: file, Line: 3, Column: 4}}), + nv("hr", []diag.Location{{File: file, Line: 3, Column: 18}}), + nv("avg", []diag.Location{{File: file, Line: 3, Column: 22}}), + }, + []diag.Location{{File: file, Line: 3, Column: 3}}, + ), + nv( + []ev{ + nv("Mark McGwire", []diag.Location{{File: file, Line: 4, Column: 4}}), + nv(65, []diag.Location{{File: file, Line: 4, Column: 18}}), + nv(0.278, []diag.Location{{File: file, Line: 4, Column: 22}}), + }, + []diag.Location{{File: file, Line: 4, Column: 3}}, + ), + nv( + []ev{ + nv("Sammy Sosa", []diag.Location{{File: file, Line: 5, Column: 4}}), + nv(63, []diag.Location{{File: file, Line: 5, Column: 18}}), + nv(0.288, []diag.Location{{File: file, Line: 5, Column: 22}}), + }, + []diag.Location{{File: file, Line: 5, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_6(t *testing.T) { + file := "testdata/yaml/spec_example_2.6.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "Mark McGwire": nv( + map[string]ev{ + "hr": nv(65, []diag.Location{{File: file, Line: 3, Column: 20}}), + "avg": nv(0.278, []diag.Location{{File: file, Line: 3, Column: 29}}), + }, + []diag.Location{{File: file, Line: 3, Column: 15}}, + ), + "Sammy Sosa": nv( + map[string]ev{ + "hr": nv(63, []diag.Location{{File: file, Line: 5, Column: 9}}), + "avg": nv(0.288, []diag.Location{{File: file, Line: 6, Column: 10}}), + }, + []diag.Location{{File: file, Line: 4, Column: 13}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_7(t *testing.T) { + file := "testdata/yaml/spec_example_2.7.yml" + v, sv := loadExample(t, file) + + // Note: we do not support multiple documents in a single YAML file. + + assertDecoded(t, nv( + []ev{ + nv( + "Mark McGwire", + []diag.Location{{File: file, Line: 5, Column: 3}}, + ), + nv( + "Sammy Sosa", + []diag.Location{{File: file, Line: 6, Column: 3}}, + ), + nv( + "Ken Griffey", + []diag.Location{{File: file, Line: 7, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 5, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_8(t *testing.T) { + file := "testdata/yaml/spec_example_2.8.yml" + v, sv := loadExample(t, file) + + // Note: we do not support multiple documents in a single YAML file. + + assertDecoded(t, nv( + map[string]ev{ + "time": nv("20:03:20", []diag.Location{{File: file, Line: 4, Column: 7}}), + "player": nv("Sammy Sosa", []diag.Location{{File: file, Line: 5, Column: 9}}), + "action": nv("strike (miss)", []diag.Location{{File: file, Line: 6, Column: 9}}), + }, + []diag.Location{{File: file, Line: 4, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_9(t *testing.T) { + file := "testdata/yaml/spec_example_2.9.yml" + v, sv := loadExample(t, file) + + // Note: we do not support multiple documents in a single YAML file. + + assertDecoded(t, nv( + map[string]ev{ + "hr": nv( + []ev{ + nv("Mark McGwire", []diag.Location{{File: file, Line: 5, Column: 3}}), + nv("Sammy Sosa", []diag.Location{{File: file, Line: 6, Column: 3}}), + }, + []diag.Location{{File: file, Line: 5, Column: 1}}, + ), + "rbi": nv( + []ev{ + nv("Sammy Sosa", []diag.Location{{File: file, Line: 9, Column: 3}}), + nv("Ken Griffey", []diag.Location{{File: file, Line: 10, Column: 3}}), + }, + []diag.Location{{File: file, Line: 9, Column: 1}}, + ), + }, + []diag.Location{{File: file, Line: 4, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_10(t *testing.T) { + file := "testdata/yaml/spec_example_2.10.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "hr": nv( + []ev{ + nv("Mark McGwire", []diag.Location{{File: file, Line: 5, Column: 3}}), + nv("Sammy Sosa", []diag.Location{{File: file, Line: 7, Column: 3}}), + }, + []diag.Location{{File: file, Line: 5, Column: 1}}, + ), + "rbi": nv( + []ev{ + // The location for an anchored value refers to the anchor, not the reference. + // This is the same location as the anchor that appears in the "hr" mapping. + nv("Sammy Sosa", []diag.Location{{File: file, Line: 7, Column: 3}}), + nv("Ken Griffey", []diag.Location{{File: file, Line: 10, Column: 3}}), + }, + []diag.Location{{File: file, Line: 9, Column: 1}}, + ), + }, + []diag.Location{{File: file, Line: 4, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_11(t *testing.T) { + file := "testdata/yaml/spec_example_2.11.yml" + // Note: non-string mapping keys are not supported by "go.yaml.in/yaml/v3". + _, _, err := decodeFile(t, file) + assert.ErrorContains(t, err, `: key is not a scalar`) +} + +func TestYAMLSpecExample_2_12(t *testing.T) { + file := "testdata/yaml/spec_example_2.12.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + []ev{ + nv( + map[string]ev{ + "item": nv("Super Hoop", []diag.Location{{File: file, Line: 5, Column: 13}}), + "quantity": nv(1, []diag.Location{{File: file, Line: 6, Column: 13}}), + }, + []diag.Location{{File: file, Line: 5, Column: 3}}, + ), + nv( + map[string]ev{ + "item": nv("Basketball", []diag.Location{{File: file, Line: 7, Column: 13}}), + "quantity": nv(4, []diag.Location{{File: file, Line: 8, Column: 13}}), + }, + []diag.Location{{File: file, Line: 7, Column: 3}}, + ), + nv( + map[string]ev{ + "item": nv("Big Shoes", []diag.Location{{File: file, Line: 9, Column: 13}}), + "quantity": nv(1, []diag.Location{{File: file, Line: 10, Column: 13}}), + }, + []diag.Location{{File: file, Line: 9, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 5, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_13(t *testing.T) { + file := "testdata/yaml/spec_example_2.13.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + ``+ + `\//||\/||`+NL+ + "// || ||__"+NL, + []diag.Location{{File: file, Line: 4, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_14(t *testing.T) { + file := "testdata/yaml/spec_example_2.14.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + `Mark McGwire's year was crippled by a knee injury.`+NL, + []diag.Location{{File: file, Line: 3, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_15(t *testing.T) { + file := "testdata/yaml/spec_example_2.15.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + ``+ + `Sammy Sosa completed another fine season with great stats.`+NL+ + NL+ + ` 63 Home Runs`+NL+ + ` 0.288 Batting Average`+NL+ + NL+ + `What a year!`+NL, + []diag.Location{{File: file, Line: 3, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_16(t *testing.T) { + file := "testdata/yaml/spec_example_2.16.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "name": nv( + "Mark McGwire", + []diag.Location{{File: file, Line: 3, Column: 7}}, + ), + "accomplishment": nv( + `Mark set a major league home run record in 1998.`+NL, + []diag.Location{{File: file, Line: 4, Column: 17}}, + ), + "stats": nv( + ``+ + `65 Home Runs`+NL+ + `0.278 Batting Average`+NL, + []diag.Location{{File: file, Line: 7, Column: 8}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_17(t *testing.T) { + file := "testdata/yaml/spec_example_2.17.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "unicode": nv( + `Sosa did fine.`+"\u263A", + []diag.Location{{File: file, Line: 3, Column: 10}}, + ), + "control": nv( + "\b1998\t1999\t2000\n", + []diag.Location{{File: file, Line: 4, Column: 10}}, + ), + "hex esc": nv( + "\x0d\x0a is \r\n", + []diag.Location{{File: file, Line: 5, Column: 10}}, + ), + "single": nv( + `"Howdy!" he cried.`, + []diag.Location{{File: file, Line: 7, Column: 9}}, + ), + "quoted": nv( + ` # Not a 'comment'.`, + []diag.Location{{File: file, Line: 8, Column: 9}}, + ), + "tie-fighter": nv( + `|\-*-/|`, + []diag.Location{{File: file, Line: 9, Column: 14}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_18(t *testing.T) { + file := "testdata/yaml/spec_example_2.18.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "plain": nv( + `This unquoted scalar spans many lines.`, + []diag.Location{{File: file, Line: 4, Column: 3}}, + ), + "quoted": nv( + `So does this quoted scalar.`+NL, + []diag.Location{{File: file, Line: 7, Column: 9}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_19(t *testing.T) { + file := "testdata/yaml/spec_example_2.19.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "canonical": nv( + 12345, + []diag.Location{{File: file, Line: 3, Column: 12}}, + ), + "decimal": nv( + 12345, + []diag.Location{{File: file, Line: 4, Column: 10}}, + ), + "octal": nv( + 12, + []diag.Location{{File: file, Line: 5, Column: 8}}, + ), + "hexadecimal": nv( + 12, + []diag.Location{{File: file, Line: 6, Column: 14}}, + ), + "octal11": nv( + 12345, + []diag.Location{{File: file, Line: 15, Column: 10}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_20(t *testing.T) { + file := "testdata/yaml/spec_example_2.20.yml" + v, sv := loadExample(t, file) + + // Equality assertion doesn't work with NaNs. + // See https://github.com/stretchr/testify/issues/624. + // + // Remove the NaN entry. + m := v.(map[string]any) + nan, ok := m["not a number"].(float64) + require.True(t, ok) + assert.True(t, math.IsNaN(nan)) + delete(m, "not a number") + + assertDecoded(t, nv( + map[string]ev{ + "canonical": nv( + 1230.15, + []diag.Location{{File: file, Line: 3, Column: 12}}, + ), + "exponential": nv( + 1230.15, + []diag.Location{{File: file, Line: 4, Column: 14}}, + ), + "fixed": nv( + 1230.15, + []diag.Location{{File: file, Line: 5, Column: 8}}, + ), + "negative infinity": nv( + math.Inf(-1), + []diag.Location{{File: file, Line: 6, Column: 20}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_21(t *testing.T) { + file := "testdata/yaml/spec_example_2.21.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "null": nv( + nil, + []diag.Location{{File: file, Line: 3, Column: 6}}, + ), + "booleans": nv( + []ev{ + nv(true, []diag.Location{{File: file, Line: 4, Column: 13}}), + nv(false, []diag.Location{{File: file, Line: 4, Column: 19}}), + }, + []diag.Location{{File: file, Line: 4, Column: 11}}, + ), + "string": nv( + "012345", + []diag.Location{{File: file, Line: 5, Column: 9}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_22(t *testing.T) { + file := "testdata/yaml/spec_example_2.22.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "canonical": nv( + "2001-12-15T02:59:43.1Z", + []diag.Location{{File: file, Line: 3, Column: 12}}, + ), + "iso8601": nv( + "2001-12-14t21:59:43.10-05:00", + []diag.Location{{File: file, Line: 4, Column: 10}}, + ), + "spaced": nv( + // This is parsed as a string, not a timestamp, + // both by "go.yaml.in/yaml/v3" and by our implementation. + "2001-12-14 21:59:43.10 -5", + []diag.Location{{File: file, Line: 5, Column: 9}}, + ), + "date": nv( + "2002-12-14", + []diag.Location{{File: file, Line: 6, Column: 7}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 1}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_23(t *testing.T) { + file := "testdata/yaml/spec_example_2.23.yml" + // Note: the !!binary tag is not supported by us. + _, _, err := decodeFile(t, file) + assert.ErrorContains(t, err, `: unknown tag: !!binary`) +} + +func TestYAMLSpecExample_2_24(t *testing.T) { + file := "testdata/yaml/spec_example_2.24.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + []ev{ + nv( + map[string]ev{ + "center": nv( + map[string]ev{ + "x": nv(73, []diag.Location{{File: file, Line: 8, Column: 23}}), + "y": nv(129, []diag.Location{{File: file, Line: 8, Column: 30}}), + }, + []diag.Location{{File: file, Line: 8, Column: 11}}, + ), + "radius": nv(7, []diag.Location{{File: file, Line: 9, Column: 11}}), + }, + []diag.Location{{File: file, Line: 7, Column: 3}}, + ), + nv( + map[string]ev{ + "start": nv( + map[string]ev{ + "x": nv(73, []diag.Location{{File: file, Line: 8, Column: 23}}), + "y": nv(129, []diag.Location{{File: file, Line: 8, Column: 30}}), + }, + []diag.Location{{File: file, Line: 8, Column: 11}}, + ), + "finish": nv( + map[string]ev{ + "x": nv(89, []diag.Location{{File: file, Line: 12, Column: 16}}), + "y": nv(102, []diag.Location{{File: file, Line: 12, Column: 23}}), + }, + []diag.Location{{File: file, Line: 12, Column: 11}}, + ), + }, + []diag.Location{{File: file, Line: 10, Column: 3}}, + ), + nv( + map[string]ev{ + "start": nv( + map[string]ev{ + "x": nv(73, []diag.Location{{File: file, Line: 8, Column: 23}}), + "y": nv(129, []diag.Location{{File: file, Line: 8, Column: 30}}), + }, + []diag.Location{{File: file, Line: 8, Column: 11}}, + ), + "color": nv(16772795, []diag.Location{{File: file, Line: 15, Column: 10}}), + "text": nv("Pretty vector drawing.", []diag.Location{{File: file, Line: 16, Column: 9}}), + }, + []diag.Location{{File: file, Line: 13, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 4, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_25(t *testing.T) { + file := "testdata/yaml/spec_example_2.25.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "Mark McGwire": nv(nil, []diag.Location{{File: file, Line: 8, Column: 1}}), + "Sammy Sosa": nv(nil, []diag.Location{{File: file, Line: 9, Column: 1}}), + "Ken Griffey": nv(nil, []diag.Location{{File: file, Line: 10, Column: 1}}), + }, + []diag.Location{{File: file, Line: 6, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_26(t *testing.T) { + file := "testdata/yaml/spec_example_2.26.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + []ev{ + nv( + map[string]ev{ + "Mark McGwire": nv(65, []diag.Location{{File: file, Line: 7, Column: 17}}), + }, + []diag.Location{{File: file, Line: 7, Column: 3}}, + ), + nv( + map[string]ev{ + "Sammy Sosa": nv(63, []diag.Location{{File: file, Line: 8, Column: 15}}), + }, + []diag.Location{{File: file, Line: 8, Column: 3}}, + ), + nv( + map[string]ev{ + "Ken Griffey": nv(58, []diag.Location{{File: file, Line: 9, Column: 16}}), + }, + []diag.Location{{File: file, Line: 9, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 6, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_27(t *testing.T) { + file := "testdata/yaml/spec_example_2.27.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "invoice": nv( + 34843, + []diag.Location{{File: file, Line: 4, Column: 10}}, + ), + "date": nv( + "2001-01-23", + []diag.Location{{File: file, Line: 5, Column: 10}}, + ), + "bill-to": nv( + map[string]ev{ + "given": nv( + "Chris", + []diag.Location{{File: file, Line: 7, Column: 12}}, + ), + "family": nv( + "Dumars", + []diag.Location{{File: file, Line: 8, Column: 12}}, + ), + "address": nv( + map[string]ev{ + "lines": nv( + "458 Walkman Dr.\nSuite #292\n", + []diag.Location{{File: file, Line: 10, Column: 12}}, + ), + "city": nv( + "Royal Oak", + []diag.Location{{File: file, Line: 13, Column: 15}}, + ), + "state": nv( + "MI", + []diag.Location{{File: file, Line: 14, Column: 15}}, + ), + "postal": nv( + 48046, + []diag.Location{{File: file, Line: 15, Column: 15}}, + ), + }, + []diag.Location{{File: file, Line: 10, Column: 5}}, + ), + }, + []diag.Location{{File: file, Line: 6, Column: 10}}, + ), + "ship-to": nv( + map[string]ev{ + "given": nv( + "Chris", + []diag.Location{{File: file, Line: 7, Column: 12}}, + ), + "family": nv( + "Dumars", + []diag.Location{{File: file, Line: 8, Column: 12}}, + ), + "address": nv( + map[string]ev{ + "lines": nv( + "458 Walkman Dr.\nSuite #292\n", + []diag.Location{{File: file, Line: 10, Column: 12}}, + ), + "city": nv( + "Royal Oak", + []diag.Location{{File: file, Line: 13, Column: 15}}, + ), + "state": nv( + "MI", + []diag.Location{{File: file, Line: 14, Column: 15}}, + ), + "postal": nv( + 48046, + []diag.Location{{File: file, Line: 15, Column: 15}}, + ), + }, + []diag.Location{{File: file, Line: 10, Column: 5}}, + ), + }, + []diag.Location{{File: file, Line: 6, Column: 10}}, + ), + "product": nv( + []ev{ + nv( + map[string]ev{ + "sku": nv( + "BL394D", + []diag.Location{{File: file, Line: 18, Column: 17}}, + ), + "quantity": nv( + 4, + []diag.Location{{File: file, Line: 19, Column: 17}}, + ), + "description": nv( + "Basketball", + []diag.Location{{File: file, Line: 20, Column: 17}}, + ), + "price": nv( + 450.0, + []diag.Location{{File: file, Line: 21, Column: 17}}, + ), + }, + []diag.Location{{File: file, Line: 18, Column: 3}}, + ), nv( + map[string]ev{ + "sku": nv( + "BL4438H", + []diag.Location{{File: file, Line: 22, Column: 17}}, + ), + "quantity": nv( + 1, + []diag.Location{{File: file, Line: 23, Column: 17}}, + ), + "description": nv( + "Super Hoop", + []diag.Location{{File: file, Line: 24, Column: 17}}, + ), + "price": nv( + 2392.0, + []diag.Location{{File: file, Line: 25, Column: 17}}, + ), + }, + []diag.Location{{File: file, Line: 22, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 18, Column: 1}}, + ), + "tax": nv( + 251.42, + []diag.Location{{File: file, Line: 26, Column: 8}}, + ), + "total": nv( + 4443.52, + []diag.Location{{File: file, Line: 27, Column: 8}}, + ), + "comments": nv( + "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.", + []diag.Location{{File: file, Line: 29, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 3, Column: 5}}, + ), v, sv) +} + +func TestYAMLSpecExample_2_28(t *testing.T) { + file := "testdata/yaml/spec_example_2.28.yml" + v, sv := loadExample(t, file) + + assertDecoded(t, nv( + map[string]ev{ + "Time": nv( + "2001-11-23 15:01:42 -5", + []diag.Location{{File: file, Line: 4, Column: 7}}, + ), + "User": nv( + "ed", + []diag.Location{{File: file, Line: 5, Column: 7}}, + ), + "Warning": nv( + "This is an error message for the log file", + []diag.Location{{File: file, Line: 7, Column: 3}}, + ), + }, + []diag.Location{{File: file, Line: 4, Column: 1}}, + ), v, sv) +} diff --git a/libs/structs/structvar/yaml_test.go b/libs/structs/structvar/yaml_test.go new file mode 100644 index 00000000000..d75410f6fc0 --- /dev/null +++ b/libs/structs/structvar/yaml_test.go @@ -0,0 +1,159 @@ +package structvar_test + +import ( + "bytes" + "fmt" + "os" + "testing" + "time" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/structs/structvar" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" +) + +// ev is an expected value with the locations it is expected to have. +// val is a scalar, a []ev or a map[string]ev. +type ev struct { + val any + locs []diag.Location +} + +func nv(val any, locs []diag.Location) ev { + return ev{val: val, locs: locs} +} + +// plain converts the expected value to the plain Go value the decoder produces. +func (e ev) plain() any { + switch v := e.val.(type) { + case []ev: + out := make([]any, len(v)) + for i, x := range v { + out[i] = x.plain() + } + return out + case map[string]ev: + out := make(map[string]any, len(v)) + for k, x := range v { + out[k] = x.plain() + } + return out + default: + return v + } +} + +// assertLocations checks the locations of every value in the expected tree. +func assertLocations(t *testing.T, l *structvar.Locations, e ev, where string) { + assert.Equal(t, e.locs, l.Get(), "locations of %s", where) + switch v := e.val.(type) { + case []ev: + for i, x := range v { + assertLocations(t, l.Index(i), x, fmt.Sprintf("%s[%d]", where, i)) + } + case map[string]ev: + for k, x := range v { + assertLocations(t, l.Key(k), x, where+"."+k) + } + } +} + +func decodeFile(t *testing.T, file string) (any, *structvar.StructVar, error) { + input, err := os.ReadFile(file) + require.NoError(t, err) + var v any + sv, _, err := structvar.DecodeYAML(file, bytes.NewBuffer(input), &v) + return v, sv, err +} + +func loadExample(t *testing.T, file string) (any, *structvar.StructVar) { + v, sv, err := decodeFile(t, file) + require.NoError(t, err) + return v, sv +} + +// assertDecoded checks the decoded value and the locations of all its parts. +func assertDecoded(t *testing.T, expected ev, v any, sv *structvar.StructVar) { + assert.Equal(t, expected.plain(), v) + assertLocations(t, sv.Locations, expected, "root") +} + +// equalToReference compares the decoded value to the one decoded by yaml.v3. +// Timestamps are kept as the string they were written as, so only their presence is compared. +func equalToReference(t *testing.T, ref, v any) { + switch r := ref.(type) { + case time.Time: + assert.IsType(t, "", v) + case map[string]any: + got, ok := v.(map[string]any) + require.True(t, ok, "expected a map, got %T", v) + require.Len(t, got, len(r)) + for k, x := range r { + equalToReference(t, x, got[k]) + } + case []any: + got, ok := v.([]any) + require.True(t, ok, "expected a sequence, got %T", v) + require.Len(t, got, len(r)) + for i, x := range r { + equalToReference(t, x, got[i]) + } + default: + assert.EqualValues(t, ref, v) + } +} + +// loadYAML decodes the file and checks that the result matches yaml.v3. +func loadYAML(t *testing.T, path string) (any, *structvar.StructVar) { + input, err := os.ReadFile(path) + require.NoError(t, err) + + var ref any + err = yaml.Unmarshal(input, &ref) + require.NoError(t, err) + + v, sv := loadExample(t, path) + require.NotNil(t, sv) + + equalToReference(t, ref, v) + return v, sv +} + +// at returns the value at the path of keys (strings) and indexes (ints). +func at(t *testing.T, v any, path ...any) any { + for _, p := range path { + switch p := p.(type) { + case string: + m, ok := v.(map[string]any) + require.True(t, ok, "expected a map at %q, got %T", p, v) + v = m[p] + case int: + s, ok := v.([]any) + require.True(t, ok, "expected a sequence at %d, got %T", p, v) + v = s[p] + } + } + return v +} + +// locsAt returns the locations of the value at the path of keys (strings) and indexes (ints). +func locsAt(sv *structvar.StructVar, path ...any) []diag.Location { + l := sv.Locations + for _, p := range path { + switch p := p.(type) { + case string: + l = l.Key(p) + case int: + l = l.Index(p) + } + } + return l.Get() +} + +func TestYAMLEmpty(t *testing.T) { + v, sv := loadYAML(t, "testdata/yaml/empty.yml") + assert.Nil(t, v) + assert.Empty(t, sv.Locations.Get()) +} diff --git a/libs/structs/structyaml/structyaml.go b/libs/structs/structyaml/structyaml.go new file mode 100644 index 00000000000..9c88f6fb21c --- /dev/null +++ b/libs/structs/structyaml/structyaml.go @@ -0,0 +1,187 @@ +// Package structyaml writes typed values as YAML with a controlled key order. +package structyaml + +import ( + "cmp" + "fmt" + "os" + "path/filepath" + "slices" + "strconv" + + "github.com/databricks/cli/libs/structs/structvar" + "go.yaml.in/yaml/v3" +) + +// Pair is an entry of a [Map]. +type Pair struct { + Key string + Value any +} + +// Map is a YAML mapping that keeps the order of its keys. Values are maps (Map or +// map[string]any, keys sorted), sequences ([]any or []string), strings, bools, ints, floats or nil. +type Map []Pair + +// M returns a Map of the given alternating keys (strings) and values. +func M(kv ...any) Map { + m := make(Map, 0, len(kv)/2) + for i := 0; i < len(kv); i += 2 { + m = append(m, Pair{Key: kv[i].(string), Value: kv[i+1]}) + } + return m +} + +// Add appends an entry. +func (m *Map) Add(key string, value any) { + *m = append(*m, Pair{Key: key, Value: value}) +} + +// Order moves the given keys to the front, in the given order. The other keys keep +// their relative order. +func (m *Map) Order(keys ...string) Map { + index := func(p Pair) int { + if i := slices.Index(keys, p.Key); i >= 0 { + return i + } + return len(keys) + } + slices.SortStableFunc(*m, func(a, b Pair) int { return cmp.Compare(index(a), index(b)) }) + return *m +} + +// Struct returns the fields of the struct (or map) v that are set, except skip, as a +// Map with the keys sorted alphabetically, and so are the keys of maps and structs +// nested in maps. Sequence elements keep the field order of their type. +func Struct(v any, skip ...string) (Map, error) { + x := structvar.NewView(v, nil, nil) + if x.Kind() != structvar.KindMap { + return nil, fmt.Errorf("expected map, got %s", x.Kind()) + } + m := fromView(x, true).(Map) + return slices.DeleteFunc(m, func(p Pair) bool { return slices.Contains(skip, p.Key) }), nil +} + +// Value returns the typed value v as a value for a Map, in field order. Unset +// fields are dropped. It returns nil if v is unset. +func Value(v any) any { + return fromView(structvar.NewView(v, nil, nil), false) +} + +func fromView(x structvar.View, sorted bool) any { + switch x.Kind() { + case structvar.KindMap: + var m Map + for k, c := range x.MapItems() { + m.Add(k, fromView(c, sorted)) + } + if sorted { + slices.SortStableFunc(m, func(a, b Pair) int { return cmp.Compare(a.Key, b.Key) }) + } + return m + case structvar.KindSequence: + s := []any{} //nolint:gocritic // an empty sequence is written as [], not null + for _, c := range x.Sequence() { + s = append(s, fromView(c, false)) + } + return s + default: + return x.AsAny() + } +} + +// Node returns v as a YAML node. Values of keys in styles (and everything below +// them) get that style. +func Node(v any, styles map[string]yaml.Style) *yaml.Node { + return toNode(v, 0, styles) +} + +func toNode(v any, style yaml.Style, styles map[string]yaml.Style) *yaml.Node { + switch v := v.(type) { + case Map: + n := &yaml.Node{Kind: yaml.MappingNode, Style: style} + for _, p := range v { + s, ok := styles[p.Key] + if !ok { + s = style + } + n.Content = append(n.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: p.Key, Style: style}, toNode(p.Value, s, styles)) + } + return n + case map[string]any: + m := make(Map, 0, len(v)) + for k, e := range v { + m.Add(k, e) + } + slices.SortFunc(m, func(a, b Pair) int { return cmp.Compare(a.Key, b.Key) }) + return toNode(m, style, styles) + case []any: + n := &yaml.Node{Kind: yaml.SequenceNode, Style: style} + for _, e := range v { + n.Content = append(n.Content, toNode(e, style, styles)) + } + return n + case []string: + n := &yaml.Node{Kind: yaml.SequenceNode, Style: style} + for _, e := range v { + n.Content = append(n.Content, toNode(e, style, styles)) + } + return n + case nil: + return &yaml.Node{Kind: yaml.ScalarNode, Value: "null", Style: style} + case string: + // A string that reads as another scalar (bool, number or empty) is quoted. + if v == "" || v == "true" || v == "false" || isNumber(v) { + style = yaml.DoubleQuotedStyle + } + return &yaml.Node{Kind: yaml.ScalarNode, Value: v, Style: style} + case bool: + return &yaml.Node{Kind: yaml.ScalarNode, Value: strconv.FormatBool(v), Style: style} + case int: + return &yaml.Node{Kind: yaml.ScalarNode, Value: strconv.Itoa(v), Style: style} + case int64: + return &yaml.Node{Kind: yaml.ScalarNode, Value: strconv.FormatInt(v, 10), Style: style} + case float64: + return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprint(v), Style: style} + default: + // Panic because we only want to deal with known types. + panic(fmt.Sprintf("invalid type: %T", v)) + } +} + +func isNumber(s string) bool { + if _, err := strconv.ParseInt(s, 0, 64); err == nil { + return true + } + _, err := strconv.ParseFloat(s, 64) + return err == nil +} + +// Save writes v as YAML to path, creating parent directories. It fails if path exists +// and force is not set. See [Node] for styles. +func Save(path string, v any, force bool, styles map[string]yaml.Style) error { + err := os.MkdirAll(filepath.Dir(path), 0o755) + if err != nil { + return err + } + + info, err := os.Stat(path) + if err == nil { + if info.IsDir() { + return fmt.Errorf("%s is a directory", path) + } + if !force { + return fmt.Errorf("%s already exists. Use --force to overwrite", path) + } + } + + file, err := os.Create(path) + if err != nil { + return err + } + defer file.Close() + + enc := yaml.NewEncoder(file) + enc.SetIndent(2) + return enc.Encode(Node(v, styles)) +} diff --git a/libs/structs/structyaml/structyaml_test.go b/libs/structs/structyaml/structyaml_test.go new file mode 100644 index 00000000000..6c62fcfcac2 --- /dev/null +++ b/libs/structs/structyaml/structyaml_test.go @@ -0,0 +1,163 @@ +package structyaml + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.yaml.in/yaml/v3" +) + +type testStruct struct { + Name string `json:"name"` + Map map[string]string `json:"map"` + List []string `json:"list"` + LongNameField string `json:"long_name_field"` + ForceSendFields []string `json:"-"` + Format string `json:"format"` +} + +func newTestStruct() *testStruct { + return &testStruct{ + Name: "test", + Map: map[string]string{"key2": "value2", "key1": "value1"}, + List: []string{"a", "b", "c"}, + ForceSendFields: []string{"Name"}, + LongNameField: "long name goes here", + } +} + +func TestStruct(t *testing.T) { + result, err := Struct(newTestStruct(), "format") + require.NoError(t, err) + assert.Equal(t, M( + "list", []any{"a", "b", "c"}, + "long_name_field", "long name goes here", + "map", M("key1", "value1", "key2", "value2"), + "name", "test", + ), result) +} + +func TestStructWithOrder(t *testing.T) { + result, err := Struct(newTestStruct(), "format") + require.NoError(t, err) + assert.Equal(t, M( + "list", []any{"a", "b", "c"}, + "name", "test", + "map", M("key1", "value1", "key2", "value2"), + "long_name_field", "long name goes here", + ), result.Order("list", "name", "map")) +} + +func TestStructNotAMap(t *testing.T) { + _, err := Struct([]string{"a"}) + assert.ErrorContains(t, err, "expected map") +} + +func TestOrderUnknownKeysKeepOrder(t *testing.T) { + m := M("d", 1, "c", 2, "b", 3, "a", 4) + assert.Equal(t, M("a", 4, "b", 3, "d", 1, "c", 2), m.Order("a", "x", "b")) +} + +func TestValueKeepsFieldOrder(t *testing.T) { + type inner struct { + Zeta string `json:"zeta"` + Alpha string `json:"alpha"` + } + assert.Equal(t, []any{M("zeta", "z", "alpha", "a")}, Value([]inner{{Zeta: "z", Alpha: "a"}})) + assert.Nil(t, Value([]inner(nil))) +} + +func TestNodeScalars(t *testing.T) { + tests := []struct { + value any + want string + style yaml.Style + }{ + {nil, "null", 0}, + {1, "1", 0}, + {int64(291), "291", 0}, + {1.0, "1", 0}, + {true, "true", 0}, + {"value", "value", 0}, + {"0x123", "0x123", yaml.DoubleQuotedStyle}, + {"0b101", "0b101", yaml.DoubleQuotedStyle}, + {"0123", "0123", yaml.DoubleQuotedStyle}, + {"1.0", "1.0", yaml.DoubleQuotedStyle}, + {"true", "true", yaml.DoubleQuotedStyle}, + {"", "", yaml.DoubleQuotedStyle}, + } + for _, tt := range tests { + n := Node(tt.value, nil) + assert.Equal(t, yaml.ScalarNode, n.Kind) + assert.Equal(t, tt.want, n.Value) + assert.Equal(t, tt.style, n.Style, "%v", tt.value) + } +} + +func TestNodeSequence(t *testing.T) { + for _, v := range []any{[]any{"value1", "value2"}, []string{"value1", "value2"}} { + n := Node(v, nil) + assert.Equal(t, yaml.SequenceNode, n.Kind) + assert.Equal(t, "value1", n.Content[0].Value) + assert.Equal(t, "value2", n.Content[1].Value) + } +} + +func TestNodeMapKeepsOrder(t *testing.T) { + n := Node(M("key3", "value3", "key1", "value1"), nil) + assert.Equal(t, yaml.MappingNode, n.Kind) + assert.Equal(t, "key3", n.Content[0].Value) + assert.Equal(t, "value3", n.Content[1].Value) + assert.Equal(t, "key1", n.Content[2].Value) + assert.Equal(t, "value1", n.Content[3].Value) +} + +func TestNodeGoMapSortsKeys(t *testing.T) { + n := Node(map[string]any{"b": 1, "a": M("y", 1, "x", 2)}, nil) + assert.Equal(t, "a", n.Content[0].Value) + assert.Equal(t, "y", n.Content[1].Content[0].Value) + assert.Equal(t, "b", n.Content[2].Value) +} + +func TestNodeStyle(t *testing.T) { + styles := map[string]yaml.Style{"styled": yaml.DoubleQuotedStyle} + n := Node(M( + "styled", M("key1", "value1", "key2", "value2"), + "unstyled", M("key3", "value3"), + ), styles) + + styled := n.Content[1] + assert.Equal(t, yaml.MappingNode, styled.Kind) + for _, c := range styled.Content { + assert.Equal(t, yaml.DoubleQuotedStyle, c.Style) + } + + unstyled := n.Content[3] + assert.Equal(t, yaml.MappingNode, unstyled.Kind) + for _, c := range unstyled.Content { + assert.Equal(t, yaml.Style(0), c.Style) + } +} + +func TestSave(t *testing.T) { + path := filepath.Join(t.TempDir(), "dir", "out.yml") + require.NoError(t, Save(path, M("b", "1", "a", M("c", []any{1, true})), false, nil)) + + data, err := os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, "b: \"1\"\na:\n c:\n - 1\n - true\n", string(data)) + + err = Save(path, M("a", 1), false, nil) + assert.ErrorContains(t, err, "already exists. Use --force to overwrite") + + require.NoError(t, Save(path, M("a", 1), true, nil)) + data, err = os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, "a: 1\n", string(data)) + + err = Save(filepath.Dir(path), M("a", 1), true, nil) + assert.ErrorContains(t, err, "is a directory") +} diff --git a/python/Taskfile.yml b/python/Taskfile.yml index efc7cac1b8d..a6e23b2e259 100644 --- a/python/Taskfile.yml +++ b/python/Taskfile.yml @@ -22,7 +22,7 @@ tasks: desc: Run pydabs tests sources: - "**/*.py" - - ../libs/dyn/dynvar/testdata/reference_vectors.json + - ../libs/structs/structvar/testdata/reference_vectors.json - pyproject.toml - uv.lock - README.md diff --git a/python/codegen/codegen/packages.py b/python/codegen/codegen/packages.py index 46674518dca..69d4944fa9a 100644 --- a/python/codegen/codegen/packages.py +++ b/python/codegen/codegen/packages.py @@ -63,7 +63,7 @@ def _load_resource_namespace() -> dict[str, str]: # pass through untyped. The Go config mutators validate the value. "interface": "Any", # time.Time is a scalar serialized as an RFC3339 string; the Go side models - # it as a string too (see libs/dyn/convert/sdk_native_types.go). + # it as a string too (see libs/structs/structvar/struct_info.go). "time.Time": "str", # duration.Duration is a scalar serialized as a seconds string (e.g. "3600s"). "duration.Duration": "str", diff --git a/python/databricks/bundles/core/_transform.py b/python/databricks/bundles/core/_transform.py index 8e3b4a6ec12..c39b3555869 100644 --- a/python/databricks/bundles/core/_transform.py +++ b/python/databricks/bundles/core/_transform.py @@ -19,7 +19,7 @@ _T = TypeVar("_T") -# Same strings as normalizeBool in libs/dyn/convert/normalize.go. +# Same strings as the bool decoding in libs/structs/structvar/decode.go. # See https://yaml.org/type/bool.html. _TRUE_STRINGS = [ "true", @@ -311,8 +311,8 @@ def _unwrap_variable(tpe: type) -> Optional[type]: # Regex for string corresponding to variables. # -# The source of truth is regex in libs/dyn/dynvar/ref.go. -# Behavioral parity is enforced by libs/dyn/dynvar/testdata/reference_vectors.json. +# The source of truth is regex in libs/structs/structvar/ref.go. +# Behavioral parity is enforced by libs/structs/structvar/testdata/reference_vectors.json. # # The (?