From d9d197bc7a8e7ccc2bf97cb6f54b5e8850f2f4bb Mon Sep 17 00:00:00 2001 From: Divyansh Vijayvergia Date: Thu, 28 May 2026 17:26:32 +0000 Subject: [PATCH 1/4] databricks experimental open: emit ?w= URL query parameter The Databricks UI is migrating from ?o= to ?w= as the SPOG URL query parameter, matching the recent workspace addressing header rename. Switch BuildResourceURL in libs/workspaceurls to write ?w= when appending the workspace identifier. This affects URLs printed by databricks experimental open. The legacy ?o= URL spelling remains a valid input wherever the CLI parses host URLs; only the emitted form changes. bundle/config/mutator/initialize_urls.go also adds a workspace identifier query parameter, but it does so before calling ResourceURL with an already-built baseURL, so this change does not affect bundle output. --- acceptance/experimental/open/output.txt | 4 ++-- cmd/experimental/workspace_open_test.go | 10 +++++----- libs/workspaceurls/urls.go | 8 ++++---- libs/workspaceurls/urls_test.go | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/acceptance/experimental/open/output.txt b/acceptance/experimental/open/output.txt index a83e0676fe8..0da1fdfec8b 100644 --- a/acceptance/experimental/open/output.txt +++ b/acceptance/experimental/open/output.txt @@ -1,11 +1,11 @@ === print URL for a job >>> [CLI] experimental open --url jobs 123 -[DATABRICKS_URL]/jobs/123?o=[NUMID] +[DATABRICKS_URL]/jobs/123?w=[NUMID] === print URL for a notebook >>> [CLI] experimental open --url notebooks 12345 -[DATABRICKS_URL]/?o=[NUMID]#notebook/12345 +[DATABRICKS_URL]/?w=[NUMID]#notebook/12345 === unknown resource type >>> [CLI] experimental open --url unknown 123 diff --git a/cmd/experimental/workspace_open_test.go b/cmd/experimental/workspace_open_test.go index 7ec8e937ece..b24ed88f4c6 100644 --- a/cmd/experimental/workspace_open_test.go +++ b/cmd/experimental/workspace_open_test.go @@ -79,26 +79,26 @@ func TestBuildWorkspaceURLHostWithTrailingSlash(t *testing.T) { func TestBuildWorkspaceURLWithWorkspaceID(t *testing.T) { got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "jobs", "123", 123456) require.NoError(t, err) - assert.Equal(t, "https://myworkspace.databricks.com/jobs/123?o=123456", got) + assert.Equal(t, "https://myworkspace.databricks.com/jobs/123?w=123456", got) } func TestBuildWorkspaceURLWithWorkspaceIDInHostname(t *testing.T) { got, err := workspaceurls.BuildResourceURL("https://adb-123456.azuredatabricks.net", "jobs", "123", 123456) require.NoError(t, err) - // Workspace ID is already in the hostname, so ?o= should not be appended. + // Workspace ID is already in the hostname, so ?w= should not be appended. assert.Equal(t, "https://adb-123456.azuredatabricks.net/jobs/123", got) } func TestBuildWorkspaceURLWithWorkspaceIDInVanityHostname(t *testing.T) { got, err := workspaceurls.BuildResourceURL("https://workspace-123456.example.com", "jobs", "123", 123456) require.NoError(t, err) - assert.Equal(t, "https://workspace-123456.example.com/jobs/123?o=123456", got) + assert.Equal(t, "https://workspace-123456.example.com/jobs/123?w=123456", got) } func TestBuildWorkspaceURLFragmentWithWorkspaceID(t *testing.T) { got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "notebooks", "12345", 789) require.NoError(t, err) - assert.Equal(t, "https://myworkspace.databricks.com/?o=789#notebook/12345", got) + assert.Equal(t, "https://myworkspace.databricks.com/?w=789#notebook/12345", got) } func TestWorkspaceOpenCommandCompletion(t *testing.T) { @@ -221,7 +221,7 @@ func TestWorkspaceOpenCommandURLFlag(t *testing.T) { require.NoError(t, err) assert.False(t, browserOpened) - assert.Equal(t, "https://myworkspace.databricks.com/jobs/123?o=789\n", stdout.String()) + assert.Equal(t, "https://myworkspace.databricks.com/jobs/123?w=789\n", stdout.String()) assert.Equal(t, "", stderr.String()) } diff --git a/libs/workspaceurls/urls.go b/libs/workspaceurls/urls.go index c6ac8fdd7cd..fa0fc86c4a8 100644 --- a/libs/workspaceurls/urls.go +++ b/libs/workspaceurls/urls.go @@ -62,7 +62,7 @@ func ResourceURL(baseURL url.URL, resourceType, id string) string { } // BuildResourceURL constructs a full workspace URL from a host string, resource -// type name, ID, and workspace ID. It parses the host, appends ?o= +// type name, ID, and workspace ID. It parses the host, appends ?w= // when needed, and formats the resource path. func BuildResourceURL(host, resourceType, id string, workspaceID int64) (string, error) { baseURL, err := workspaceBaseURL(host, workspaceID) @@ -94,13 +94,13 @@ func workspaceBaseURL(host string, workspaceID int64) (*url.URL, error) { return baseURL, nil } - orgID := strconv.FormatInt(workspaceID, 10) - if hasWorkspaceIDInHostname(baseURL.Hostname(), orgID) { + wsID := strconv.FormatInt(workspaceID, 10) + if hasWorkspaceIDInHostname(baseURL.Hostname(), wsID) { return baseURL, nil } values := baseURL.Query() - values.Add("o", orgID) + values.Add("w", wsID) baseURL.RawQuery = values.Encode() return baseURL, nil diff --git a/libs/workspaceurls/urls_test.go b/libs/workspaceurls/urls_test.go index fd28ff44c2d..5107d0a21c3 100644 --- a/libs/workspaceurls/urls_test.go +++ b/libs/workspaceurls/urls_test.go @@ -49,11 +49,11 @@ func TestWorkspaceBaseURL(t *testing.T) { expected string }{ {"no workspace ID", "https://myworkspace.databricks.com", 0, "https://myworkspace.databricks.com"}, - {"with workspace ID", "https://myworkspace.databricks.com", 123456, "https://myworkspace.databricks.com?o=123456"}, + {"with workspace ID", "https://myworkspace.databricks.com", 123456, "https://myworkspace.databricks.com?w=123456"}, {"trailing slash stripped", "https://myworkspace.databricks.com/", 0, "https://myworkspace.databricks.com/"}, - {"trailing slash with workspace ID", "https://myworkspace.databricks.com/", 789, "https://myworkspace.databricks.com/?o=789"}, + {"trailing slash with workspace ID", "https://myworkspace.databricks.com/", 789, "https://myworkspace.databricks.com/?w=789"}, {"adb hostname skips query param", "https://adb-123456.azuredatabricks.net", 123456, "https://adb-123456.azuredatabricks.net"}, - {"adb hostname mismatch adds param", "https://adb-999.azuredatabricks.net", 123456, "https://adb-999.azuredatabricks.net?o=123456"}, + {"adb hostname mismatch adds param", "https://adb-999.azuredatabricks.net", 123456, "https://adb-999.azuredatabricks.net?w=123456"}, } for _, tt := range tests { @@ -80,9 +80,9 @@ func TestBuildResourceURL(t *testing.T) { expected string }{ {"simple path", "https://host.com", "jobs", "123", 0, "https://host.com/jobs/123"}, - {"path with workspace ID", "https://host.com", "jobs", "123", 456, "https://host.com/jobs/123?o=456"}, + {"path with workspace ID", "https://host.com", "jobs", "123", 456, "https://host.com/jobs/123?w=456"}, {"fragment pattern", "https://host.com", "notebooks", "12345", 0, "https://host.com/#notebook/12345"}, - {"fragment with workspace ID", "https://host.com", "notebooks", "12345", 789, "https://host.com/?o=789#notebook/12345"}, + {"fragment with workspace ID", "https://host.com", "notebooks", "12345", 789, "https://host.com/?w=789#notebook/12345"}, {"registered model normalizes dots", "https://host.com", "registered_models", "catalog.schema.model", 0, "https://host.com/explore/data/models/catalog/schema/model"}, } From f5b0e3e7e669a92110acc27167c30218f3813056 Mon Sep 17 00:00:00 2001 From: Divyansh Vijayvergia Date: Fri, 29 May 2026 13:40:56 +0000 Subject: [PATCH 2/4] Extend ?w= URL emission to bundle resource URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bundle/config/mutator/initialize_urls.go is the bundle-side counterpart of libs/workspaceurls/workspaceBaseURL: it appends ?o= to every resource URL printed by `databricks bundle summary`. Flip it to emit ?w= to match the new workspace addressing convention. The 12 bundle resource URL builders that consume the baseURL (alerts, apps, pipelines, jobs, dashboards, experiments, models, model serving, registered models, clusters, sql warehouses, vector search) all inherit the new spelling without code changes since they just append the resource path/fragment to the baseURL. Updates: - bundle/config/mutator/initialize_urls.go: emit ?w= instead of ?o=, rename local orgId → workspaceID, refresh the comment. - bundle/config/mutator/initialize_urls_test.go: ~10 expected-URL assertions flipped from ?o= to ?w=. - 18 acceptance/bundle/** golden files regenerated with ?w=. - 12 acceptance/bundle/** test.toml scrub patterns widened from \?o=... to \?[ow]=... so they match both the new (?w=) and legacy (?o=) URL parameter forms. `Run URL: ...?o=...` lines in run / deploy / spark task / integration_whl fixtures are intentionally left as ?o= — those URLs come from the platform API's RunPageUrl response field, not from CLI code, and the API response side has not migrated yet (asymmetric migration; the platform still echoes ?o= until the response-side rename ships). --- acceptance/bundle/bundle_tag/id/output.txt | 2 +- acceptance/bundle/bundle_tag/url/output.txt | 2 +- .../url_ref/out.summary.terraform.txt | 4 ++-- .../bundle/deploy/wal/chain-3-jobs/output.txt | 4 ++-- .../deploy/wal/corrupted-wal-entry/output.txt | 4 ++-- .../bind/database_instance/output.txt | 2 +- .../bind/pipelines/recreate/out.summary.json | 2 +- .../bind/pipelines/update/out.summary.json | 2 +- .../deployment/bind/sql_warehouse/output.txt | 2 +- acceptance/bundle/open/output.txt | 4 ++-- .../resource_deps/create_error/output.txt | 2 +- .../resources/alerts/with_file/test.toml | 4 ++-- .../dashboards/detect-change/test.toml | 4 ++-- .../single-instance/test.toml | 4 ++-- .../resources/jobs/check-metadata/test.toml | 4 ++-- .../resources/postgres_branches/test.toml | 4 ++-- .../resources/postgres_catalogs/test.toml | 4 ++-- .../resources/postgres_endpoints/test.toml | 4 ++-- .../resources/postgres_projects/test.toml | 4 ++-- .../postgres_synced_tables/test.toml | 4 ++-- .../resources/schemas/recreate/output.txt | 2 +- .../resources/sql_warehouses/output.txt | 4 ++-- .../synced_database_tables/basic/test.toml | 4 ++-- .../vector_search_endpoints/basic/output.txt | 4 ++-- .../vector_search_indexes/basic/output.txt | 4 ++-- .../volumes/change-comment/output.txt | 4 ++-- .../resources/volumes/change-name/output.txt | 2 +- .../volumes/change-schema-name/output.txt | 2 +- .../state/force_pull_commands/output.txt | 4 ++-- .../bundle/summary/modified_status/output.txt | 16 +++++++------- .../integration_classic/test.toml | 4 ++-- bundle/config/mutator/initialize_urls.go | 22 +++++++++---------- bundle/config/mutator/initialize_urls_test.go | 22 +++++++++---------- 33 files changed, 80 insertions(+), 80 deletions(-) diff --git a/acceptance/bundle/bundle_tag/id/output.txt b/acceptance/bundle/bundle_tag/id/output.txt index 604c2df9699..acfd8c3d1a1 100644 --- a/acceptance/bundle/bundle_tag/id/output.txt +++ b/acceptance/bundle/bundle_tag/id/output.txt @@ -68,7 +68,7 @@ Resources: Jobs: foo: Name: Untitled - URL: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: diff --git a/acceptance/bundle/bundle_tag/url/output.txt b/acceptance/bundle/bundle_tag/url/output.txt index 3e4536666c1..76d46c68fa7 100644 --- a/acceptance/bundle/bundle_tag/url/output.txt +++ b/acceptance/bundle/bundle_tag/url/output.txt @@ -68,7 +68,7 @@ Resources: Jobs: foo: Name: Untitled - URL: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: diff --git a/acceptance/bundle/bundle_tag/url_ref/out.summary.terraform.txt b/acceptance/bundle/bundle_tag/url_ref/out.summary.terraform.txt index e06a0d3041a..f9312d009ba 100644 --- a/acceptance/bundle/bundle_tag/url_ref/out.summary.terraform.txt +++ b/acceptance/bundle/bundle_tag/url_ref/out.summary.terraform.txt @@ -7,7 +7,7 @@ Resources: Jobs: bar: Name: Untitled - URL: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] foo: Name: Untitled - URL: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] diff --git a/acceptance/bundle/deploy/wal/chain-3-jobs/output.txt b/acceptance/bundle/deploy/wal/chain-3-jobs/output.txt index 7e04ba4dae3..f27bfaa3f2c 100644 --- a/acceptance/bundle/deploy/wal/chain-3-jobs/output.txt +++ b/acceptance/bundle/deploy/wal/chain-3-jobs/output.txt @@ -98,10 +98,10 @@ Resources: Jobs: job_01: Name: job-01 - URL: [DATABRICKS_URL]/jobs/[JOB_01_ID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[JOB_01_ID]?w=[NUMID] job_02: Name: job-02 - URL: [DATABRICKS_URL]/jobs/[JOB_02_ID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[JOB_02_ID]?w=[NUMID] job_03: Name: job-03 URL: (not deployed) diff --git a/acceptance/bundle/deploy/wal/corrupted-wal-entry/output.txt b/acceptance/bundle/deploy/wal/corrupted-wal-entry/output.txt index 1aee4fe481d..b29571ff666 100644 --- a/acceptance/bundle/deploy/wal/corrupted-wal-entry/output.txt +++ b/acceptance/bundle/deploy/wal/corrupted-wal-entry/output.txt @@ -23,10 +23,10 @@ Resources: Jobs: another_valid: Name: another-valid - URL: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] valid_job: Name: valid-job - URL: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] >>> cat .databricks/bundle/default/resources.json.wal.corrupted {"k":"resources.jobs.partial_write","v":{"__id__":"33","state":{"name":"partial- diff --git a/acceptance/bundle/deployment/bind/database_instance/output.txt b/acceptance/bundle/deployment/bind/database_instance/output.txt index b9da270a126..95c9600c488 100644 --- a/acceptance/bundle/deployment/bind/database_instance/output.txt +++ b/acceptance/bundle/deployment/bind/database_instance/output.txt @@ -14,7 +14,7 @@ Resources: Database instances: database_instance1: Name: test-instance-02 - URL: [DATABRICKS_URL]/compute/database-instances/test-instance-02?o=[NUMID] + URL: [DATABRICKS_URL]/compute/database-instances/test-instance-02?w=[NUMID] >>> [CLI] bundle deployment unbind database_instance1 Updating deployment state... diff --git a/acceptance/bundle/deployment/bind/pipelines/recreate/out.summary.json b/acceptance/bundle/deployment/bind/pipelines/recreate/out.summary.json index 59dbfe4a809..e67ca2304dc 100644 --- a/acceptance/bundle/deployment/bind/pipelines/recreate/out.summary.json +++ b/acceptance/bundle/deployment/bind/pipelines/recreate/out.summary.json @@ -17,7 +17,7 @@ ], "name": "test-pipeline-[UNIQUE_NAME]", "storage": "/Shared/new_storage", - "url": "[DATABRICKS_URL]/pipelines/[NEW_PIPELINE_ID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/pipelines/[NEW_PIPELINE_ID]?w=[NUMID]" } } } diff --git a/acceptance/bundle/deployment/bind/pipelines/update/out.summary.json b/acceptance/bundle/deployment/bind/pipelines/update/out.summary.json index d4d9b72a851..fce55e66364 100644 --- a/acceptance/bundle/deployment/bind/pipelines/update/out.summary.json +++ b/acceptance/bundle/deployment/bind/pipelines/update/out.summary.json @@ -26,7 +26,7 @@ } ], "name": "test-pipeline", - "url": "[DATABRICKS_URL]/pipelines/[NEW_PIPELINE_ID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/pipelines/[NEW_PIPELINE_ID]?w=[NUMID]" } } }, diff --git a/acceptance/bundle/deployment/bind/sql_warehouse/output.txt b/acceptance/bundle/deployment/bind/sql_warehouse/output.txt index 8ec970d6c1b..ea7f3945a54 100644 --- a/acceptance/bundle/deployment/bind/sql_warehouse/output.txt +++ b/acceptance/bundle/deployment/bind/sql_warehouse/output.txt @@ -14,7 +14,7 @@ Resources: SQL Warehouses: sql_warehouse1: Name: DEFAULT Test SQL Warehouse - URL: [DATABRICKS_URL]/sql/warehouses/[SQL-WAREHOUSE-ID]?o=[NUMID] + URL: [DATABRICKS_URL]/sql/warehouses/[SQL-WAREHOUSE-ID]?w=[NUMID] >>> [CLI] bundle deployment unbind sql_warehouse1 Updating deployment state... diff --git a/acceptance/bundle/open/output.txt b/acceptance/bundle/open/output.txt index 2da91364c7b..e2bbd82d4fa 100644 --- a/acceptance/bundle/open/output.txt +++ b/acceptance/bundle/open/output.txt @@ -20,8 +20,8 @@ Deployment complete! === Use a fake browser that just prints the URL it would have opened === open after deployment >>> [CLI] bundle open foo -Opening browser at [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] -[DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] +Opening browser at [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] +[DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] === test auto-completion handler >>> [CLI] __complete bundle open , diff --git a/acceptance/bundle/resource_deps/create_error/output.txt b/acceptance/bundle/resource_deps/create_error/output.txt index 38c5ef034f7..f8f3c819fa0 100644 --- a/acceptance/bundle/resource_deps/create_error/output.txt +++ b/acceptance/bundle/resource_deps/create_error/output.txt @@ -64,7 +64,7 @@ Resources: URL: (not deployed) independent: Name: independent - URL: [DATABRICKS_URL]/jobs/[INDEPENDENT_ID]?o=[NUMID] + URL: [DATABRICKS_URL]/jobs/[INDEPENDENT_ID]?w=[NUMID] === Plan should still contain foo and bar >>> [CLI] bundle plan diff --git a/acceptance/bundle/resources/alerts/with_file/test.toml b/acceptance/bundle/resources/alerts/with_file/test.toml index 487005b9ae5..efeaad4b922 100644 --- a/acceptance/bundle/resources/alerts/with_file/test.toml +++ b/acceptance/bundle/resources/alerts/with_file/test.toml @@ -8,9 +8,9 @@ Ignore = [".databricks"] # See: https://github.com/databricks/cli/issues/4221 TimeoutCloud = "5m" -# redact ?o=[NUMID]. Different clouds can have different URL serialization, like [DATABRICKS_URL]/sql/alerts-v2/[ALERT_ID] vs [DATABRICKS_URL]/sql/alerts-v2/[ALERT_ID]?o=[NUMID]. +# redact ?[ow]=[NUMID]. Different clouds can have different URL serialization, like [DATABRICKS_URL]/sql/alerts-v2/[ALERT_ID] vs [DATABRICKS_URL]/sql/alerts-v2/[ALERT_ID]?o=[NUMID]. [[Repls]] -Old = '\?o=\d+' +Old = '\?[ow]=\d+' New = '' Order = 0 diff --git a/acceptance/bundle/resources/dashboards/detect-change/test.toml b/acceptance/bundle/resources/dashboards/detect-change/test.toml index dd9353c1bc7..e946788273f 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/test.toml +++ b/acceptance/bundle/resources/dashboards/detect-change/test.toml @@ -16,8 +16,8 @@ Old = "[0-9a-z]{16,}" New = "[ALPHANUMID]" [[Repls]] -# clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/database_instances/single-instance/test.toml b/acceptance/bundle/resources/database_instances/single-instance/test.toml index 4640b352cfa..b0970696c65 100644 --- a/acceptance/bundle/resources/database_instances/single-instance/test.toml +++ b/acceptance/bundle/resources/database_instances/single-instance/test.toml @@ -9,7 +9,7 @@ Ignore = [ ] [[Repls]] -# clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/jobs/check-metadata/test.toml b/acceptance/bundle/resources/jobs/check-metadata/test.toml index 8ba61c13225..735597ef37a 100644 --- a/acceptance/bundle/resources/jobs/check-metadata/test.toml +++ b/acceptance/bundle/resources/jobs/check-metadata/test.toml @@ -15,7 +15,7 @@ Ignore = [ MSYS_NO_PATHCONV = "1" [[Repls]] -# clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[NUMID\]' +# clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[NUMID\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/postgres_branches/test.toml b/acceptance/bundle/resources/postgres_branches/test.toml index 9a4d447f994..b26ad84164b 100644 --- a/acceptance/bundle/resources/postgres_branches/test.toml +++ b/acceptance/bundle/resources/postgres_branches/test.toml @@ -15,8 +15,8 @@ Ignore = [ ] [[Repls]] -# Clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# Clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/postgres_catalogs/test.toml b/acceptance/bundle/resources/postgres_catalogs/test.toml index 66f0811b343..974678034d8 100644 --- a/acceptance/bundle/resources/postgres_catalogs/test.toml +++ b/acceptance/bundle/resources/postgres_catalogs/test.toml @@ -14,8 +14,8 @@ Ignore = [ ] [[Repls]] -# Clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# Clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/postgres_endpoints/test.toml b/acceptance/bundle/resources/postgres_endpoints/test.toml index 66fd405c0c6..de2febb9a25 100644 --- a/acceptance/bundle/resources/postgres_endpoints/test.toml +++ b/acceptance/bundle/resources/postgres_endpoints/test.toml @@ -15,8 +15,8 @@ Ignore = [ ] [[Repls]] -# Clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# Clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/postgres_projects/test.toml b/acceptance/bundle/resources/postgres_projects/test.toml index 4e67bd684f3..1746de6261e 100644 --- a/acceptance/bundle/resources/postgres_projects/test.toml +++ b/acceptance/bundle/resources/postgres_projects/test.toml @@ -15,8 +15,8 @@ Ignore = [ ] [[Repls]] -# Clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# Clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/postgres_synced_tables/test.toml b/acceptance/bundle/resources/postgres_synced_tables/test.toml index bcde950f372..d83bb85e3a2 100644 --- a/acceptance/bundle/resources/postgres_synced_tables/test.toml +++ b/acceptance/bundle/resources/postgres_synced_tables/test.toml @@ -14,8 +14,8 @@ Ignore = [ ] [[Repls]] -# Clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# Clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/schemas/recreate/output.txt b/acceptance/bundle/resources/schemas/recreate/output.txt index 7c173eb11f0..39cafa5b4c3 100644 --- a/acceptance/bundle/resources/schemas/recreate/output.txt +++ b/acceptance/bundle/resources/schemas/recreate/output.txt @@ -23,7 +23,7 @@ Deployment complete! "comment": "COMMENT1", "id": "main.myschema", "name": "myschema", - "url": "[DATABRICKS_URL]/explore/data/main/myschema?o=[NUMID]" + "url": "[DATABRICKS_URL]/explore/data/main/myschema?w=[NUMID]" } } diff --git a/acceptance/bundle/resources/sql_warehouses/output.txt b/acceptance/bundle/resources/sql_warehouses/output.txt index a39c625f723..b751bc3c0b0 100644 --- a/acceptance/bundle/resources/sql_warehouses/output.txt +++ b/acceptance/bundle/resources/sql_warehouses/output.txt @@ -67,7 +67,7 @@ Resources: SQL Warehouses: test_sql_warehouse: Name: sql_warehouse_name - URL: [DATABRICKS_URL]/sql/warehouses/[UUID]?o=[NUMID] + URL: [DATABRICKS_URL]/sql/warehouses/[UUID]?w=[NUMID] === Update the warehouse name >>> update_file.py databricks.yml sql_warehouse_name sql_warehouse_name_2 @@ -105,7 +105,7 @@ Resources: SQL Warehouses: test_sql_warehouse: Name: sql_warehouse_name_2 - URL: [DATABRICKS_URL]/sql/warehouses/[UUID]?o=[NUMID] + URL: [DATABRICKS_URL]/sql/warehouses/[UUID]?w=[NUMID] === Destroy the warehouse >>> [CLI] bundle destroy --auto-approve diff --git a/acceptance/bundle/resources/synced_database_tables/basic/test.toml b/acceptance/bundle/resources/synced_database_tables/basic/test.toml index 191670590b5..e93f7b08093 100644 --- a/acceptance/bundle/resources/synced_database_tables/basic/test.toml +++ b/acceptance/bundle/resources/synced_database_tables/basic/test.toml @@ -9,8 +9,8 @@ RecordRequests = false RunsOnDbr = false [[Repls]] -# clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[(NUMID|ALPHANUMID)\]' +# clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[(NUMID|ALPHANUMID)\]' New = '' Order = 1000 diff --git a/acceptance/bundle/resources/vector_search_endpoints/basic/output.txt b/acceptance/bundle/resources/vector_search_endpoints/basic/output.txt index d3c70f81e40..e9f27f65eb5 100644 --- a/acceptance/bundle/resources/vector_search_endpoints/basic/output.txt +++ b/acceptance/bundle/resources/vector_search_endpoints/basic/output.txt @@ -18,7 +18,7 @@ Resources: Vector Search Endpoints: my_endpoint: Name: vs-endpoint-[UNIQUE_NAME] - URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?o=[NUMID] + URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?w=[NUMID] >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-vs-endpoint-[UNIQUE_NAME]/default/files... @@ -42,7 +42,7 @@ Resources: Vector Search Endpoints: my_endpoint: Name: vs-endpoint-[UNIQUE_NAME] - URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?o=[NUMID] + URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?w=[NUMID] >>> print_requests.py //vector-search/endpoints diff --git a/acceptance/bundle/resources/vector_search_indexes/basic/output.txt b/acceptance/bundle/resources/vector_search_indexes/basic/output.txt index d75bfea61db..3c3defde772 100644 --- a/acceptance/bundle/resources/vector_search_indexes/basic/output.txt +++ b/acceptance/bundle/resources/vector_search_indexes/basic/output.txt @@ -18,7 +18,7 @@ Resources: Vector Search Endpoints: my_endpoint: Name: vs-endpoint-[UNIQUE_NAME] - URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?o=[NUMID] + URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?w=[NUMID] Vector Search Indexes: my_index: Name: vs-index-[UNIQUE_NAME] @@ -48,7 +48,7 @@ Resources: Vector Search Endpoints: my_endpoint: Name: vs-endpoint-[UNIQUE_NAME] - URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?o=[NUMID] + URL: [DATABRICKS_URL]/compute/vector-search/vs-endpoint-[UNIQUE_NAME]?w=[NUMID] Vector Search Indexes: my_index: Name: vs-index-[UNIQUE_NAME] diff --git a/acceptance/bundle/resources/volumes/change-comment/output.txt b/acceptance/bundle/resources/volumes/change-comment/output.txt index 51ea42102d1..fa291c749f7 100644 --- a/acceptance/bundle/resources/volumes/change-comment/output.txt +++ b/acceptance/bundle/resources/volumes/change-comment/output.txt @@ -35,7 +35,7 @@ Deployment complete! === Summary should now show id and url "main.myschema.myvolume" -"[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?o=[NUMID]" +"[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?w=[NUMID]" === Verify deployment >>> [CLI] volumes read main.myschema.myvolume @@ -64,7 +64,7 @@ Deployment complete! "id": "main.myschema.myvolume", "name": "myvolume", "schema_name": "myschema", - "url": "[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?o=[NUMID]", + "url": "[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?w=[NUMID]", "volume_type": "MANAGED" } diff --git a/acceptance/bundle/resources/volumes/change-name/output.txt b/acceptance/bundle/resources/volumes/change-name/output.txt index e34fadd4a61..0572daef298 100644 --- a/acceptance/bundle/resources/volumes/change-name/output.txt +++ b/acceptance/bundle/resources/volumes/change-name/output.txt @@ -27,7 +27,7 @@ Deployment complete! "id": "main.myschema.myvolume", "name": "myvolume", "schema_name": "myschema", - "url": "[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?o=[NUMID]", + "url": "[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?w=[NUMID]", "volume_type": "MANAGED" } } diff --git a/acceptance/bundle/resources/volumes/change-schema-name/output.txt b/acceptance/bundle/resources/volumes/change-schema-name/output.txt index 4d5145da9e2..66292ea6621 100644 --- a/acceptance/bundle/resources/volumes/change-schema-name/output.txt +++ b/acceptance/bundle/resources/volumes/change-schema-name/output.txt @@ -27,7 +27,7 @@ Deployment complete! "id": "main.myschema.myvolume", "name": "myvolume", "schema_name": "myschema", - "url": "[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?o=[NUMID]", + "url": "[DATABRICKS_URL]/explore/data/volumes/main/myschema/myvolume?w=[NUMID]", "volume_type": "MANAGED" } } diff --git a/acceptance/bundle/state/force_pull_commands/output.txt b/acceptance/bundle/state/force_pull_commands/output.txt index 10560af0965..b5ecce5e26a 100644 --- a/acceptance/bundle/state/force_pull_commands/output.txt +++ b/acceptance/bundle/state/force_pull_commands/output.txt @@ -21,12 +21,12 @@ Deployment complete! === bundle open without --force-pull: no remote state read >>> [CLI] bundle open foo -Opening browser at [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] +Opening browser at [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] === bundle open --force-pull: remote state read >>> [CLI] bundle open foo --force-pull -Opening browser at [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] +Opening browser at [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] { "method": "GET", "path": "/api/2.0/workspace-files/Workspace/Users/[USERNAME]/.bundle/force-pull-commands/default/state/STATE_FILENAME" diff --git a/acceptance/bundle/summary/modified_status/output.txt b/acceptance/bundle/summary/modified_status/output.txt index 82bc66bf9f1..92338e101c9 100644 --- a/acceptance/bundle/summary/modified_status/output.txt +++ b/acceptance/bundle/summary/modified_status/output.txt @@ -97,7 +97,7 @@ Deployment complete! "quartz_cron_schedule": "44 19 */1 * * ?", "timezone_id": "Europe/Amsterdam" }, - "url": "[DATABRICKS_URL]/sql/alerts-v2/[UUID]?o=[NUMID]", + "url": "[DATABRICKS_URL]/sql/alerts-v2/[UUID]?w=[NUMID]", "warehouse_id": "aaaaaaaaaaaaaaaa" } }, @@ -118,7 +118,7 @@ Deployment complete! } ], "name": "test-pipeline", - "url": "[DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID]" } }, "schemas": { @@ -127,7 +127,7 @@ Deployment complete! "comment": "COMMENT1", "id": "main.test-schema", "name": "test-schema", - "url": "[DATABRICKS_URL]/explore/data/main/test-schema?o=[NUMID]" + "url": "[DATABRICKS_URL]/explore/data/main/test-schema?w=[NUMID]" } }, "sql_warehouses": { @@ -140,7 +140,7 @@ Deployment complete! "max_num_clusters": 1, "name": "test-sql-warehouse", "spot_instance_policy": "COST_OPTIMIZED", - "url": "[DATABRICKS_URL]/sql/warehouses/[UUID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/sql/warehouses/[UUID]?w=[NUMID]" } } } @@ -152,28 +152,28 @@ Deployment complete! "my_alert": { "id": "[UUID]", "modified_status": "deleted", - "url": "[DATABRICKS_URL]/sql/alerts-v2/[UUID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/sql/alerts-v2/[UUID]?w=[NUMID]" } }, "pipelines": { "my_pipeline": { "id": "[UUID]", "modified_status": "deleted", - "url": "[DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID]" } }, "schemas": { "my_schema": { "id": "main.test-schema", "modified_status": "deleted", - "url": "[DATABRICKS_URL]/explore/data/main/test-schema?o=[NUMID]" + "url": "[DATABRICKS_URL]/explore/data/main/test-schema?w=[NUMID]" } }, "sql_warehouses": { "my_sql_warehouse": { "id": "[UUID]", "modified_status": "deleted", - "url": "[DATABRICKS_URL]/sql/warehouses/[UUID]?o=[NUMID]" + "url": "[DATABRICKS_URL]/sql/warehouses/[UUID]?w=[NUMID]" } } } diff --git a/acceptance/bundle/templates/default-python/integration_classic/test.toml b/acceptance/bundle/templates/default-python/integration_classic/test.toml index a2371aec563..4d358acdc89 100644 --- a/acceptance/bundle/templates/default-python/integration_classic/test.toml +++ b/acceptance/bundle/templates/default-python/integration_classic/test.toml @@ -34,8 +34,8 @@ Old = "python setup.py bdist_wheel" New = "python3 setup.py bdist_wheel" [[Repls]] -# clean up ?o= suffix after URL since not all workspaces have that -Old = '\?o=\[NUMID\]' +# clean up ?[ow]= suffix after URL since not all workspaces have that +Old = '\?[ow]=\[NUMID\]' New = '' Order = 1000 diff --git a/bundle/config/mutator/initialize_urls.go b/bundle/config/mutator/initialize_urls.go index ea69a38ce1c..e14382b3897 100644 --- a/bundle/config/mutator/initialize_urls.go +++ b/bundle/config/mutator/initialize_urls.go @@ -29,31 +29,31 @@ func (m *initializeURLs) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn if err != nil { return diag.FromErr(err) } - orgId := strconv.FormatInt(workspaceId, 10) + workspaceIDStr := strconv.FormatInt(workspaceId, 10) host := b.WorkspaceClient(ctx).Config.CanonicalHostName() - err = initializeForWorkspace(b, orgId, host) + err = initializeForWorkspace(b, workspaceIDStr, host) if err != nil { return diag.FromErr(err) } return nil } -func initializeForWorkspace(b *bundle.Bundle, orgId, host string) error { +func initializeForWorkspace(b *bundle.Bundle, workspaceID, host string) error { baseURL, err := url.Parse(host) if err != nil { return err } - // Add ?o= only if wasn't in the subdomain already. - // The ?o= is needed when vanity URLs / legacy workspace URLs are used. - // If it's not needed we prefer to leave it out since these URLs are rather - // long for most terminals. + // Add ?w= only if wasn't in the subdomain + // already. The parameter is needed when vanity URLs / legacy workspace + // URLs are used. If it's not needed we prefer to leave it out since these + // URLs are rather long for most terminals. // - // See https://docs.databricks.com/en/workspace/workspace-details.html for - // further reading about the '?o=' suffix. - if !strings.Contains(baseURL.Hostname(), orgId) { + // The legacy ?o= spelling is also accepted by the platform; we emit ?w= + // here to match the new workspace addressing convention. + if !strings.Contains(baseURL.Hostname(), workspaceID) { values := baseURL.Query() - values.Add("o", orgId) + values.Add("w", workspaceID) baseURL.RawQuery = values.Encode() } diff --git a/bundle/config/mutator/initialize_urls_test.go b/bundle/config/mutator/initialize_urls_test.go index 393f50271d7..b3e10660b31 100644 --- a/bundle/config/mutator/initialize_urls_test.go +++ b/bundle/config/mutator/initialize_urls_test.go @@ -106,17 +106,17 @@ func TestInitializeURLs(t *testing.T) { } expectedURLs := map[string]string{ - "job1": "https://mycompany.databricks.com/jobs/1?o=123456", - "pipeline1": "https://mycompany.databricks.com/pipelines/3?o=123456", - "experiment1": "https://mycompany.databricks.com/ml/experiments/4?o=123456", - "model1": "https://mycompany.databricks.com/ml/models/a%20model%20uses%20its%20name%20for%20identifier?o=123456", - "servingendpoint1": "https://mycompany.databricks.com/ml/endpoints/my_serving_endpoint?o=123456", - "registeredmodel1": "https://mycompany.databricks.com/explore/data/models/8?o=123456", - "qualityMonitor1": "https://mycompany.databricks.com/explore/data/catalog/schema/qualityMonitor1?o=123456", - "vectorsearchindex1": "https://mycompany.databricks.com/explore/data/catalog/schema/vectorsearchindex1?o=123456", - "schema1": "https://mycompany.databricks.com/explore/data/catalog/schema?o=123456", - "cluster1": "https://mycompany.databricks.com/compute/clusters/1017-103929-vlr7jzcf?o=123456", - "dashboard1": "https://mycompany.databricks.com/dashboardsv3/01ef8d56871e1d50ae30ce7375e42478/published?o=123456", + "job1": "https://mycompany.databricks.com/jobs/1?w=123456", + "pipeline1": "https://mycompany.databricks.com/pipelines/3?w=123456", + "experiment1": "https://mycompany.databricks.com/ml/experiments/4?w=123456", + "model1": "https://mycompany.databricks.com/ml/models/a%20model%20uses%20its%20name%20for%20identifier?w=123456", + "servingendpoint1": "https://mycompany.databricks.com/ml/endpoints/my_serving_endpoint?w=123456", + "registeredmodel1": "https://mycompany.databricks.com/explore/data/models/8?w=123456", + "qualityMonitor1": "https://mycompany.databricks.com/explore/data/catalog/schema/qualityMonitor1?w=123456", + "vectorsearchindex1": "https://mycompany.databricks.com/explore/data/catalog/schema/vectorsearchindex1?w=123456", + "schema1": "https://mycompany.databricks.com/explore/data/catalog/schema?w=123456", + "cluster1": "https://mycompany.databricks.com/compute/clusters/1017-103929-vlr7jzcf?w=123456", + "dashboard1": "https://mycompany.databricks.com/dashboardsv3/01ef8d56871e1d50ae30ce7375e42478/published?w=123456", } err := initializeForWorkspace(b, "123456", "https://mycompany.databricks.com/") From 22d9bd729bfb9b9b856743ce906ba9048d47291a Mon Sep 17 00:00:00 2001 From: Divyansh Vijayvergia Date: Fri, 29 May 2026 14:54:31 +0000 Subject: [PATCH 3/4] Regenerate pipelines acceptance fixtures for ?w= URL emission The acceptance/pipelines tree exercises the pipelines-binary mode of the CLI; `databricks pipelines deploy` and friends print resource URLs via the same bundle/config/mutator/initialize_urls.go path that PR 3 just flipped to ?w=. Regenerate the 19 affected golden files to match. The open-after-deployment/output.txt golden is part of a macOS-only test (its test.toml sets GOOS.windows = false and GOOS.linux = false) so ./task test-update can't regenerate it locally; that one is updated by hand as a mass literal swap, which is the explicit exception called out in .agent/rules/auto-generated-files.md. --- acceptance/pipelines/deploy/auto-approve/output.txt | 2 +- acceptance/pipelines/deploy/create-pipeline/output.txt | 2 +- .../pipelines/deploy/fail-on-active-runs/output.txt | 2 +- acceptance/pipelines/deploy/force-lock/output.txt | 2 +- .../deploy/render-diagnostics-warning/output.txt | 2 +- acceptance/pipelines/deploy/var-flag/output.txt | 2 +- acceptance/pipelines/destroy/auto-approve/output.txt | 2 +- .../pipelines/destroy/destroy-pipeline/output.txt | 2 +- acceptance/pipelines/destroy/force-lock/output.txt | 2 +- .../pipelines/dry-run/dry-run-pipeline/output.txt | 2 +- acceptance/pipelines/dry-run/no-wait/output.txt | 2 +- acceptance/pipelines/dry-run/restart/output.txt | 2 +- acceptance/pipelines/e2e/output.txt | 10 +++++----- .../pipelines/open/open-after-deployment/output.txt | 6 +++--- acceptance/pipelines/run/no-wait/output.txt | 2 +- acceptance/pipelines/run/refresh-flags/output.txt | 2 +- acceptance/pipelines/run/restart/output.txt | 2 +- acceptance/pipelines/run/run-info/output.txt | 2 +- acceptance/pipelines/stop/output.txt | 2 +- 19 files changed, 25 insertions(+), 25 deletions(-) diff --git a/acceptance/pipelines/deploy/auto-approve/output.txt b/acceptance/pipelines/deploy/auto-approve/output.txt index 971748bbaf0..a38e8d77d80 100644 --- a/acceptance/pipelines/deploy/auto-approve/output.txt +++ b/acceptance/pipelines/deploy/auto-approve/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-auto Deploying resources... Updating deployment state... Deployment complete! -View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Remove resources from configuration to test auto-approve >>> rm resources.yml diff --git a/acceptance/pipelines/deploy/create-pipeline/output.txt b/acceptance/pipelines/deploy/create-pipeline/output.txt index ca6cd27e975..6d873c67f2e 100644 --- a/acceptance/pipelines/deploy/create-pipeline/output.txt +++ b/acceptance/pipelines/deploy/create-pipeline/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-create-pipeli Deploying resources... Updating deployment state... Deployment complete! -View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] >>> [CLI] pipelines get [UUID] { diff --git a/acceptance/pipelines/deploy/fail-on-active-runs/output.txt b/acceptance/pipelines/deploy/fail-on-active-runs/output.txt index c9057378a48..14277d8c7fa 100644 --- a/acceptance/pipelines/deploy/fail-on-active-runs/output.txt +++ b/acceptance/pipelines/deploy/fail-on-active-runs/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/pipeline-fail-on-a Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] >>> [CLI] pipelines deploy --fail-on-active-runs Error: pipeline [UUID] is running diff --git a/acceptance/pipelines/deploy/force-lock/output.txt b/acceptance/pipelines/deploy/force-lock/output.txt index b1092d3f523..392fa58954b 100644 --- a/acceptance/pipelines/deploy/force-lock/output.txt +++ b/acceptance/pipelines/deploy/force-lock/output.txt @@ -20,4 +20,4 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-forc Deploying resources... Updating deployment state... Deployment complete! -View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] diff --git a/acceptance/pipelines/deploy/render-diagnostics-warning/output.txt b/acceptance/pipelines/deploy/render-diagnostics-warning/output.txt index efa65f931ae..b1fdcf752a5 100644 --- a/acceptance/pipelines/deploy/render-diagnostics-warning/output.txt +++ b/acceptance/pipelines/deploy/render-diagnostics-warning/output.txt @@ -8,5 +8,5 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/render-diagnostics Deploying resources... Updating deployment state... Deployment complete! -View your pipeline test-pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline test-pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] diff --git a/acceptance/pipelines/deploy/var-flag/output.txt b/acceptance/pipelines/deploy/var-flag/output.txt index 620c0966913..6fe3c7a3873 100644 --- a/acceptance/pipelines/deploy/var-flag/output.txt +++ b/acceptance/pipelines/deploy/var-flag/output.txt @@ -5,7 +5,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-var- Deploying resources... Updating deployment state... Deployment complete! -View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Verify: Check that variables were substituted correctly >>> [CLI] pipelines get [UUID] diff --git a/acceptance/pipelines/destroy/auto-approve/output.txt b/acceptance/pipelines/destroy/auto-approve/output.txt index 3a59e2ffc2f..76bf97b4e2f 100644 --- a/acceptance/pipelines/destroy/auto-approve/output.txt +++ b/acceptance/pipelines/destroy/auto-approve/output.txt @@ -5,7 +5,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-dest Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] >>> errcode [CLI] pipelines destroy Error: this command will destroy all resources deployed by this bundle, including workspace files in the deployment directory. diff --git a/acceptance/pipelines/destroy/destroy-pipeline/output.txt b/acceptance/pipelines/destroy/destroy-pipeline/output.txt index d06ff16cfca..4e5ab020852 100644 --- a/acceptance/pipelines/destroy/destroy-pipeline/output.txt +++ b/acceptance/pipelines/destroy/destroy-pipeline/output.txt @@ -5,7 +5,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-dest Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] >>> [CLI] pipelines destroy --auto-approve The following resources will be deleted: diff --git a/acceptance/pipelines/destroy/force-lock/output.txt b/acceptance/pipelines/destroy/force-lock/output.txt index 5579565b06f..f253d542a91 100644 --- a/acceptance/pipelines/destroy/force-lock/output.txt +++ b/acceptance/pipelines/destroy/force-lock/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-forc Deploying resources... Updating deployment state... Deployment complete! -View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline foo here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === upload lock file >>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/test-pipeline-force-lock/default/state/deploy.lock --format AUTO --file ./deploy.lock diff --git a/acceptance/pipelines/dry-run/dry-run-pipeline/output.txt b/acceptance/pipelines/dry-run/dry-run-pipeline/output.txt index 5ae77f15d7f..159397f0f9a 100644 --- a/acceptance/pipelines/dry-run/dry-run-pipeline/output.txt +++ b/acceptance/pipelines/dry-run/dry-run-pipeline/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run/ Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Dry running pipeline, should have validate_only set to true >>> [CLI] pipelines dry-run diff --git a/acceptance/pipelines/dry-run/no-wait/output.txt b/acceptance/pipelines/dry-run/no-wait/output.txt index bd171a3f912..853ee8f7b00 100644 --- a/acceptance/pipelines/dry-run/no-wait/output.txt +++ b/acceptance/pipelines/dry-run/no-wait/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run- Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Dry running pipeline with --no-wait flag >>> [CLI] pipelines dry-run --no-wait diff --git a/acceptance/pipelines/dry-run/restart/output.txt b/acceptance/pipelines/dry-run/restart/output.txt index 62732b6e3ef..f6764e55d6d 100644 --- a/acceptance/pipelines/dry-run/restart/output.txt +++ b/acceptance/pipelines/dry-run/restart/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run- Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Dry running pipeline with --restart flag, should stop the current pipeline and start a new run >>> [CLI] pipelines dry-run --restart diff --git a/acceptance/pipelines/e2e/output.txt b/acceptance/pipelines/e2e/output.txt index 65b27ec3506..8e029977b23 100644 --- a/acceptance/pipelines/e2e/output.txt +++ b/acceptance/pipelines/e2e/output.txt @@ -20,8 +20,8 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/lakeflow_project/d Deploying resources... Updating deployment state... Deployment complete! -View your job sample_job here: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] -View your pipeline lakeflow_project_etl here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your job sample_job here: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] +View your pipeline lakeflow_project_etl here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Run pipeline >>> [CLI] pipelines run @@ -44,9 +44,9 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/lakeflow_project/d Deploying resources... Updating deployment state... Deployment complete! -View your job sample_job here: [DATABRICKS_URL]/jobs/[NUMID]?o=[NUMID] -View your pipeline lakeflow_project_etl here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] -View your pipeline lakeflow_project_etl_2 here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your job sample_job here: [DATABRICKS_URL]/jobs/[NUMID]?w=[NUMID] +View your pipeline lakeflow_project_etl here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] +View your pipeline lakeflow_project_etl_2 here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Assert the second pipeline is created >>> [CLI] pipelines get [UUID] diff --git a/acceptance/pipelines/open/open-after-deployment/output.txt b/acceptance/pipelines/open/open-after-deployment/output.txt index ec19082f2e8..ff6a660e626 100644 --- a/acceptance/pipelines/open/open-after-deployment/output.txt +++ b/acceptance/pipelines/open/open-after-deployment/output.txt @@ -10,15 +10,15 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/pipelines-open/def Deploying resources... Updating deployment state... Deployment complete! -View your pipeline test-pipelines-open here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline test-pipelines-open here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Modify PATH so that real open is not run === open after deployment. This will fail to open browser and complain, that's ok, we only want the message >>> [CLI] pipelines open -Opening browser at [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +Opening browser at [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] Error: exec: "open": cannot run executable found relative to current directory === open with KEY, expect same output as opening without KEY >>> [CLI] pipelines open test-pipelines-open -Opening browser at [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +Opening browser at [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] Error: exec: "open": cannot run executable found relative to current directory diff --git a/acceptance/pipelines/run/no-wait/output.txt b/acceptance/pipelines/run/no-wait/output.txt index 39987c2e3d5..2ceb291d0dd 100644 --- a/acceptance/pipelines/run/no-wait/output.txt +++ b/acceptance/pipelines/run/no-wait/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run- Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === test --no-wait flag >>> [CLI] pipelines run --no-wait diff --git a/acceptance/pipelines/run/refresh-flags/output.txt b/acceptance/pipelines/run/refresh-flags/output.txt index 4a3b2c3234d..b7296296577 100644 --- a/acceptance/pipelines/run/refresh-flags/output.txt +++ b/acceptance/pipelines/run/refresh-flags/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run- Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Running pipeline with --refresh flag and specific tables >>> [CLI] pipelines run --refresh table1,table2 diff --git a/acceptance/pipelines/run/restart/output.txt b/acceptance/pipelines/run/restart/output.txt index e28549b9360..1ba6202cc03 100644 --- a/acceptance/pipelines/run/restart/output.txt +++ b/acceptance/pipelines/run/restart/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run- Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Running pipeline with --restart flag >>> [CLI] pipelines run --restart diff --git a/acceptance/pipelines/run/run-info/output.txt b/acceptance/pipelines/run/run-info/output.txt index ffdecbfcda6..ddbbe6a9d6e 100644 --- a/acceptance/pipelines/run/run-info/output.txt +++ b/acceptance/pipelines/run/run-info/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-run/ Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] === Run pipeline and display pipeline configuration summary and events durations >>> [CLI] pipelines run diff --git a/acceptance/pipelines/stop/output.txt b/acceptance/pipelines/stop/output.txt index b8a85e88977..9c8cd6f57a6 100644 --- a/acceptance/pipelines/stop/output.txt +++ b/acceptance/pipelines/stop/output.txt @@ -4,7 +4,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-pipeline-stop Deploying resources... Updating deployment state... Deployment complete! -View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] +View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?w=[NUMID] >>> [CLI] pipelines run Recommendation: This command runs the last deployed version of the code From bf65882229961c015565bae58588098fc3efed6e Mon Sep 17 00:00:00 2001 From: Divyansh Vijayvergia <171924202+Divyansh-db@users.noreply.github.com> Date: Mon, 1 Jun 2026 13:56:30 +0200 Subject: [PATCH 4/4] Resolve workspace IDs as strings at all CLI consumers (#5379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary The CLI calls \`CurrentWorkspaceID\` on the workspace client in five places to obtain the workspace ID. The SDK helper returns \`(int64, error)\` and reads the value from the \`X-Databricks-Org-Id\` response header on \`/api/2.0/preview/scim/v2/Me\`, parsing with \`strconv.ParseInt\`. Every consumer then formats the int64 back to a string at its own boundary — or, in two cases under \`experimental/ssh\`, embeds it directly with \`%d\`. This PR introduces a small helper that returns the workspace ID as a string and short-circuits the API call when the workspace ID is already configured on the client, and migrates the five consumers to use it. As a side effect, the in-CLI workspace ID flow becomes string-typed end-to-end, so non-numeric workspace identifiers (e.g. connection-style IDs) flow through cleanly when configured. ## The helper \`libs/auth.ResolveWorkspaceID(ctx, w)\`: 1. If \`w.Config.WorkspaceID\` is set (and not the CLI-only \`"none"\` sentinel), return it verbatim. No API call. 2. Otherwise call \`w.CurrentWorkspaceID(ctx)\` and return \`strconv.FormatInt(id, 10)\`. The fast path is also a small performance win — the workspace ID is already configured in the common SPOG case (set via \`--workspace-id\`, \`DATABRICKS_WORKSPACE_ID\`, \`workspace_id\` in \`.databrickscfg\`, or \`?w=\`/\`?o=\` on a host URL), and the existing code unconditionally hit \`/Me\` to resolve the same value. ## Consumer migrations Five call sites switch from \`CurrentWorkspaceID\` to \`ResolveWorkspaceID\`: - \`cmd/experimental/workspace_open.go\` (\`databricks experimental open\`) - \`bundle/config/mutator/initialize_urls.go\` (\`databricks bundle summary\` and any other bundle command that renders resource URLs) - \`cmd/apps/run_local.go\` (\`databricks apps run-local\`) - \`experimental/ssh/internal/client/websockets.go\` (\`databricks experimental ssh\` driver-proxy URL) - \`experimental/ssh/internal/client/client.go\` (\`databricks experimental ssh\` cluster-metadata fetch) ## Downstream signatures widen from int64 to string - \`libs/workspaceurls.BuildResourceURL\`, \`workspaceBaseURL\` - \`libs/apps/runlocal.Config.WorkspaceID\`, \`NewConfig\` - The two \`fmt.Sprintf\` calls in \`experimental/ssh\` swap \`%d\` → \`%s\`. The \`/driver-proxy-api/o//...\` URL keeps its legacy \`o\` path segment — that's a separate platform-side URL scheme decision from the \`?o=\`/\`?w=\` query parameter migration. ## Behavior change summary - Configured workspace ID flow: no behavior change. The value the CLI sends as a routing header / appends to a URL / sets as \`DATABRICKS_WORKSPACE_ID\` for child processes is the same string it was before. - Resolved-from-\`/Me\` flow: also no behavior change. The SDK still enforces numeric in that path; the helper just stringifies the result. - Non-numeric configured IDs (UUID-shaped connection-style identifiers set via \`?w=\` etc.): newly supported end-to-end through the five consumers. Previously the int64 typing dropped them. ## Out of scope - \`libs/auth/introspect.go\` declares \`WorkspaceID int64\` on the \`/api/2.0/tokens/introspect\` response. Different endpoint, separate follow-up. - \`cmd/auth/login.go\`'s workspace picker reads \`WorkspaceId int64\` from the SDK's \`Workspaces.List\` schema. Upstream SDK change. - The \`/driver-proxy-api/o//\` URL path segment in \`experimental ssh\` keeps the legacy \`o\` form. That's a separate platform-side URL scheme decision; only the format specifier changes here. ## Test plan - [x] \`go test ./libs/auth/... ./libs/workspaceurls/... ./libs/apps/... ./bundle/config/mutator/... ./cmd/experimental/... ./cmd/apps/... ./experimental/ssh/...\` — green - [x] \`./task test-update\` over the previously-affected acceptance suites produces no diff (behavior preserved) - [x] \`./task lint-q\` — 0 issues; \`./task fmt\` — clean --- acceptance/bundle/user_agent/output.txt | 2 - .../simple/out.requests.summary.direct.json | 9 -- .../out.requests.summary.terraform.json | 9 -- bundle/config/mutator/initialize_urls.go | 7 +- cmd/apps/run_local.go | 2 +- cmd/experimental/workspace_open.go | 7 +- cmd/experimental/workspace_open_test.go | 40 +++---- experimental/ssh/internal/client/client.go | 5 +- .../ssh/internal/client/websockets.go | 8 +- libs/apps/runlocal/cfg.go | 4 +- libs/apps/runlocal/env.go | 2 +- libs/auth/workspace_id.go | 36 +++++++ libs/auth/workspace_id_test.go | 101 ++++++++++++++++++ libs/workspaceurls/urls.go | 16 +-- libs/workspaceurls/urls_test.go | 31 +++--- 15 files changed, 201 insertions(+), 78 deletions(-) create mode 100644 libs/auth/workspace_id.go create mode 100644 libs/auth/workspace_id_test.go diff --git a/acceptance/bundle/user_agent/output.txt b/acceptance/bundle/user_agent/output.txt index 5c6e383d1ff..f2a1cd12a2b 100644 --- a/acceptance/bundle/user_agent/output.txt +++ b/acceptance/bundle/user_agent/output.txt @@ -125,12 +125,10 @@ MISS run.terraform /.well-known/databricks-config 'cli/[DEV_VERSION] databricks- MISS summary.direct /api/2.0/preview/scim/v2/Me 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none auth/pat' MISS summary.direct /api/2.0/workspace/get-status 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none auth/pat' MISS summary.direct /api/2.0/workspace/get-status 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none auth/pat' -OK summary.direct /api/2.0/preview/scim/v2/Me engine/direct MISS summary.direct /.well-known/databricks-config 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS]' MISS summary.terraform /api/2.0/preview/scim/v2/Me 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none auth/pat' MISS summary.terraform /api/2.0/workspace/get-status 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none auth/pat' MISS summary.terraform /api/2.0/workspace/get-status 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none auth/pat' -OK summary.terraform /api/2.0/preview/scim/v2/Me engine/terraform MISS summary.terraform /.well-known/databricks-config 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS]' MISS validate.direct /api/2.0/preview/scim/v2/Me 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_validate cmd-exec-id/[UUID] interactive/none auth/pat' MISS validate.direct /api/2.0/workspace/get-status 'cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_validate cmd-exec-id/[UUID] interactive/none auth/pat' diff --git a/acceptance/bundle/user_agent/simple/out.requests.summary.direct.json b/acceptance/bundle/user_agent/simple/out.requests.summary.direct.json index c3017391f2f..3a3e2db9e9a 100644 --- a/acceptance/bundle/user_agent/simple/out.requests.summary.direct.json +++ b/acceptance/bundle/user_agent/simple/out.requests.summary.direct.json @@ -33,15 +33,6 @@ "return_export_info": "true" } } -{ - "headers": { - "User-Agent": [ - "cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none engine/direct auth/pat" - ] - }, - "method": "GET", - "path": "/api/2.0/preview/scim/v2/Me" -} { "headers": { "User-Agent": [ diff --git a/acceptance/bundle/user_agent/simple/out.requests.summary.terraform.json b/acceptance/bundle/user_agent/simple/out.requests.summary.terraform.json index bf160a9744d..3a3e2db9e9a 100644 --- a/acceptance/bundle/user_agent/simple/out.requests.summary.terraform.json +++ b/acceptance/bundle/user_agent/simple/out.requests.summary.terraform.json @@ -33,15 +33,6 @@ "return_export_info": "true" } } -{ - "headers": { - "User-Agent": [ - "cli/[DEV_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_summary cmd-exec-id/[UUID] interactive/none engine/terraform auth/pat" - ] - }, - "method": "GET", - "path": "/api/2.0/preview/scim/v2/Me" -} { "headers": { "User-Agent": [ diff --git a/bundle/config/mutator/initialize_urls.go b/bundle/config/mutator/initialize_urls.go index e14382b3897..c3a877d9e86 100644 --- a/bundle/config/mutator/initialize_urls.go +++ b/bundle/config/mutator/initialize_urls.go @@ -3,10 +3,10 @@ package mutator import ( "context" "net/url" - "strconv" "strings" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/diag" ) @@ -25,13 +25,12 @@ func (m *initializeURLs) Name() string { } func (m *initializeURLs) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { - workspaceId, err := b.WorkspaceClient(ctx).CurrentWorkspaceID(ctx) + workspaceID, err := auth.ResolveWorkspaceID(ctx, b.WorkspaceClient(ctx)) if err != nil { return diag.FromErr(err) } - workspaceIDStr := strconv.FormatInt(workspaceId, 10) host := b.WorkspaceClient(ctx).Config.CanonicalHostName() - err = initializeForWorkspace(b, workspaceIDStr, host) + err = initializeForWorkspace(b, workspaceID, host) if err != nil { return diag.FromErr(err) } diff --git a/cmd/apps/run_local.go b/cmd/apps/run_local.go index 8d42783c47e..144fbf5354a 100644 --- a/cmd/apps/run_local.go +++ b/cmd/apps/run_local.go @@ -29,7 +29,7 @@ const SHUTDOWN_TIMEOUT = 15 * time.Second func setupWorkspaceAndConfig(cmd *cobra.Command, entryPoint string, appPort int) (*runlocal.Config, *runlocal.AppSpec, error) { ctx := cmd.Context() w := cmdctx.WorkspaceClient(ctx) - workspaceID, err := w.CurrentWorkspaceID(ctx) + workspaceID, err := auth.ResolveWorkspaceID(ctx, w) if err != nil { return nil, nil, err } diff --git a/cmd/experimental/workspace_open.go b/cmd/experimental/workspace_open.go index e48bdcc6d96..b6d05e71fc6 100644 --- a/cmd/experimental/workspace_open.go +++ b/cmd/experimental/workspace_open.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/cobra" "github.com/databricks/cli/cmd/root" + "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/browser" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" @@ -15,8 +16,8 @@ import ( "github.com/databricks/cli/libs/workspaceurls" ) -var currentWorkspaceID = func(ctx context.Context) (int64, error) { - return cmdctx.WorkspaceClient(ctx).CurrentWorkspaceID(ctx) +var resolveWorkspaceID = func(ctx context.Context) (string, error) { + return auth.ResolveWorkspaceID(ctx, cmdctx.WorkspaceClient(ctx)) } var openWorkspaceURL = browser.Open @@ -49,7 +50,7 @@ Examples: resourceType := args[0] id := args[1] - workspaceID, err := currentWorkspaceID(ctx) + workspaceID, err := resolveWorkspaceID(ctx) if err != nil { log.Warnf(ctx, "Could not determine workspace ID: %v", err) } diff --git a/cmd/experimental/workspace_open_test.go b/cmd/experimental/workspace_open_test.go index 78390187475..f81e79be52b 100644 --- a/cmd/experimental/workspace_open_test.go +++ b/cmd/experimental/workspace_open_test.go @@ -38,7 +38,7 @@ func TestBuildWorkspaceURLPathBasedResources(t *testing.T) { for _, tt := range tests { t.Run(tt.resourceType+"/"+tt.id, func(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", tt.resourceType, tt.id, 0) + got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", tt.resourceType, tt.id, "") require.NoError(t, err) assert.Equal(t, tt.expected, got) }) @@ -57,7 +57,7 @@ func TestBuildWorkspaceURLFragmentBasedResources(t *testing.T) { for _, tt := range tests { t.Run(tt.id, func(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", tt.resourceType, tt.id, 0) + got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", tt.resourceType, tt.id, "") require.NoError(t, err) assert.Equal(t, tt.expected, got) }) @@ -65,38 +65,38 @@ func TestBuildWorkspaceURLFragmentBasedResources(t *testing.T) { } func TestBuildWorkspaceURLUnknownResourceType(t *testing.T) { - _, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "unknown", "123", 0) + _, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "unknown", "123", "") assert.ErrorContains(t, err, "unknown resource type \"unknown\"") assert.ErrorContains(t, err, "alerts, apps, catalogs, clusters, dashboards, database_catalogs, database_instances, experiments, jobs, model_serving_endpoints, models, notebooks, pipelines, postgres_catalogs, postgres_synced_tables, quality_monitors, queries, registered_models, schemas, synced_database_tables, vector_search_endpoints, vector_search_indexes, volumes, warehouses") } func TestBuildWorkspaceURLHostWithTrailingSlash(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com/", "jobs", "123", 0) + got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com/", "jobs", "123", "") require.NoError(t, err) assert.Equal(t, "https://myworkspace.databricks.com/jobs/123", got) } func TestBuildWorkspaceURLWithWorkspaceID(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "jobs", "123", 123456) + got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "jobs", "123", "123456") require.NoError(t, err) assert.Equal(t, "https://myworkspace.databricks.com/jobs/123?w=123456", got) } func TestBuildWorkspaceURLWithWorkspaceIDInHostname(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://adb-123456.azuredatabricks.net", "jobs", "123", 123456) + got, err := workspaceurls.BuildResourceURL("https://adb-123456.azuredatabricks.net", "jobs", "123", "123456") require.NoError(t, err) // Workspace ID is already in the hostname, so ?w= should not be appended. assert.Equal(t, "https://adb-123456.azuredatabricks.net/jobs/123", got) } func TestBuildWorkspaceURLWithWorkspaceIDInVanityHostname(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://workspace-123456.example.com", "jobs", "123", 123456) + got, err := workspaceurls.BuildResourceURL("https://workspace-123456.example.com", "jobs", "123", "123456") require.NoError(t, err) assert.Equal(t, "https://workspace-123456.example.com/jobs/123?w=123456", got) } func TestBuildWorkspaceURLFragmentWithWorkspaceID(t *testing.T) { - got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "notebooks", "12345", 789) + got, err := workspaceurls.BuildResourceURL("https://myworkspace.databricks.com", "notebooks", "12345", "789") require.NoError(t, err) assert.Equal(t, "https://myworkspace.databricks.com/?w=789#notebook/12345", got) } @@ -158,15 +158,15 @@ func TestWorkspaceOpenCommandHelpText(t *testing.T) { } func TestWorkspaceOpenCommandOpensBrowserByDefault(t *testing.T) { - originalCurrentWorkspaceID := currentWorkspaceID + originalResolveWorkspaceID := resolveWorkspaceID originalOpenWorkspaceURL := openWorkspaceURL t.Cleanup(func() { - currentWorkspaceID = originalCurrentWorkspaceID + resolveWorkspaceID = originalResolveWorkspaceID openWorkspaceURL = originalOpenWorkspaceURL }) - currentWorkspaceID = func(context.Context) (int64, error) { - return 0, nil + resolveWorkspaceID = func(context.Context) (string, error) { + return "", nil } var gotURL string @@ -197,15 +197,15 @@ func TestWorkspaceOpenCommandOpensBrowserByDefault(t *testing.T) { } func TestWorkspaceOpenCommandURLFlag(t *testing.T) { - originalCurrentWorkspaceID := currentWorkspaceID + originalResolveWorkspaceID := resolveWorkspaceID originalOpenWorkspaceURL := openWorkspaceURL t.Cleanup(func() { - currentWorkspaceID = originalCurrentWorkspaceID + resolveWorkspaceID = originalResolveWorkspaceID openWorkspaceURL = originalOpenWorkspaceURL }) - currentWorkspaceID = func(context.Context) (int64, error) { - return 789, nil + resolveWorkspaceID = func(context.Context) (string, error) { + return "789", nil } browserOpened := false @@ -238,15 +238,15 @@ func TestWorkspaceOpenCommandURLFlag(t *testing.T) { } func TestWorkspaceOpenCommandWarnsWhenWorkspaceIDLookupFails(t *testing.T) { - originalCurrentWorkspaceID := currentWorkspaceID + originalResolveWorkspaceID := resolveWorkspaceID originalOpenWorkspaceURL := openWorkspaceURL t.Cleanup(func() { - currentWorkspaceID = originalCurrentWorkspaceID + resolveWorkspaceID = originalResolveWorkspaceID openWorkspaceURL = originalOpenWorkspaceURL }) - currentWorkspaceID = func(context.Context) (int64, error) { - return 0, errors.New("lookup failed") + resolveWorkspaceID = func(context.Context) (string, error) { + return "", errors.New("lookup failed") } openWorkspaceURL = func(ctx context.Context, targetURL string) error { diff --git a/experimental/ssh/internal/client/client.go b/experimental/ssh/internal/client/client.go index 69360b85d12..ff3ce1ee331 100644 --- a/experimental/ssh/internal/client/client.go +++ b/experimental/ssh/internal/client/client.go @@ -26,6 +26,7 @@ import ( "github.com/databricks/cli/experimental/ssh/internal/vscode" sshWorkspace "github.com/databricks/cli/experimental/ssh/internal/workspace" "github.com/databricks/cli/internal/build" + "github.com/databricks/cli/libs/auth" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go" @@ -438,11 +439,11 @@ func getServerMetadata(ctx context.Context, client *databricks.WorkspaceClient, return 0, "", "", errors.Join(errServerMetadata, errors.New("cluster ID not available in metadata")) } - workspaceID, err := client.CurrentWorkspaceID(ctx) + workspaceID, err := auth.ResolveWorkspaceID(ctx, client) if err != nil { return 0, "", "", err } - metadataURL := fmt.Sprintf("%s/driver-proxy-api/o/%d/%s/%d/metadata", client.Config.Host, workspaceID, effectiveClusterID, wsMetadata.Port) + metadataURL := fmt.Sprintf("%s/driver-proxy-api/o/%s/%s/%d/metadata", client.Config.Host, workspaceID, effectiveClusterID, wsMetadata.Port) log.Debugf(ctx, "Metadata URL: %s", metadataURL) req, err := http.NewRequestWithContext(ctx, http.MethodGet, metadataURL, nil) if err != nil { diff --git a/experimental/ssh/internal/client/websockets.go b/experimental/ssh/internal/client/websockets.go index 0dd7e37781f..3241a9be2af 100644 --- a/experimental/ssh/internal/client/websockets.go +++ b/experimental/ssh/internal/client/websockets.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" + "github.com/databricks/cli/libs/auth" "github.com/databricks/databricks-sdk-go" "github.com/gorilla/websocket" ) @@ -38,11 +39,14 @@ func createWebsocketConnection(ctx context.Context, client *databricks.Workspace } func getProxyURL(ctx context.Context, client *databricks.WorkspaceClient, connID, clusterID string, serverPort int) (string, error) { - workspaceID, err := client.CurrentWorkspaceID(ctx) + workspaceID, err := auth.ResolveWorkspaceID(ctx, client) if err != nil { return "", fmt.Errorf("failed to get current workspace ID: %w", err) } host := client.Config.Host - url := fmt.Sprintf("%s/driver-proxy-api/o/%d/%s/%d/ssh?id=%s", host, workspaceID, clusterID, serverPort, connID) + // The /driver-proxy-api/o//... path is a legacy URL form on + // the driver-proxy endpoint and uses an "o" path segment regardless of + // whether the workspace ID itself is the legacy or new shape. + url := fmt.Sprintf("%s/driver-proxy-api/o/%s/%s/%d/ssh?id=%s", host, workspaceID, clusterID, serverPort, connID) return url, nil } diff --git a/libs/apps/runlocal/cfg.go b/libs/apps/runlocal/cfg.go index d196eb1208d..7b236571d5c 100644 --- a/libs/apps/runlocal/cfg.go +++ b/libs/apps/runlocal/cfg.go @@ -8,7 +8,7 @@ import ( type Config struct { AppName string AppURL string - WorkspaceID int64 + WorkspaceID string ServerName string Host string WorkspaceHost string @@ -24,7 +24,7 @@ const ( DEFAULT_PORT = 8000 ) -func NewConfig(workspaceHost string, workspaceID int64, appDir, host string, port int) *Config { +func NewConfig(workspaceHost, workspaceID, appDir, host string, port int) *Config { c := &Config{ AppName: DEFAULT_APP_NAME, AppURL: "http://" + net.JoinHostPort(host, strconv.Itoa(port)), diff --git a/libs/apps/runlocal/env.go b/libs/apps/runlocal/env.go index 7c0f562d41c..1eae427d621 100644 --- a/libs/apps/runlocal/env.go +++ b/libs/apps/runlocal/env.go @@ -18,7 +18,7 @@ func GetBaseEnvVars(config *Config) []EnvVar { {Name: "PYTHONUNBUFFERED", Value: "1"}, {Name: "DATABRICKS_APP_NAME", Value: config.AppName}, {Name: "DATABRICKS_APP_URL", Value: config.AppURL}, - {Name: "DATABRICKS_WORKSPACE_ID", Value: strconv.FormatInt(config.WorkspaceID, 10)}, + {Name: "DATABRICKS_WORKSPACE_ID", Value: config.WorkspaceID}, {Name: "DATABRICKS_HOST", Value: config.WorkspaceHost}, {Name: "DATABRICKS_APP_PORT", Value: strconv.Itoa(config.Port)}, {Name: "GRADIO_SERVER_NAME", Value: config.ServerName}, diff --git a/libs/auth/workspace_id.go b/libs/auth/workspace_id.go new file mode 100644 index 00000000000..be48a6578b4 --- /dev/null +++ b/libs/auth/workspace_id.go @@ -0,0 +1,36 @@ +package auth + +import ( + "context" + "strconv" + + "github.com/databricks/databricks-sdk-go" +) + +// ResolveWorkspaceID returns the workspace ID as a string, preferring the +// value already configured on the client and falling back to a /Me probe. +// +// The fast path short-circuits the API call when w.Config.WorkspaceID is +// set (via --workspace-id, DATABRICKS_WORKSPACE_ID, workspace_id in +// .databrickscfg, ?o=/?w= on a host URL, or any other config source). The +// CLI-only "none" sentinel is treated as unset so it never leaks as a +// routing identifier. +// +// The fallback path delegates to w.CurrentWorkspaceID, which reads the +// X-Databricks-Org-Id response header on /api/2.0/preview/scim/v2/Me and +// parses it as int64. The numeric constraint is enforced by the SDK on +// that path; the helper just stringifies the result. +// +// Compared to calling w.CurrentWorkspaceID directly, the string return +// type lets callers pass the value to URL builders, env vars, and other +// string-typed sinks without a manual strconv.FormatInt step. +func ResolveWorkspaceID(ctx context.Context, w *databricks.WorkspaceClient) (string, error) { + if id := w.Config.WorkspaceID; id != "" && id != WorkspaceIDNone { + return id, nil + } + id, err := w.CurrentWorkspaceID(ctx) + if err != nil { + return "", err + } + return strconv.FormatInt(id, 10), nil +} diff --git a/libs/auth/workspace_id_test.go b/libs/auth/workspace_id_test.go new file mode 100644 index 00000000000..9b8f0cb214b --- /dev/null +++ b/libs/auth/workspace_id_test.go @@ -0,0 +1,101 @@ +package auth_test + +import ( + "testing" + + "github.com/databricks/cli/libs/auth" + "github.com/databricks/cli/libs/testserver" + "github.com/databricks/databricks-sdk-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestResolveWorkspaceID_FastPathReturnsConfiguredValue(t *testing.T) { + // Pointing at a /Me handler that would return a different ID lets us + // confirm the fast path doesn't hit the API. + server := testserver.New(t) + server.Handle("GET", "/api/2.0/preview/scim/v2/Me", func(req testserver.Request) any { + t.Fatalf("/Me should not be called when WorkspaceID is configured") + return nil + }) + testserver.AddDefaultHandlers(server) + + w, err := databricks.NewWorkspaceClient(&databricks.Config{ + Host: server.URL, + Token: "testtoken", + WorkspaceID: "12345", + }) + require.NoError(t, err) + + got, err := auth.ResolveWorkspaceID(t.Context(), w) + require.NoError(t, err) + assert.Equal(t, "12345", got) +} + +func TestResolveWorkspaceID_FastPathPassesThroughNonNumericValue(t *testing.T) { + // Connection-style identifiers (UUID-shaped, etc.) are valid as of the + // X-Databricks-Workspace-Id header rollout. The configured value must + // flow through unchanged. + server := testserver.New(t) + server.Handle("GET", "/api/2.0/preview/scim/v2/Me", func(req testserver.Request) any { + t.Fatalf("/Me should not be called when WorkspaceID is configured") + return nil + }) + testserver.AddDefaultHandlers(server) + + const connID = "123e4567-e89b-12d3-a456-426614174000" + w, err := databricks.NewWorkspaceClient(&databricks.Config{ + Host: server.URL, + Token: "testtoken", + WorkspaceID: connID, + }) + require.NoError(t, err) + + got, err := auth.ResolveWorkspaceID(t.Context(), w) + require.NoError(t, err) + assert.Equal(t, connID, got) +} + +func TestResolveWorkspaceID_NoneSentinelFallsThroughToAPI(t *testing.T) { + // The CLI persists "none" in .databrickscfg to mark profiles where the + // user skipped workspace selection. It must not leak as a routing ID. + server := testserver.New(t) + testserver.AddDefaultHandlers(server) + + w, err := databricks.NewWorkspaceClient(&databricks.Config{ + Host: server.URL, + Token: "testtoken", + WorkspaceID: auth.WorkspaceIDNone, + }) + require.NoError(t, err) + + got, err := auth.ResolveWorkspaceID(t.Context(), w) + require.NoError(t, err) + // The default /Me handler returns X-Databricks-Org-Id: 900800700600. + assert.Equal(t, "900800700600", got) +} + +func TestResolveWorkspaceID_FallbackHitsMeAndStringifiesResponse(t *testing.T) { + // Note: testserver.New unconditionally registers a + // /.well-known/databricks-config handler that returns + // workspace_id=900800700600. The SDK config resolver picks that up and + // pre-populates cfg.WorkspaceID, which means the helper's fast path + // returns "900800700600" without ever hitting /Me. From the helper's + // perspective the observable behavior is identical (it returns + // "900800700600" either way), so this test still covers what callers + // see — it just lands on the fast path rather than the literal + // fallback path. The fast path is also exercised explicitly by + // TestResolveWorkspaceID_FastPathReturnsConfiguredValue. + server := testserver.New(t) + testserver.AddDefaultHandlers(server) + + w, err := databricks.NewWorkspaceClient(&databricks.Config{ + Host: server.URL, + Token: "testtoken", + }) + require.NoError(t, err) + + got, err := auth.ResolveWorkspaceID(t.Context(), w) + require.NoError(t, err) + assert.Equal(t, "900800700600", got) +} diff --git a/libs/workspaceurls/urls.go b/libs/workspaceurls/urls.go index 788c63a63a6..be9a41fc959 100644 --- a/libs/workspaceurls/urls.go +++ b/libs/workspaceurls/urls.go @@ -4,7 +4,6 @@ import ( "fmt" "net/url" "slices" - "strconv" "strings" ) @@ -80,8 +79,10 @@ func ResourceURL(baseURL url.URL, resourceType, id string) string { // BuildResourceURL constructs a full workspace URL from a host string, resource // type name, ID, and workspace ID. It parses the host, appends ?w= -// when needed, and formats the resource path. -func BuildResourceURL(host, resourceType, id string, workspaceID int64) (string, error) { +// when needed, and formats the resource path. An empty workspaceID skips the +// query parameter (use when the workspace ID is unknown or already encoded in +// the hostname). +func BuildResourceURL(host, resourceType, id, workspaceID string) (string, error) { baseURL, err := workspaceBaseURL(host, workspaceID) if err != nil { return "", err @@ -101,23 +102,22 @@ func resolveAlias(resourceType string) string { return resourceType } -func workspaceBaseURL(host string, workspaceID int64) (*url.URL, error) { +func workspaceBaseURL(host, workspaceID string) (*url.URL, error) { baseURL, err := url.Parse(host) if err != nil { return nil, fmt.Errorf("invalid workspace host %q: %w", host, err) } - if workspaceID == 0 { + if workspaceID == "" { return baseURL, nil } - wsID := strconv.FormatInt(workspaceID, 10) - if hasWorkspaceIDInHostname(baseURL.Hostname(), wsID) { + if hasWorkspaceIDInHostname(baseURL.Hostname(), workspaceID) { return baseURL, nil } values := baseURL.Query() - values.Add("w", wsID) + values.Add("w", workspaceID) baseURL.RawQuery = values.Encode() return baseURL, nil diff --git a/libs/workspaceurls/urls_test.go b/libs/workspaceurls/urls_test.go index 5107d0a21c3..af7b5b08ea4 100644 --- a/libs/workspaceurls/urls_test.go +++ b/libs/workspaceurls/urls_test.go @@ -45,15 +45,16 @@ func TestWorkspaceBaseURL(t *testing.T) { tests := []struct { name string host string - workspaceID int64 + workspaceID string expected string }{ - {"no workspace ID", "https://myworkspace.databricks.com", 0, "https://myworkspace.databricks.com"}, - {"with workspace ID", "https://myworkspace.databricks.com", 123456, "https://myworkspace.databricks.com?w=123456"}, - {"trailing slash stripped", "https://myworkspace.databricks.com/", 0, "https://myworkspace.databricks.com/"}, - {"trailing slash with workspace ID", "https://myworkspace.databricks.com/", 789, "https://myworkspace.databricks.com/?w=789"}, - {"adb hostname skips query param", "https://adb-123456.azuredatabricks.net", 123456, "https://adb-123456.azuredatabricks.net"}, - {"adb hostname mismatch adds param", "https://adb-999.azuredatabricks.net", 123456, "https://adb-999.azuredatabricks.net?w=123456"}, + {"no workspace ID", "https://myworkspace.databricks.com", "", "https://myworkspace.databricks.com"}, + {"with workspace ID", "https://myworkspace.databricks.com", "123456", "https://myworkspace.databricks.com?w=123456"}, + {"trailing slash stripped", "https://myworkspace.databricks.com/", "", "https://myworkspace.databricks.com/"}, + {"trailing slash with workspace ID", "https://myworkspace.databricks.com/", "789", "https://myworkspace.databricks.com/?w=789"}, + {"adb hostname skips query param", "https://adb-123456.azuredatabricks.net", "123456", "https://adb-123456.azuredatabricks.net"}, + {"adb hostname mismatch adds param", "https://adb-999.azuredatabricks.net", "123456", "https://adb-999.azuredatabricks.net?w=123456"}, + {"connection-id-style workspace ID passes through", "https://spog.example.com", "123e4567-e89b-12d3-a456-426614174000", "https://spog.example.com?w=123e4567-e89b-12d3-a456-426614174000"}, } for _, tt := range tests { @@ -66,7 +67,7 @@ func TestWorkspaceBaseURL(t *testing.T) { } func TestWorkspaceBaseURLInvalidHost(t *testing.T) { - _, err := workspaceBaseURL("://invalid", 0) + _, err := workspaceBaseURL("://invalid", "") assert.ErrorContains(t, err, "invalid workspace host") } @@ -76,14 +77,14 @@ func TestBuildResourceURL(t *testing.T) { host string resourceType string id string - workspaceID int64 + workspaceID string expected string }{ - {"simple path", "https://host.com", "jobs", "123", 0, "https://host.com/jobs/123"}, - {"path with workspace ID", "https://host.com", "jobs", "123", 456, "https://host.com/jobs/123?w=456"}, - {"fragment pattern", "https://host.com", "notebooks", "12345", 0, "https://host.com/#notebook/12345"}, - {"fragment with workspace ID", "https://host.com", "notebooks", "12345", 789, "https://host.com/?w=789#notebook/12345"}, - {"registered model normalizes dots", "https://host.com", "registered_models", "catalog.schema.model", 0, "https://host.com/explore/data/models/catalog/schema/model"}, + {"simple path", "https://host.com", "jobs", "123", "", "https://host.com/jobs/123"}, + {"path with workspace ID", "https://host.com", "jobs", "123", "456", "https://host.com/jobs/123?w=456"}, + {"fragment pattern", "https://host.com", "notebooks", "12345", "", "https://host.com/#notebook/12345"}, + {"fragment with workspace ID", "https://host.com", "notebooks", "12345", "789", "https://host.com/?w=789#notebook/12345"}, + {"registered model normalizes dots", "https://host.com", "registered_models", "catalog.schema.model", "", "https://host.com/explore/data/models/catalog/schema/model"}, } for _, tt := range tests { @@ -96,7 +97,7 @@ func TestBuildResourceURL(t *testing.T) { } func TestBuildResourceURLUnknownType(t *testing.T) { - _, err := BuildResourceURL("https://host.com", "unknown", "123", 0) + _, err := BuildResourceURL("https://host.com", "unknown", "123", "") assert.ErrorContains(t, err, "unknown resource type") }