Skip to content

Commit 30b5035

Browse files
Add method to fetch Host Metadata (#678)
## 🥞 Stacked PR Use this [link](https://github.com/databricks/databricks-sdk-java/pull/678/files) to review incremental changes. - [**stack/config-auto-complete-1**](#678) [[Files changed](https://github.com/databricks/databricks-sdk-java/pull/678/files)] - [stack/config-auto-complete-2](#679) [[Files changed](https://github.com/databricks/databricks-sdk-java/pull/679/files/82fbbc2e5c9b9f38705df9a88ad0b6898d793b82..37e2032fd767f99b0c52b6ea57f60dea92e516e1)] - [stack/config-auto-complete-3](#680) [[Files changed](https://github.com/databricks/databricks-sdk-java/pull/680/files/adbceef3cc10ffef3e5844fe09f58c210f922f6a..18b73249bb564d01fe237f2464145a2ca025e9b4)] --------- ## Summary Adds `HostMetadata` and a package-private `getHostMetadata()` on `DatabricksConfig` for parsing the `/.well-known/databricks-config` discovery endpoint. ## Why Databricks hosts expose a standard `/.well-known/databricks-config` endpoint that returns the OIDC endpoint, account ID, and workspace ID in a single request. The SDK had no primitive to consume it — OIDC endpoint discovery was handled entirely through host-type-specific logic that requires the caller to already know whether the host is a workspace, account console, or unified host. This PR introduces the foundational building block: a `HostMetadata` class and a package-private `getHostMetadata()` on `DatabricksConfig` that fetches and parses the endpoint. The method returns raw metadata with no substitution (e.g. `{account_id}` placeholders are left as-is), keeping it a pure discovery primitive. Callers decide how to interpret the result. ## What changed ### Interface changes - **`HostMetadata`** — New class in `com.databricks.sdk.core.oauth` with fields `oidcEndpoint`, `accountId`, `workspaceId`. Deserialized from JSON via Jackson. - **`DatabricksConfig.getHostMetadata()` (package-private)** — Fetches `{host}/.well-known/databricks-config` and returns a `HostMetadata`. Throws `DatabricksException` on any HTTP error. ### Behavioral changes None. No existing code paths are modified. ### Internal changes Tests in `DatabricksConfigTest` covering the two response shapes (workspace with static OIDC endpoint, account host with `{account_id}` template) and the HTTP error path. ## How is this tested? Unit tests in `DatabricksConfigTest` using `FixtureServer`. Both workspace and account host response shapes are exercised, plus an HTTP error case. NO_CHANGELOG=true
1 parent d86b772 commit 30b5035

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.databricks.sdk.core.http.Request;
66
import com.databricks.sdk.core.http.Response;
77
import com.databricks.sdk.core.oauth.ErrorTokenSource;
8+
import com.databricks.sdk.core.oauth.HostMetadata;
89
import com.databricks.sdk.core.oauth.OAuthHeaderFactory;
910
import com.databricks.sdk.core.oauth.OpenIDConnectEndpoints;
1011
import com.databricks.sdk.core.oauth.TokenSource;
@@ -800,6 +801,32 @@ public OpenIDConnectEndpoints getDatabricksOidcEndpoints() throws IOException {
800801
return fetchOidcEndpointsFromDiscovery();
801802
}
802803

804+
/**
805+
* [Experimental] Fetch the raw Databricks well-known configuration from
806+
* {host}/.well-known/databricks-config.
807+
*
808+
* <p><b>Note:</b> This API is experimental and may change or be removed in future releases
809+
* without notice.
810+
*
811+
* @return Parsed {@link HostMetadata} as returned by the server.
812+
* @throws DatabricksException if the request fails or the server returns a non-200 status.
813+
*/
814+
HostMetadata getHostMetadata() throws IOException {
815+
String url = host + "/.well-known/databricks-config";
816+
try {
817+
Request request = new Request("GET", url);
818+
Response resp = getHttpClient().execute(request);
819+
if (resp.getStatusCode() != 200) {
820+
throw new DatabricksException(
821+
"Failed to fetch host metadata from " + url + ": HTTP " + resp.getStatusCode());
822+
}
823+
return new ObjectMapper().readValue(resp.getBody(), HostMetadata.class);
824+
} catch (IOException e) {
825+
throw new DatabricksException(
826+
"Failed to fetch host metadata from " + url + ": " + e.getMessage(), e);
827+
}
828+
}
829+
803830
private OpenIDConnectEndpoints fetchOidcEndpointsFromDiscovery() {
804831
try {
805832
Request request = new Request("GET", discoveryUrl);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.databricks.sdk.core.oauth;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
/**
7+
* [Experimental] Parsed response from the /.well-known/databricks-config discovery endpoint.
8+
*
9+
* <p><b>Note:</b> This API is experimental and may change or be removed in future releases without
10+
* notice.
11+
*/
12+
@JsonIgnoreProperties(ignoreUnknown = true)
13+
public class HostMetadata {
14+
@JsonProperty("oidc_endpoint")
15+
private String oidcEndpoint;
16+
17+
@JsonProperty("account_id")
18+
private String accountId;
19+
20+
@JsonProperty("workspace_id")
21+
private String workspaceId;
22+
23+
public HostMetadata() {}
24+
25+
public HostMetadata(String oidcEndpoint, String accountId, String workspaceId) {
26+
this.oidcEndpoint = oidcEndpoint;
27+
this.accountId = accountId;
28+
this.workspaceId = workspaceId;
29+
}
30+
31+
public String getOidcEndpoint() {
32+
return oidcEndpoint;
33+
}
34+
35+
public String getAccountId() {
36+
return accountId;
37+
}
38+
39+
public String getWorkspaceId() {
40+
return workspaceId;
41+
}
42+
}

databricks-sdk-java/src/test/java/com/databricks/sdk/core/DatabricksConfigTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.databricks.sdk.core.commons.CommonsHttpClient;
77
import com.databricks.sdk.core.http.HttpClient;
88
import com.databricks.sdk.core.oauth.ErrorTokenSource;
9+
import com.databricks.sdk.core.oauth.HostMetadata;
910
import com.databricks.sdk.core.oauth.OAuthHeaderFactory;
1011
import com.databricks.sdk.core.oauth.OpenIDConnectEndpoints;
1112
import com.databricks.sdk.core.oauth.Token;
@@ -421,4 +422,61 @@ public void testGetClientTypeAccountOnUnified() {
421422
.setExperimentalIsUnifiedHost(true)
422423
.getClientType());
423424
}
425+
426+
// --- HostMetadata tests ---
427+
428+
private static final String DUMMY_ACCOUNT_ID = "00000000-0000-0000-0000-000000000001";
429+
private static final String DUMMY_WORKSPACE_ID = "111111111111111";
430+
431+
private static Environment emptyEnv() {
432+
return new Environment(new HashMap<>(), new ArrayList<>(), System.getProperty("os.name"));
433+
}
434+
435+
@Test
436+
public void testGetHostMetadataWorkspaceStaticOidcEndpoint() throws IOException {
437+
String response =
438+
"{\"oidc_endpoint\":\"https://ws.databricks.com/oidc\","
439+
+ "\"account_id\":\""
440+
+ DUMMY_ACCOUNT_ID
441+
+ "\","
442+
+ "\"workspace_id\":\""
443+
+ DUMMY_WORKSPACE_ID
444+
+ "\"}";
445+
try (FixtureServer server =
446+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
447+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
448+
config.resolve(emptyEnv());
449+
HostMetadata meta = config.getHostMetadata();
450+
assertEquals("https://ws.databricks.com/oidc", meta.getOidcEndpoint());
451+
assertEquals(DUMMY_ACCOUNT_ID, meta.getAccountId());
452+
assertEquals(DUMMY_WORKSPACE_ID, meta.getWorkspaceId());
453+
}
454+
}
455+
456+
@Test
457+
public void testGetHostMetadataAccountRawOidcTemplate() throws IOException {
458+
String response =
459+
"{\"oidc_endpoint\":\"https://acc.databricks.com/oidc/accounts/{account_id}\"}";
460+
try (FixtureServer server =
461+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
462+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
463+
config.resolve(emptyEnv());
464+
HostMetadata meta = config.getHostMetadata();
465+
assertEquals("https://acc.databricks.com/oidc/accounts/{account_id}", meta.getOidcEndpoint());
466+
assertNull(meta.getAccountId());
467+
assertNull(meta.getWorkspaceId());
468+
}
469+
}
470+
471+
@Test
472+
public void testGetHostMetadataRaisesOnHttpError() throws IOException {
473+
try (FixtureServer server =
474+
new FixtureServer().with("GET", "/.well-known/databricks-config", "{}", 404)) {
475+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
476+
config.resolve(emptyEnv());
477+
DatabricksException ex =
478+
assertThrows(DatabricksException.class, () -> config.getHostMetadata());
479+
assertTrue(ex.getMessage().contains("Failed to fetch host metadata"));
480+
}
481+
}
424482
}

0 commit comments

Comments
 (0)