Skip to content

Commit 782d3c9

Browse files
committed
Add support for parsing scopes from config file
1 parent 724548f commit 782d3c9

File tree

4 files changed

+93
-1
lines changed

4 files changed

+93
-1
lines changed

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
import com.databricks.sdk.support.InternalApi;
44
import java.lang.reflect.Field;
5+
import java.lang.reflect.ParameterizedType;
56
import java.time.Duration;
7+
import java.util.Arrays;
8+
import java.util.List;
69
import java.util.Map;
710
import java.util.Objects;
11+
import java.util.stream.Collectors;
812

913
@InternalApi
1014
class ConfigAttributeAccessor {
@@ -63,6 +67,21 @@ public void setValueOnConfig(DatabricksConfig cfg, String value) throws IllegalA
6367
field.set(cfg, seconds > 0 ? Duration.ofSeconds(seconds) : null);
6468
} else if (field.getType() == ProxyConfig.ProxyAuthType.class) {
6569
field.set(cfg, ProxyConfig.ProxyAuthType.valueOf(value));
70+
} else if (List.class.isAssignableFrom(field.getType())) {
71+
// Handle List<String> fields (e.g., scopes)
72+
// Parse comma and/or whitespace separated values from environment variable or config file
73+
if (field.getGenericType() instanceof ParameterizedType) {
74+
ParameterizedType paramType = (ParameterizedType) field.getGenericType();
75+
if (paramType.getActualTypeArguments().length > 0
76+
&& paramType.getActualTypeArguments()[0] == String.class) {
77+
// Split by commas and/or whitespace and filter out empty strings
78+
List<String> list =
79+
Arrays.stream(value.trim().split("[,\\s]+"))
80+
.filter(s -> !s.isEmpty())
81+
.collect(Collectors.toList());
82+
field.set(cfg, list);
83+
}
84+
}
6685
}
6786
field.setAccessible(false);
6887
}
@@ -91,6 +110,10 @@ public String toString() {
91110
}
92111

93112
public String getAsString(Object value) {
113+
if (value instanceof List) {
114+
// Format lists as space-separated values for consistency with input format
115+
return String.join(" ", (List<String>) value);
116+
}
94117
return value.toString();
95118
}
96119

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ private synchronized DatabricksConfig innerResolve() {
204204
try {
205205
ConfigLoader.resolve(this);
206206
ConfigLoader.validate(this);
207+
sortScopes();
207208
ConfigLoader.fixHostIfNeeded(this);
208209
initHttp();
209210
return this;
@@ -212,6 +213,15 @@ private synchronized DatabricksConfig innerResolve() {
212213
}
213214
}
214215

216+
/**
217+
* Sort scopes in-place for better de-duplication in the refresh token cache.
218+
*/
219+
private void sortScopes() {
220+
if (scopes != null && !scopes.isEmpty()) {
221+
java.util.Collections.sort(scopes);
222+
}
223+
}
224+
215225
private void initHttp() {
216226
if (httpClient != null) {
217227
return;

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,52 @@ public void testDisableOauthRefreshTokenEnvironmentVariable() {
322322

323323
assertEquals(true, config.getDisableOauthRefreshToken());
324324
}
325+
326+
// Config File Scope Parsing Tests
327+
328+
@Test
329+
public void testConfigFileScopesEmptyDefaultsToAllApis() {
330+
Map<String, String> env = new HashMap<>();
331+
env.put("HOME", "src/test/resources/testdata");
332+
333+
DatabricksConfig config = new DatabricksConfig().setProfile("scope-empty");
334+
config.resolve(new Environment(env, new ArrayList<>(), System.getProperty("os.name")));
335+
336+
List<String> scopes = config.getScopes();
337+
assertEquals(1, scopes.size());
338+
assertEquals("all-apis", scopes.get(0));
339+
}
340+
341+
@Test
342+
public void testConfigFileScopesSingle() {
343+
Map<String, String> env = new HashMap<>();
344+
env.put("HOME", "src/test/resources/testdata");
345+
346+
DatabricksConfig config = new DatabricksConfig().setProfile("scope-single");
347+
config.resolve(new Environment(env, new ArrayList<>(), System.getProperty("os.name")));
348+
349+
List<String> scopes = config.getScopes();
350+
assertEquals(1, scopes.size());
351+
assertEquals("clusters:read", scopes.get(0));
352+
}
353+
354+
@Test
355+
public void testConfigFileScopesMultipleSorted() {
356+
Map<String, String> env = new HashMap<>();
357+
env.put("HOME", "src/test/resources/testdata");
358+
359+
DatabricksConfig config = new DatabricksConfig().setProfile("scope-multiple");
360+
config.resolve(new Environment(env, new ArrayList<>(), System.getProperty("os.name")));
361+
362+
List<String> scopes = config.getScopes();
363+
// Should be sorted alphabetically
364+
assertEquals(7, scopes.size());
365+
assertEquals("clusters:read", scopes.get(0));
366+
assertEquals("files:read", scopes.get(1));
367+
assertEquals("iam:read", scopes.get(2));
368+
assertEquals("jobs:read", scopes.get(3));
369+
assertEquals("mlflow:read", scopes.get(4));
370+
assertEquals("model-serving:read", scopes.get(5));
371+
assertEquals("pipelines:read", scopes.get(6));
372+
}
325373
}

databricks-sdk-java/src/test/resources/testdata/.databrickscfg

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,15 @@ google_credentials = paw48590aw8e09t8apu
3838

3939
[pat.with.dot]
4040
host = https://dbc-XXXXXXXX-YYYY.cloud.databricks.com/
41-
token = PT0+IC9kZXYvdXJhbmRvbSA8PT0KYFZ
41+
token = PT0+IC9kZXYvdXJhbmRvbSA8PT0KYFZ
42+
43+
[scope-empty]
44+
host = https://example.cloud.databricks.com
45+
46+
[scope-single]
47+
host = https://example.cloud.databricks.com
48+
scopes = clusters:read
49+
50+
[scope-multiple]
51+
host = https://example.cloud.databricks.com
52+
scopes = clusters:read, jobs:read, pipelines:read, iam:read, files:read, mlflow:read, model-serving:read

0 commit comments

Comments
 (0)