From 3f62b7c64c88a76a3e2d22b3a058dfc16f7fb549 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Thu, 16 May 2024 17:37:31 +0200 Subject: [PATCH 1/6] Support partner registration in user agent --- .../com/databricks/sdk/core/UserAgent.java | 25 ++++++++++---- .../databricks/sdk/core/UserAgentTest.java | 34 +++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java index faf3067bd..73033cb13 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java @@ -1,6 +1,8 @@ package com.databricks.sdk.core; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -10,6 +12,8 @@ public class UserAgent { private static final Map otherInfo = new HashMap<>(); + private static final List partners = new ArrayList<>(); + // TODO: check if reading from // /META-INF/maven/com.databricks/databrics-sdk-java/pom.properties // or getClass().getPackage().getImplementationVersion() is enough. @@ -20,6 +24,10 @@ public static void withProduct(String product, String productVersion) { UserAgent.productVersion = productVersion; } + public static void withPartner(String partner) { + partners.add(partner); + } + public static void withOtherInfo(String key, String value) { otherInfo.put(key, value); } @@ -43,12 +51,15 @@ private static String jvmVersion() { } public static String asString() { - String otherInfo = - UserAgent.otherInfo.entrySet().stream() - .map(e -> String.format(" %s/%s", e.getKey(), e.getValue())) - .collect(Collectors.joining()); - return String.format( - "%s/%s databricks-sdk-java/%s jvm/%s os/%s%s", - product, productVersion, version, jvmVersion(), osName(), otherInfo); + List segments = new ArrayList<>(); + segments.add(String.format("%s/%s", product, productVersion)); + segments.add(String.format("databricks-sdk-java/%s", version)); + segments.add(String.format("jvm/%s", jvmVersion())); + segments.add(String.format("os/%s", osName())); + segments.addAll( + otherInfo.entrySet().stream() + .map(e -> String.format("%s/%s", e.getKey(), e.getValue())).collect(Collectors.toSet())); + segments.addAll(partners.stream().map(p -> "partner/" + p).collect(Collectors.toSet())); + return segments.stream().collect(Collectors.joining(" ")); } } diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java new file mode 100644 index 000000000..435c886fa --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java @@ -0,0 +1,34 @@ +package com.databricks.sdk.core; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UserAgentTest { + @Test + public void testUserAgent() { + UserAgent.withProduct("product", "productVersion"); + String userAgent = UserAgent.asString(); + Assertions.assertTrue(userAgent.contains("product/productVersion")); + Assertions.assertTrue(userAgent.contains("databricks-sdk-java/")); + Assertions.assertTrue(userAgent.contains("os/")); + Assertions.assertTrue(userAgent.contains("jvm/")); + } + + @Test + public void testUserAgentWithPartner() { + UserAgent.withPartner("partner1"); + UserAgent.withPartner("partner2"); + String userAgent = UserAgent.asString(); + Assertions.assertTrue(userAgent.contains("partner/partner1")); + Assertions.assertTrue(userAgent.contains("partner/partner2")); + } + + @Test + public void testUserAgentWithOtherInfo() { + UserAgent.withOtherInfo("key1", "value1"); + UserAgent.withOtherInfo("key2", "value2"); + String userAgent = UserAgent.asString(); + Assertions.assertTrue(userAgent.contains("key1/value1")); + Assertions.assertTrue(userAgent.contains("key2/value2")); + } +} From 186a5711432546d7e84b77fb9ad399c22bf1e267 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Thu, 16 May 2024 17:54:18 +0200 Subject: [PATCH 2/6] support multiple entries with the same key --- .../com/databricks/sdk/core/UserAgent.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java index 73033cb13..f69e65445 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java @@ -10,9 +10,25 @@ public class UserAgent { private static String product = "unknown"; private static String productVersion = "0.0.0"; - private static final Map otherInfo = new HashMap<>(); + private static class Info { + private String key; + private String value; - private static final List partners = new ArrayList<>(); + public Info(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + } + + private static final ArrayList otherInfo = new ArrayList<>(); // TODO: check if reading from // /META-INF/maven/com.databricks/databrics-sdk-java/pom.properties @@ -25,11 +41,11 @@ public static void withProduct(String product, String productVersion) { } public static void withPartner(String partner) { - partners.add(partner); + withOtherInfo("partner", partner); } public static void withOtherInfo(String key, String value) { - otherInfo.put(key, value); + otherInfo.add(new Info(key, value)); } private static String osName() { @@ -57,9 +73,8 @@ public static String asString() { segments.add(String.format("jvm/%s", jvmVersion())); segments.add(String.format("os/%s", osName())); segments.addAll( - otherInfo.entrySet().stream() + otherInfo.stream() .map(e -> String.format("%s/%s", e.getKey(), e.getValue())).collect(Collectors.toSet())); - segments.addAll(partners.stream().map(p -> "partner/" + p).collect(Collectors.toSet())); return segments.stream().collect(Collectors.joining(" ")); } } From 197e18976fd74b526066b21baf5416ee103d1a65 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Fri, 17 May 2024 17:10:01 +0200 Subject: [PATCH 3/6] validation gp --- .../com/databricks/sdk/core/UserAgent.java | 49 +++++++++++++++++++ .../databricks/sdk/core/UserAgentTest.java | 21 ++++++++ 2 files changed, 70 insertions(+) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java index f69e65445..43e927d33 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class UserAgent { @@ -40,11 +41,59 @@ public static void withProduct(String product, String productVersion) { UserAgent.productVersion = productVersion; } + // Regular expression copied from https://semver.org/. + private static final String semVerCore = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)"; + private static final String semVerPrerelease = "(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"; + private static final String semVerBuildmetadata = "(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?"; + private static final Pattern regexpSemVer = Pattern.compile("^" + semVerCore + semVerPrerelease + semVerBuildmetadata + "$"); + + // Sanitize replaces all non-alphanumeric characters with a hyphen. Use this to + // ensure that the user agent value is valid. This is useful when the value is not + // ensured to be valid at compile time. + // + // Example: You want to avoid having '/' and ' ' in the value because it will + // make downstream applications fail. + // + // Note: Semver strings are comprised of alphanumeric characters, hyphens, periods + // and plus signs. This function will not remove these characters. + // see: + // 1. https://semver.org/#spec-item-9 + // 2. https://semver.org/#spec-item-10 + private static final Pattern regexpAlphanum = Pattern.compile("^[0-9A-Za-z_\\.\\+-]+$"); + private static final Pattern regexpAlphanumInverse = Pattern.compile("[^0-9A-Za-z_\\.\\+-]"); + + public static String sanitize(String s) { + return regexpAlphanumInverse.matcher(s).replaceAll("-"); + } + + public static boolean matchSemVer(String s) throws IllegalArgumentException { + if (regexpSemVer.matcher(s).matches()) { + return true; + } + throw new IllegalArgumentException("Invalid semver string: " + s); + } + + public static boolean matchAlphanum(String s) throws IllegalArgumentException { + if (regexpAlphanum.matcher(s).matches()) { + return true; + } + throw new IllegalArgumentException("Invalid alphanumeric string: " + s); + } + + public static boolean matchAlphanumOrSemVer(String s) throws IllegalArgumentException { + if (regexpAlphanum.matcher(s).matches() || regexpSemVer.matcher(s).matches()) { + return true; + } + throw new IllegalArgumentException("Invalid alphanumeric or semver string: " + s); + } + public static void withPartner(String partner) { withOtherInfo("partner", partner); } public static void withOtherInfo(String key, String value) { + matchAlphanum(key); + matchAlphanumOrSemVer(value); otherInfo.add(new Info(key, value)); } diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java index 435c886fa..b7931d760 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java @@ -31,4 +31,25 @@ public void testUserAgentWithOtherInfo() { Assertions.assertTrue(userAgent.contains("key1/value1")); Assertions.assertTrue(userAgent.contains("key2/value2")); } + + @Test + public void testUserAgentWithInvalidKey() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + UserAgent.withOtherInfo("key1!", "value1"); + }); + } + + @Test + public void testUserAgentWithInvalidValue() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + UserAgent.withOtherInfo("key1", "value1!"); + }); + } + + @Test + public void testUserAgentWithSemverValue() { + UserAgent.withOtherInfo("key1", "1.0.0-dev+metadata"); + String userAgent = UserAgent.asString(); + Assertions.assertTrue(userAgent.contains("key1/1.0.0-dev+metadata")); + } } From a8c48b638838d731ba5a81b4843364aae0ef1ae3 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Fri, 17 May 2024 17:14:36 +0200 Subject: [PATCH 4/6] fmt --- .../java/com/databricks/sdk/core/UserAgent.java | 11 ++++++----- .../com/databricks/sdk/core/UserAgentTest.java | 16 ++++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java index 43e927d33..d35f3d0e0 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java @@ -1,9 +1,7 @@ package com.databricks.sdk.core; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -43,9 +41,11 @@ public static void withProduct(String product, String productVersion) { // Regular expression copied from https://semver.org/. private static final String semVerCore = "(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)"; - private static final String semVerPrerelease = "(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"; + private static final String semVerPrerelease = + "(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"; private static final String semVerBuildmetadata = "(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?"; - private static final Pattern regexpSemVer = Pattern.compile("^" + semVerCore + semVerPrerelease + semVerBuildmetadata + "$"); + private static final Pattern regexpSemVer = + Pattern.compile("^" + semVerCore + semVerPrerelease + semVerBuildmetadata + "$"); // Sanitize replaces all non-alphanumeric characters with a hyphen. Use this to // ensure that the user agent value is valid. This is useful when the value is not @@ -123,7 +123,8 @@ public static String asString() { segments.add(String.format("os/%s", osName())); segments.addAll( otherInfo.stream() - .map(e -> String.format("%s/%s", e.getKey(), e.getValue())).collect(Collectors.toSet())); + .map(e -> String.format("%s/%s", e.getKey(), e.getValue())) + .collect(Collectors.toSet())); return segments.stream().collect(Collectors.joining(" ")); } } diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java index b7931d760..b44c07e7c 100644 --- a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java @@ -34,16 +34,20 @@ public void testUserAgentWithOtherInfo() { @Test public void testUserAgentWithInvalidKey() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - UserAgent.withOtherInfo("key1!", "value1"); - }); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> { + UserAgent.withOtherInfo("key1!", "value1"); + }); } @Test public void testUserAgentWithInvalidValue() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - UserAgent.withOtherInfo("key1", "value1!"); - }); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> { + UserAgent.withOtherInfo("key1", "value1!"); + }); } @Test From 248b5cb2bfb4eb44fdbe5c1455a5f952129f47b9 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Tue, 28 May 2024 13:23:26 +0200 Subject: [PATCH 5/6] fix comment --- .../com/databricks/sdk/core/UserAgent.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java index d35f3d0e0..8268f16e1 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java @@ -47,21 +47,18 @@ public static void withProduct(String product, String productVersion) { private static final Pattern regexpSemVer = Pattern.compile("^" + semVerCore + semVerPrerelease + semVerBuildmetadata + "$"); - // Sanitize replaces all non-alphanumeric characters with a hyphen. Use this to - // ensure that the user agent value is valid. This is useful when the value is not - // ensured to be valid at compile time. - // - // Example: You want to avoid having '/' and ' ' in the value because it will - // make downstream applications fail. - // - // Note: Semver strings are comprised of alphanumeric characters, hyphens, periods - // and plus signs. This function will not remove these characters. - // see: - // 1. https://semver.org/#spec-item-9 - // 2. https://semver.org/#spec-item-10 private static final Pattern regexpAlphanum = Pattern.compile("^[0-9A-Za-z_\\.\\+-]+$"); private static final Pattern regexpAlphanumInverse = Pattern.compile("[^0-9A-Za-z_\\.\\+-]"); + /** + * Replaces all non-alphanumeric characters with a hyphen. Use this to ensure that the user agent value is valid. This + * is useful when the value is not ensured to be valid at compile time. + *

+ * Note: Semver strings are comprised of alphanumeric characters, hyphens, periods + * and plus signs. This function will not remove these characters. + * @param s The string to sanitize. + * @return The sanitized string. + */ public static String sanitize(String s) { return regexpAlphanumInverse.matcher(s).replaceAll("-"); } From 74cc8ccb14a5c21eebe1c4dabd5f8494aa4d1626 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Fri, 7 Jun 2024 17:07:44 +0200 Subject: [PATCH 6/6] fmt --- .../main/java/com/databricks/sdk/core/UserAgent.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java index 8268f16e1..aeefd5c5b 100644 --- a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java @@ -51,11 +51,12 @@ public static void withProduct(String product, String productVersion) { private static final Pattern regexpAlphanumInverse = Pattern.compile("[^0-9A-Za-z_\\.\\+-]"); /** - * Replaces all non-alphanumeric characters with a hyphen. Use this to ensure that the user agent value is valid. This - * is useful when the value is not ensured to be valid at compile time. - *

- * Note: Semver strings are comprised of alphanumeric characters, hyphens, periods - * and plus signs. This function will not remove these characters. + * Replaces all non-alphanumeric characters with a hyphen. Use this to ensure that the user agent + * value is valid. This is useful when the value is not ensured to be valid at compile time. + * + *

Note: Semver strings are comprised of alphanumeric characters, hyphens, periods and plus + * signs. This function will not remove these characters. + * * @param s The string to sanitize. * @return The sanitized string. */