diff --git a/build.gradle.kts b/build.gradle.kts index cfc6a750..d0a187bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -88,10 +88,14 @@ tasks.register("integrationTest") { val generatedVersionDir = layout.buildDirectory.dir("generated-version") +// Not "version.properties" at the jar root. stream-chat-java ships that same name, and +// ClassLoader.getResourceAsStream returns whichever jar comes first. +val sdkVersionResource = "io/getstream/stream-sdk-java/version.properties" + tasks.register("generateVersionProperties") { doLast { - val f = layout.buildDirectory.file("generated-version/version.properties") - val propertiesFile = f.get().asFile + val propertiesFile = + layout.buildDirectory.file("generated-version/$sdkVersionResource").get().asFile propertiesFile.parentFile.mkdirs() val properties = Properties() properties.setProperty("version", version.toString()) diff --git a/src/main/java/io/getstream/services/framework/StreamHTTPClient.java b/src/main/java/io/getstream/services/framework/StreamHTTPClient.java index 9dc86ed7..e679be35 100644 --- a/src/main/java/io/getstream/services/framework/StreamHTTPClient.java +++ b/src/main/java/io/getstream/services/framework/StreamHTTPClient.java @@ -132,12 +132,23 @@ public StreamHTTPClient(Properties properties, @NotNull StreamClientOptions opti .compact(); } + // Unique to this jar. A root version.properties collides with stream-chat-java (and any + // other dependency that ships that name): the classloader returns the first match. + private static final String VERSION_RESOURCE = "/io/getstream/stream-sdk-java/version.properties"; + private static @NotNull String readSdkVersion() { - var clsLoader = StreamHTTPClient.class.getClassLoader(); - try (var inputStream = clsLoader.getResourceAsStream("version.properties")) { + try (var inputStream = StreamHTTPClient.class.getResourceAsStream(VERSION_RESOURCE)) { + if (inputStream == null) { + throw new IllegalStateException("missing SDK version resource " + VERSION_RESOURCE); + } var properties = new Properties(); properties.load(inputStream); - return properties.getProperty("version"); + var version = properties.getProperty("version"); + if (version == null || version.isBlank()) { + throw new IllegalStateException( + "SDK version resource " + VERSION_RESOURCE + " has no version"); + } + return version; } catch (IOException ex) { throw new IllegalStateException(ex); } diff --git a/src/test/java/io/getstream/services/framework/LoggingTest.java b/src/test/java/io/getstream/services/framework/LoggingTest.java index 04e98f9a..9b0510f0 100644 --- a/src/test/java/io/getstream/services/framework/LoggingTest.java +++ b/src/test/java/io/getstream/services/framework/LoggingTest.java @@ -105,7 +105,7 @@ void clientInitializedOnceWithSchema() { var inits = log.named("client.initialized"); assertEquals(1, inits.size()); String m = inits.get(0).message(); - // sdk.name is a fixed constant; version is present but its value depends on version.properties. + // sdk.name is a fixed constant; version is present but its value comes from this SDK's jar. assertTrue(m.contains("stream.sdk.name=stream-sdk-java"), m); assertTrue(m.contains("stream.sdk.version="), m); // Pool/timeout knobs equal the StreamClientOptions defaults for a default-options client. diff --git a/src/test/java/io/getstream/services/framework/SdkVersionTest.java b/src/test/java/io/getstream/services/framework/SdkVersionTest.java new file mode 100644 index 00000000..a10fa748 --- /dev/null +++ b/src/test/java/io/getstream/services/framework/SdkVersionTest.java @@ -0,0 +1,60 @@ +package io.getstream.services.framework; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.Map; +import java.util.Properties; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +// src/test/resources/version.properties is on the classpath ahead of the SDK jar and would be +// what ClassLoader.getResourceAsStream("version.properties") returned. The header must ignore it. +public class SdkVersionTest { + private MockWebServer server; + + @BeforeEach + void setUp() throws Exception { + server = new MockWebServer(); + server.start(); + } + + @AfterEach + void tearDown() throws Exception { + server.shutdown(); + } + + @Test + void clientHeaderUsesThisSdkVersionWhenAnotherVersionPropertiesIsFirst() throws Exception { + try (var decoy = + StreamHTTPClient.class.getClassLoader().getResourceAsStream("version.properties")) { + assertNotNull(decoy, "the test fixture version.properties is not on the classpath"); + var decoyProps = new Properties(); + decoyProps.load(decoy); + assertEquals("0.0.0-collision", decoyProps.getProperty("version")); + } + + String expected; + try (var own = + StreamHTTPClient.class.getResourceAsStream( + "/io/getstream/stream-sdk-java/version.properties")) { + assertNotNull(own); + var ownProps = new Properties(); + ownProps.load(own); + expected = "stream-java-client-" + ownProps.getProperty("version"); + } + + server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); + var client = new StreamHTTPClient("key", "012345678901234567890123456789ab"); + client.setBaseUrl(server.url("/").toString()); + new StreamRequest>( + client, "GET", "/api/v2/app", null, null, new TypeReference<>() {}) + .execute(); + + assertEquals(expected, server.takeRequest().getHeader("X-Stream-Client")); + } +} diff --git a/src/test/resources/version.properties b/src/test/resources/version.properties new file mode 100644 index 00000000..3440ca8e --- /dev/null +++ b/src/test/resources/version.properties @@ -0,0 +1 @@ +version=0.0.0-collision