diff --git a/.changes/0833e425-f3e6-4bea-a4ce-c2b73f7f39b6.json b/.changes/0833e425-f3e6-4bea-a4ce-c2b73f7f39b6.json new file mode 100644 index 00000000000..4acb26b7b54 --- /dev/null +++ b/.changes/0833e425-f3e6-4bea-a4ce-c2b73f7f39b6.json @@ -0,0 +1,6 @@ +{ + "id": "0833e425-f3e6-4bea-a4ce-c2b73f7f39b6", + "type": "misc", + "description": "⚠️ **IMPORTANT**: Upgrade to Kotlin 2.1.0", + "requiresMinorVersionBump": true +} \ No newline at end of file diff --git a/aws-runtime/aws-http/api/aws-http.api b/aws-runtime/aws-http/api/aws-http.api index d677ed9a9dc..2c157dc2266 100644 --- a/aws-runtime/aws-http/api/aws-http.api +++ b/aws-runtime/aws-http/api/aws-http.api @@ -140,16 +140,6 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/AddUserAgentMetadata public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V } -public final class aws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { - public static final field DDB_MAPPER Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; - public static final field S3_EXPRESS_BUCKET Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public fun getIdentifier ()Ljava/lang/String; - public fun toString ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; - public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; -} - public final class aws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { public static final field INSTANCE Laws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor; public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -197,9 +187,11 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/UnsupportedSigningAl } public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public static final field DDB_MAPPER Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; public static final field S3_EXPRESS_BUCKET Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; public static fun getEntries ()Lkotlin/enums/EnumEntries; public fun getIdentifier ()Ljava/lang/String; + public fun toString ()Ljava/lang/String; public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; } diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt deleted file mode 100644 index 5115ddf47cc..00000000000 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.runtime.http.interceptors - -import aws.sdk.kotlin.runtime.http.BUSINESS_METRICS_MAX_LENGTH -import aws.sdk.kotlin.runtime.http.middleware.USER_AGENT -import aws.smithy.kotlin.runtime.InternalApi -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics -import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext -import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor -import aws.smithy.kotlin.runtime.http.request.HttpRequest -import aws.smithy.kotlin.runtime.http.request.toBuilder -import aws.smithy.kotlin.runtime.telemetry.logging.Logger -import aws.smithy.kotlin.runtime.telemetry.logging.logger -import kotlin.coroutines.coroutineContext - -/** - * Appends business metrics to the `User-Agent` header. - */ -public class BusinessMetricsInterceptor : HttpInterceptor { - override suspend fun modifyBeforeTransmit(context: ProtocolRequestInterceptorContext): HttpRequest { - val logger = coroutineContext.logger() - - context.executionContext.getOrNull(BusinessMetrics)?.let { metrics -> - val metricsString = formatMetrics(metrics, logger) - val currentUserAgentHeader = context.protocolRequest.headers[USER_AGENT] - val modifiedRequest = context.protocolRequest.toBuilder() - - modifiedRequest.headers[USER_AGENT] = currentUserAgentHeader + metricsString - - return modifiedRequest.build() - } - return context.protocolRequest - } -} - -/** - * Makes sure the metrics do not exceed the maximum size and truncates them if so. - * Makes sure that metric identifiers are not > 2 chars in length. Skips them if so. - */ -private fun formatMetrics(metrics: MutableSet, logger: Logger): String { - val allowedMetrics = metrics.filter { - if (it.identifier.length > 2) { - logger.warn { - "Business metric '${it.identifier}' will be skipped due to length being > 2. " + - "This is likely a bug. Please raise an issue at https://github.com/awslabs/aws-sdk-kotlin/issues/new/choose" - } - false - } else { - true - } - } - if (allowedMetrics.isEmpty()) return "" - val metricsString = allowedMetrics.joinToString(",", "m/") { it.identifier } - val metricsByteArray = metricsString.encodeToByteArray() - - if (metricsByteArray.size <= BUSINESS_METRICS_MAX_LENGTH) return metricsString - - val lastCommaIndex = metricsByteArray - .sliceArray(0 until 1024) - .indexOfLast { it == ','.code.toByte() } - .takeIf { it != -1 } - - lastCommaIndex?.let { - return metricsByteArray.decodeToString( - 0, - lastCommaIndex, - true, - ) - } - - throw IllegalStateException("Business metrics are incorrectly formatted: $metricsString") -} - -/** - * AWS SDK specific business metrics - */ -@InternalApi -public enum class AwsBusinessMetric(public override val identifier: String) : BusinessMetric { - S3_EXPRESS_BUCKET("J"), - DDB_MAPPER("d"), - ; - - override fun toString(): String = identifier -} diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt index cf3d8586b3e..56f88433456 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt @@ -8,13 +8,26 @@ import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.MutableAttributes import aws.smithy.kotlin.runtime.collections.toMutableAttributes +import aws.smithy.kotlin.runtime.telemetry.logging.Logger /** * Makes sure the metrics do not exceed the maximum size and truncates them if so. + * Makes sure that metric identifiers are not > 2 chars in length. Skips them if so. */ -internal fun formatMetrics(metrics: MutableSet): String { - if (metrics.isEmpty()) return "" - val metricsString = metrics.joinToString(",", "m/") { it.identifier } +internal fun formatMetrics(metrics: MutableSet, logger: Logger): String { + val allowedMetrics = metrics.filter { + if (it.identifier.length > 2) { + logger.warn { + "Business metric '${it.identifier}' will be skipped due to length being > 2. " + + "This is likely a bug. Please raise an issue at https://github.com/awslabs/aws-sdk-kotlin/issues/new/choose" + } + false + } else { + true + } + } + if (allowedMetrics.isEmpty()) return "" + val metricsString = allowedMetrics.joinToString(",", "m/") { it.identifier } val metricsByteArray = metricsString.encodeToByteArray() if (metricsByteArray.size <= BUSINESS_METRICS_MAX_LENGTH) return metricsString @@ -41,6 +54,7 @@ internal fun formatMetrics(metrics: MutableSet): String { @InternalApi public enum class AwsBusinessMetric(public override val identifier: String) : BusinessMetric { S3_EXPRESS_BUCKET("J"), + DDB_MAPPER("d"), ; @InternalApi @@ -64,6 +78,8 @@ public enum class AwsBusinessMetric(public override val identifier: String) : Bu CREDENTIALS_HTTP("z"), CREDENTIALS_IMDS("0"), } + + override fun toString(): String = identifier } /** diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt index 43bed2aecf1..3e92416afe7 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt @@ -10,14 +10,18 @@ import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor import aws.smithy.kotlin.runtime.http.request.HttpRequest import aws.smithy.kotlin.runtime.http.request.toBuilder +import aws.smithy.kotlin.runtime.telemetry.logging.logger +import kotlin.coroutines.coroutineContext /** * Appends business metrics to the `User-Agent` header. */ public class BusinessMetricsInterceptor : HttpInterceptor { override suspend fun modifyBeforeTransmit(context: ProtocolRequestInterceptorContext): HttpRequest { + val logger = coroutineContext.logger() + context.executionContext.getOrNull(BusinessMetrics)?.let { metrics -> - val metricsString = formatMetrics(metrics) + val metricsString = formatMetrics(metrics, logger) val currentUserAgentHeader = context.protocolRequest.headers[USER_AGENT] val modifiedRequest = context.protocolRequest.toBuilder() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b0713cf239f..2b51d27817e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,15 @@ [versions] -kotlin-version = "2.0.21" -ksp-version = "2.0.21-1.0.25" # Keep in sync with kotlin-version +kotlin-version = "2.1.0" +ksp-version = "2.1.0-1.0.29" # Keep in sync with kotlin-version dokka-version = "1.9.10" -aws-kotlin-repo-tools-version = "0.4.14" +aws-kotlin-repo-tools-version = "0.4.17" # libs coroutines-version = "1.9.0" atomicfu-version = "0.25.0" +binary-compatibility-validator-version = "0.16.3" # smithy-kotlin codegen and runtime are versioned separately smithy-kotlin-runtime-version = "1.3.30" @@ -140,7 +141,7 @@ dokka = { id = "org.jetbrains.dokka", version.ref = "dokka-version"} kotlin-jvm = {id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-version" } kotlin-multiplatform = {id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin-version" } kotlinx-benchmark = { id = "org.jetbrains.kotlinx.benchmark", version.ref = "kotlinx-benchmark-version" } -kotlinx-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.13.2" } +kotlinx-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator-version" } kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-version"} ksp = { id = "com.google.devtools.ksp", version.ref = "ksp-version" } aws-kotlin-repo-tools-kmp = { id = "aws.sdk.kotlin.gradle.kmp", version.ref = "aws-kotlin-repo-tools-version" }