From 082564e6e2c716ac62fc2fc98523e0e2aab5b7fd Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Fri, 14 Jun 2024 15:37:49 -0700 Subject: [PATCH 1/6] feat: add runtime version metadata to the first Momento call Add a new Runtime-Version key to the metadata of the first Momento call made by a client. It contains JVM or Android-specific version info. Move the implementation-specific sdk and runtime version logic into an expect class with jvm and android versions. --- .../sdk/internal/ControlGrpcStubsManager.kt | 2 +- .../sdk/internal/DataGrpcStubsManager.kt | 2 +- .../sdk/internal/TopicGrpcStubsManager.kt | 2 +- .../internal/UserHeaderInterceptor.android.kt | 19 +++++++++++++++++++ .../sdk/internal/UserHeaderInterceptor.kt | 13 ++++++++++--- .../sdk/internal/ControlGrpcStubsManager.kt | 2 +- .../sdk/internal/DataGrpcStubsManager.kt | 2 +- .../sdk/internal/TopicGrpcStubsManager.kt | 2 +- .../sdk/internal/UserHeaderInterceptor.jvm.kt | 16 ++++++++++++++++ 9 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt create mode 100644 src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt index faaf8a9..ade819a 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt @@ -43,7 +43,7 @@ internal class ControlGrpcStubsManager(credentialProvider: CredentialProvider) : channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor("android", credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt index d42a48c..3e8a012 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt @@ -47,7 +47,7 @@ internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, conf channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor("android", credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt index a179d63..5b0c21a 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt @@ -59,7 +59,7 @@ internal class TopicGrpcStubsManager(credentialProvider: CredentialProvider) : C channelBuilder.keepAliveTimeout(5, TimeUnit.SECONDS) channelBuilder.keepAliveWithoutCalls(true) val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor("android", credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt new file mode 100644 index 0000000..7c62ba0 --- /dev/null +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt @@ -0,0 +1,19 @@ +package software.momento.kotlin.sdk.internal + +import android.os.Build + +internal actual class PlatformInfo { + internal actual val sdkVersion: String + get() { + val version = this.javaClass.getPackage()?.implementationVersion ?: "unknown" + return "kotlin-android:$version" + } + internal actual val runtimeVersion: String + get() { + val kotlinVersion = KotlinVersion.CURRENT + val androidVersion = Build.VERSION.RELEASE + val deviceModel = Build.MODEL + val manufacturer = Build.MANUFACTURER + return "Android $androidVersion, Kotlin $kotlinVersion, $manufacturer $deviceModel" + } +} diff --git a/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt b/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt index b2fae15..b14f154 100644 --- a/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt +++ b/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt @@ -2,8 +2,7 @@ package software.momento.kotlin.sdk.internal import kotlin.jvm.Volatile -internal class UserHeaderInterceptor(sdkType: String, private val tokenValue: String) : io.grpc.ClientInterceptor { - private val sdkVersion = String.format("kotlin-$sdkType:%s", this.javaClass.getPackage()?.implementationVersion ?: "unknown") +internal class UserHeaderInterceptor(private val tokenValue: String) : io.grpc.ClientInterceptor { override fun interceptCall( methodDescriptor: io.grpc.MethodDescriptor, callOptions: io.grpc.CallOptions, @@ -15,7 +14,9 @@ internal class UserHeaderInterceptor(sdkType: String, private val tokenValue: St override fun start(listener: Listener, metadata: io.grpc.Metadata) { metadata.put(AUTH_HEADER_KEY, tokenValue) if (!isUserAgentSent) { - metadata.put(SDK_AGENT_KEY, sdkVersion) + val platformInfo = PlatformInfo() + metadata.put(SDK_AGENT_KEY, platformInfo.sdkVersion) + metadata.put(RUNTIME_VERSION_KEY, platformInfo.runtimeVersion) isUserAgentSent = true } super.start(listener, metadata) @@ -28,9 +29,15 @@ internal class UserHeaderInterceptor(sdkType: String, private val tokenValue: St io.grpc.Metadata.Key.of("Authorization", io.grpc.Metadata.ASCII_STRING_MARSHALLER) private val SDK_AGENT_KEY: io.grpc.Metadata.Key = io.grpc.Metadata.Key.of("Agent", io.grpc.Metadata.ASCII_STRING_MARSHALLER) + private val RUNTIME_VERSION_KEY: io.grpc.Metadata.Key = + io.grpc.Metadata.Key.of("Runtime-Version", io.grpc.Metadata.ASCII_STRING_MARSHALLER) @Volatile private var isUserAgentSent = false } } +internal expect class PlatformInfo() { + internal val sdkVersion: String + internal val runtimeVersion: String +} diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt index efc75aa..3c12415 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt @@ -44,7 +44,7 @@ internal class ControlGrpcStubsManager(credentialProvider: CredentialProvider) : channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor("jvm", credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt index 1db02b1..b16122e 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt @@ -46,7 +46,7 @@ internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, conf channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor("jvm", credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt index e9b9080..5b0c21a 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt @@ -59,7 +59,7 @@ internal class TopicGrpcStubsManager(credentialProvider: CredentialProvider) : C channelBuilder.keepAliveTimeout(5, TimeUnit.SECONDS) channelBuilder.keepAliveWithoutCalls(true) val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor("jvm", credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt new file mode 100644 index 0000000..3e64391 --- /dev/null +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt @@ -0,0 +1,16 @@ +package software.momento.kotlin.sdk.internal + +internal actual class PlatformInfo { + internal actual val sdkVersion: String + get() { + val version = this.javaClass.getPackage()?.implementationVersion ?: "unknown" + return "kotlin-jvm:$version" + } + internal actual val runtimeVersion: String + get() { + val javaVendor = System.getProperty("java.vendor") + val javaVersion = System.getProperty("java.version") + val kotlinVersion = KotlinVersion.CURRENT + return "$javaVendor:$javaVersion, Kotlin $kotlinVersion" + } +} From a1c7ac9715b63bca5b1a981d32b500ca4dcc2fc4 Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Mon, 17 Jun 2024 14:09:04 -0700 Subject: [PATCH 2/6] fix: switch android github build to ubuntu Switch the android build on github to ubuntu, since it is now recommended as the faster and cheaper way of running and android emulator. --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 047a734..f404696 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: android: # The Android emulator only has hardware acceleration on macOS. - runs-on: macos-latest + runs-on: ubuntu-latest strategy: matrix: api-level: [ 23 ] @@ -72,6 +72,12 @@ jobs: with: arguments: clean build -x jvmTest -x testDebugUnitTest -x testReleaseUnitTest + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: run tests uses: reactivecircus/android-emulator-runner@v2 with: From b5735ec978201e4ba3e77461cb3c537bebb06185 Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Mon, 1 Jul 2024 10:28:26 -0700 Subject: [PATCH 3/6] Lowercase metadata keys. Add client type to agent string. Switch isUserAgentSent in UserHeaderInterceptor to a local variable so the agent header will be sent with every client creation instead of once per run of the program. --- .../sdk/internal/ControlGrpcStubsManager.kt | 3 ++- .../sdk/internal/DataGrpcStubsManager.kt | 6 ++++-- .../sdk/internal/TopicGrpcStubsManager.kt | 2 +- .../internal/UserHeaderInterceptor.android.kt | 10 +++++----- .../sdk/internal/UserHeaderInterceptor.kt | 19 +++++++++---------- .../sdk/internal/ControlGrpcStubsManager.kt | 4 ++-- .../sdk/internal/DataGrpcStubsManager.kt | 7 ++++--- .../sdk/internal/TopicGrpcStubsManager.kt | 2 +- .../sdk/internal/UserHeaderInterceptor.jvm.kt | 10 +++++----- 9 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt index ade819a..161367e 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt @@ -17,6 +17,7 @@ internal class ControlGrpcStubsManager(credentialProvider: CredentialProvider) : channel = setupConnection(credentialProvider) futureStub = ScsControlGrpcKt.ScsControlCoroutineStub(channel) } + val stub: ScsControlGrpcKt.ScsControlCoroutineStub /** * Returns a stub with appropriate deadlines. @@ -43,7 +44,7 @@ internal class ControlGrpcStubsManager(credentialProvider: CredentialProvider) : channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey, "cache")) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt index 3e8a012..40b7944 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt @@ -11,7 +11,8 @@ import java.util.concurrent.TimeUnit import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes -internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, configuration: GrpcConfiguration) : Closeable { +internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, configuration: GrpcConfiguration) : + Closeable { private val deadline: Duration private val channel: ManagedChannel private val futureStub: ScsGrpcKt.ScsCoroutineStub @@ -21,6 +22,7 @@ internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, conf channel = setupConnection(credentialProvider) futureStub = ScsGrpcKt.ScsCoroutineStub(channel) } + val stub: ScsGrpcKt.ScsCoroutineStub /** * Returns a stub with appropriate deadlines. @@ -47,7 +49,7 @@ internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, conf channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey, "cache")) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt index 5b0c21a..7ab61dd 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt @@ -59,7 +59,7 @@ internal class TopicGrpcStubsManager(credentialProvider: CredentialProvider) : C channelBuilder.keepAliveTimeout(5, TimeUnit.SECONDS) channelBuilder.keepAliveWithoutCalls(true) val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey, "cache")) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt index 7c62ba0..7d6918e 100644 --- a/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt +++ b/src/androidMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.android.kt @@ -3,11 +3,6 @@ package software.momento.kotlin.sdk.internal import android.os.Build internal actual class PlatformInfo { - internal actual val sdkVersion: String - get() { - val version = this.javaClass.getPackage()?.implementationVersion ?: "unknown" - return "kotlin-android:$version" - } internal actual val runtimeVersion: String get() { val kotlinVersion = KotlinVersion.CURRENT @@ -16,4 +11,9 @@ internal actual class PlatformInfo { val manufacturer = Build.MANUFACTURER return "Android $androidVersion, Kotlin $kotlinVersion, $manufacturer $deviceModel" } + + internal actual fun getSdkVersion(clientType: String): String { + val version = this.javaClass.getPackage()?.implementationVersion ?: "unknown" + return "kotlin-android:$clientType:$version" + } } diff --git a/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt b/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt index b14f154..5d43023 100644 --- a/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt +++ b/src/commonMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.kt @@ -1,8 +1,9 @@ package software.momento.kotlin.sdk.internal -import kotlin.jvm.Volatile +internal class UserHeaderInterceptor(private val tokenValue: String, private val clientType: String) : + io.grpc.ClientInterceptor { + private var isUserAgentSent = false -internal class UserHeaderInterceptor(private val tokenValue: String) : io.grpc.ClientInterceptor { override fun interceptCall( methodDescriptor: io.grpc.MethodDescriptor, callOptions: io.grpc.CallOptions, @@ -15,7 +16,7 @@ internal class UserHeaderInterceptor(private val tokenValue: String) : io.grpc.C metadata.put(AUTH_HEADER_KEY, tokenValue) if (!isUserAgentSent) { val platformInfo = PlatformInfo() - metadata.put(SDK_AGENT_KEY, platformInfo.sdkVersion) + metadata.put(SDK_AGENT_KEY, platformInfo.getSdkVersion(clientType)) metadata.put(RUNTIME_VERSION_KEY, platformInfo.runtimeVersion) isUserAgentSent = true } @@ -26,18 +27,16 @@ internal class UserHeaderInterceptor(private val tokenValue: String) : io.grpc.C companion object { private val AUTH_HEADER_KEY: io.grpc.Metadata.Key = - io.grpc.Metadata.Key.of("Authorization", io.grpc.Metadata.ASCII_STRING_MARSHALLER) + io.grpc.Metadata.Key.of("authorization", io.grpc.Metadata.ASCII_STRING_MARSHALLER) private val SDK_AGENT_KEY: io.grpc.Metadata.Key = - io.grpc.Metadata.Key.of("Agent", io.grpc.Metadata.ASCII_STRING_MARSHALLER) + io.grpc.Metadata.Key.of("agent", io.grpc.Metadata.ASCII_STRING_MARSHALLER) private val RUNTIME_VERSION_KEY: io.grpc.Metadata.Key = - io.grpc.Metadata.Key.of("Runtime-Version", io.grpc.Metadata.ASCII_STRING_MARSHALLER) - - @Volatile - private var isUserAgentSent = false + io.grpc.Metadata.Key.of("runtime-version", io.grpc.Metadata.ASCII_STRING_MARSHALLER) } } internal expect class PlatformInfo() { - internal val sdkVersion: String internal val runtimeVersion: String + + internal fun getSdkVersion(clientType: String): String } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt index 3c12415..161367e 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/ControlGrpcStubsManager.kt @@ -1,6 +1,5 @@ package software.momento.kotlin.sdk.internal -import grpc.cache_client.ScsGrpcKt import grpc.control_client.ScsControlGrpcKt import io.grpc.ClientInterceptor import io.grpc.ManagedChannel @@ -18,6 +17,7 @@ internal class ControlGrpcStubsManager(credentialProvider: CredentialProvider) : channel = setupConnection(credentialProvider) futureStub = ScsControlGrpcKt.ScsControlCoroutineStub(channel) } + val stub: ScsControlGrpcKt.ScsControlCoroutineStub /** * Returns a stub with appropriate deadlines. @@ -44,7 +44,7 @@ internal class ControlGrpcStubsManager(credentialProvider: CredentialProvider) : channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey, "cache")) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt index b16122e..1f6edd9 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/DataGrpcStubsManager.kt @@ -9,9 +9,9 @@ import software.momento.kotlin.sdk.config.GrpcConfiguration import java.io.Closeable import java.util.concurrent.TimeUnit import kotlin.time.Duration -import kotlin.time.Duration.Companion.minutes -internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, configuration: GrpcConfiguration) : Closeable { +internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, configuration: GrpcConfiguration) : + Closeable { private val deadline: Duration private val channel: ManagedChannel private val futureStub: ScsGrpcKt.ScsCoroutineStub @@ -21,6 +21,7 @@ internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, conf channel = setupConnection(credentialProvider) futureStub = ScsGrpcKt.ScsCoroutineStub(channel) } + val stub: ScsGrpcKt.ScsCoroutineStub /** * Returns a stub with appropriate deadlines. @@ -46,7 +47,7 @@ internal class DataGrpcStubsManager(credentialProvider: CredentialProvider, conf channelBuilder.useTransportSecurity() channelBuilder.disableRetry() val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey, "cache")) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt index 5b0c21a..a2ac5e3 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/TopicGrpcStubsManager.kt @@ -59,7 +59,7 @@ internal class TopicGrpcStubsManager(credentialProvider: CredentialProvider) : C channelBuilder.keepAliveTimeout(5, TimeUnit.SECONDS) channelBuilder.keepAliveWithoutCalls(true) val clientInterceptors: MutableList = ArrayList() - clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey)) + clientInterceptors.add(UserHeaderInterceptor(credentialProvider.apiKey, "topic")) channelBuilder.intercept(clientInterceptors) return channelBuilder.build() } diff --git a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt index 3e64391..72acfd9 100644 --- a/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt +++ b/src/jvmMain/kotlin/software/momento/kotlin/sdk/internal/UserHeaderInterceptor.jvm.kt @@ -1,11 +1,6 @@ package software.momento.kotlin.sdk.internal internal actual class PlatformInfo { - internal actual val sdkVersion: String - get() { - val version = this.javaClass.getPackage()?.implementationVersion ?: "unknown" - return "kotlin-jvm:$version" - } internal actual val runtimeVersion: String get() { val javaVendor = System.getProperty("java.vendor") @@ -13,4 +8,9 @@ internal actual class PlatformInfo { val kotlinVersion = KotlinVersion.CURRENT return "$javaVendor:$javaVersion, Kotlin $kotlinVersion" } + + internal actual fun getSdkVersion(clientType: String): String { + val version = this.javaClass.getPackage()?.implementationVersion ?: "unknown" + return "kotlin-jvm:$clientType:$version" + } } From 10a857eb91f654c59f73a7596aafd727e2ba51c6 Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Wed, 10 Jul 2024 12:02:48 -0700 Subject: [PATCH 4/6] Update to the latest Momento SDK Use ubuntu 22.04 instead of ubuntu latest for running the android tests because the emulator runner has issues on 24.04 https://github.com/ReactiveCircus/android-emulator-runner/issues/400 Add some improved test failure messages. --- .github/workflows/ci.yml | 3 +-- build.gradle.kts | 8 ++++---- .../momento/kotlin/sdk/CacheClientScalarTest.kt | 16 ++++++++-------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f404696..a4db87e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,8 +45,7 @@ jobs: arguments: clean build android: - # The Android emulator only has hardware acceleration on macOS. - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: api-level: [ 23 ] diff --git a/build.gradle.kts b/build.gradle.kts index 6c4d9ff..d3a3265 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -63,7 +63,7 @@ kotlin { val jvmMain by getting { dependencies { implementation(kotlin("stdlib-jdk8")) - implementation("software.momento.kotlin:client-protos-jvm:0.100.0") + implementation("software.momento.kotlin:client-protos-jvm:0.114.0") runtimeOnly("io.grpc:grpc-netty:1.57.2") } } @@ -74,7 +74,7 @@ kotlin { } val androidMain by getting { dependencies { - implementation("software.momento.kotlin:client-protos-android:0.100.0") + implementation("software.momento.kotlin:client-protos-android:0.114.0") runtimeOnly("io.grpc:grpc-okhttp:1.57.2") } } @@ -88,8 +88,8 @@ kotlin { dependencies { implementation(kotlin("test-junit")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") - implementation("androidx.test.ext:junit:1.1.5") - implementation("androidx.test.espresso:espresso-core:3.5.1") + implementation("androidx.test.ext:junit:1.2.1") + implementation("androidx.test.espresso:espresso-core:3.6.1") } } } diff --git a/src/androidInstrumentedTest/kotlin/software/momento/kotlin/sdk/CacheClientScalarTest.kt b/src/androidInstrumentedTest/kotlin/software/momento/kotlin/sdk/CacheClientScalarTest.kt index 3dacfb7..e1fb656 100644 --- a/src/androidInstrumentedTest/kotlin/software/momento/kotlin/sdk/CacheClientScalarTest.kt +++ b/src/androidInstrumentedTest/kotlin/software/momento/kotlin/sdk/CacheClientScalarTest.kt @@ -38,19 +38,19 @@ class CacheClientScalarTest: BaseAndroidTestClass() { val value = "cache-value" var getResponse = cacheClient.get(cacheName, key) - assert(getResponse is GetResponse.Miss) + assert(getResponse is GetResponse.Miss) { "expected Miss, got $getResponse" } val setResponse = cacheClient.set(cacheName, key, value) - assert(setResponse is SetResponse.Success) + assert(setResponse is SetResponse.Success) { "expected Success, got $setResponse" } getResponse = cacheClient.get(cacheName, key) assert((getResponse as GetResponse.Hit).value == value) val deleteResponse = cacheClient.delete(cacheName, key) - assert(deleteResponse is DeleteResponse.Success) + assert(deleteResponse is DeleteResponse.Success) { "expected Success, got $deleteResponse" } getResponse = cacheClient.get(cacheName, key) - assert(getResponse is GetResponse.Miss) + assert(getResponse is GetResponse.Miss) { "expected Miss, got $getResponse" } } @Test @@ -59,18 +59,18 @@ class CacheClientScalarTest: BaseAndroidTestClass() { val value = "cache-value".encodeToByteArray() var getResponse = cacheClient.get(cacheName, key) - assert(getResponse is GetResponse.Miss) + assert(getResponse is GetResponse.Miss) { "expected Miss, got $getResponse" } val setResponse = cacheClient.set(cacheName, key, value) - assert(setResponse is SetResponse.Success) + assert(setResponse is SetResponse.Success) { "expected Miss, got $setResponse" } getResponse = cacheClient.get(cacheName, key) assert((getResponse as GetResponse.Hit).valueByteArray.contentEquals(value)) val deleteResponse = cacheClient.delete(cacheName, key) - assert(deleteResponse is DeleteResponse.Success) + assert(deleteResponse is DeleteResponse.Success) { "expected Success, got $getResponse" } getResponse = cacheClient.get(cacheName, key) - assert(getResponse is GetResponse.Miss) + assert(getResponse is GetResponse.Miss) { "expected Miss, got $getResponse" } } } From 2f609144a4f1854a84528c7566417c77dd755f9c Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Wed, 10 Jul 2024 13:25:53 -0700 Subject: [PATCH 5/6] Run android tests with api level 26 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4db87e..4b85a9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,10 +45,10 @@ jobs: arguments: clean build android: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: - api-level: [ 23 ] + api-level: [ 26 ] env: TEST_API_KEY: ${{ secrets.ALPHA_TEST_AUTH_TOKEN }} TEST_CACHE_NAME: kotlin-integration-test-android-ci-${{ github.sha }} From cbcd4978400d34901c3d3c11b1b7d91f58170063 Mon Sep 17 00:00:00 2001 From: Nate Anderson Date: Wed, 10 Jul 2024 14:56:05 -0700 Subject: [PATCH 6/6] Add a comment explaining the KVM section in ci.yml --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b85a9f..d25feea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,8 @@ jobs: with: arguments: clean build -x jvmTest -x testDebugUnitTest -x testReleaseUnitTest + # Required for hardware accelerated Android emulation on linux + # See https://github.com/ReactiveCircus/android-emulator-runner - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules