Skip to content

Commit 31dca75

Browse files
committed
Merge branch 'main' of github.com:awslabs/aws-sdk-kotlin into v1.4
2 parents 2a824b8 + ecab1a0 commit 31dca75

File tree

86 files changed

+86264
-8905
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+86264
-8905
lines changed

CHANGELOG.md

Lines changed: 166 additions & 0 deletions
Large diffs are not rendered by default.

aws-runtime/aws-http/api/aws-http.api

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/AddUserAgentMetadata
140140
public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V
141141
}
142142

143+
public final class aws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric {
144+
public static final field DDB_MAPPER Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric;
145+
public static final field S3_EXPRESS_BUCKET Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric;
146+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
147+
public fun getIdentifier ()Ljava/lang/String;
148+
public fun toString ()Ljava/lang/String;
149+
public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric;
150+
public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric;
151+
}
152+
143153
public final class aws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor : aws/smithy/kotlin/runtime/client/Interceptor {
144154
public static final field INSTANCE Laws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor;
145155
public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.sdk.kotlin.runtime.http.interceptors
6+
7+
import aws.sdk.kotlin.runtime.http.BUSINESS_METRICS_MAX_LENGTH
8+
import aws.sdk.kotlin.runtime.http.middleware.USER_AGENT
9+
import aws.smithy.kotlin.runtime.InternalApi
10+
import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric
11+
import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics
12+
import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext
13+
import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor
14+
import aws.smithy.kotlin.runtime.http.request.HttpRequest
15+
import aws.smithy.kotlin.runtime.http.request.toBuilder
16+
import aws.smithy.kotlin.runtime.telemetry.logging.Logger
17+
import aws.smithy.kotlin.runtime.telemetry.logging.logger
18+
import kotlin.coroutines.coroutineContext
19+
20+
/**
21+
* Appends business metrics to the `User-Agent` header.
22+
*/
23+
public class BusinessMetricsInterceptor : HttpInterceptor {
24+
override suspend fun modifyBeforeTransmit(context: ProtocolRequestInterceptorContext<Any, HttpRequest>): HttpRequest {
25+
val logger = coroutineContext.logger<BusinessMetricsInterceptor>()
26+
27+
context.executionContext.getOrNull(BusinessMetrics)?.let { metrics ->
28+
val metricsString = formatMetrics(metrics, logger)
29+
val currentUserAgentHeader = context.protocolRequest.headers[USER_AGENT]
30+
val modifiedRequest = context.protocolRequest.toBuilder()
31+
32+
modifiedRequest.headers[USER_AGENT] = currentUserAgentHeader + metricsString
33+
34+
return modifiedRequest.build()
35+
}
36+
return context.protocolRequest
37+
}
38+
}
39+
40+
/**
41+
* Makes sure the metrics do not exceed the maximum size and truncates them if so.
42+
* Makes sure that metric identifiers are not > 2 chars in length. Skips them if so.
43+
*/
44+
private fun formatMetrics(metrics: MutableSet<BusinessMetric>, logger: Logger): String {
45+
val allowedMetrics = metrics.filter {
46+
if (it.identifier.length > 2) {
47+
logger.warn {
48+
"Business metric '${it.identifier}' will be skipped due to length being > 2. " +
49+
"This is likely a bug. Please raise an issue at https://github.com/awslabs/aws-sdk-kotlin/issues/new/choose"
50+
}
51+
false
52+
} else {
53+
true
54+
}
55+
}
56+
if (allowedMetrics.isEmpty()) return ""
57+
val metricsString = allowedMetrics.joinToString(",", "m/") { it.identifier }
58+
val metricsByteArray = metricsString.encodeToByteArray()
59+
60+
if (metricsByteArray.size <= BUSINESS_METRICS_MAX_LENGTH) return metricsString
61+
62+
val lastCommaIndex = metricsByteArray
63+
.sliceArray(0 until 1024)
64+
.indexOfLast { it == ','.code.toByte() }
65+
.takeIf { it != -1 }
66+
67+
lastCommaIndex?.let {
68+
return metricsByteArray.decodeToString(
69+
0,
70+
lastCommaIndex,
71+
true,
72+
)
73+
}
74+
75+
throw IllegalStateException("Business metrics are incorrectly formatted: $metricsString")
76+
}
77+
78+
/**
79+
* AWS SDK specific business metrics
80+
*/
81+
@InternalApi
82+
public enum class AwsBusinessMetric(public override val identifier: String) : BusinessMetric {
83+
S3_EXPRESS_BUCKET("J"),
84+
DDB_MAPPER("d"),
85+
;
86+
87+
override fun toString(): String = identifier
88+
}

aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,12 @@ import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric
1212
import aws.smithy.kotlin.runtime.businessmetrics.SmithyBusinessMetric
1313
import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric
1414
import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext
15-
import aws.smithy.kotlin.runtime.collections.get
1615
import aws.smithy.kotlin.runtime.http.*
1716
import aws.smithy.kotlin.runtime.http.request.HttpRequest
1817
import aws.smithy.kotlin.runtime.net.url.Url
1918
import aws.smithy.kotlin.runtime.operation.ExecutionContext
2019
import kotlinx.coroutines.test.runTest
21-
import kotlin.test.Test
22-
import kotlin.test.assertFailsWith
23-
import kotlin.test.assertFalse
24-
import kotlin.test.assertTrue
20+
import kotlin.test.*
2521

2622
class BusinessMetricsInterceptorTest {
2723
@Test
@@ -34,6 +30,23 @@ class BusinessMetricsInterceptorTest {
3430
assertFalse(userAgentHeader.endsWith("m/"))
3531
}
3632

33+
@Test
34+
fun noValidBusinessMetrics() = runTest {
35+
val executionContext = ExecutionContext()
36+
37+
val invalidBusinessMetric = object : BusinessMetric {
38+
override val identifier: String = "All work and no play makes Jack a dull boy".repeat(1000)
39+
}
40+
41+
executionContext.emitBusinessMetric(invalidBusinessMetric)
42+
43+
val interceptor = BusinessMetricsInterceptor()
44+
val request = interceptor.modifyBeforeTransmit(interceptorContext(executionContext))
45+
val userAgentHeader = request.headers[USER_AGENT]!!
46+
47+
assertFalse(userAgentHeader.endsWith("m/"))
48+
}
49+
3750
@Test
3851
fun businessMetrics() = runTest {
3952
val executionContext = ExecutionContext()
@@ -68,48 +81,56 @@ class BusinessMetricsInterceptorTest {
6881
}
6982

7083
@Test
71-
fun truncateBusinessMetrics() = runTest {
84+
fun businessMetricsMaxLength() = runTest {
7285
val executionContext = ExecutionContext()
7386

74-
for (i in 0..1024) {
87+
for (i in 0..BUSINESS_METRICS_MAX_LENGTH) {
7588
executionContext.emitBusinessMetric(
7689
object : BusinessMetric {
7790
override val identifier: String = i.toString()
7891
},
7992
)
8093
}
8194

82-
val rawMetrics = executionContext[aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics]
83-
val rawMetricsString = rawMetrics.joinToString(",", "m/")
84-
val rawMetricsByteArray = rawMetricsString.encodeToByteArray()
85-
86-
assertTrue(rawMetricsByteArray.size >= BUSINESS_METRICS_MAX_LENGTH)
87-
8895
val interceptor = BusinessMetricsInterceptor()
8996
val request = interceptor.modifyBeforeTransmit(interceptorContext(executionContext))
9097
val userAgentHeader = request.headers[USER_AGENT]!!
91-
val truncatedMetrics = "m/" + userAgentHeader.substringAfter("m/")
98+
val metrics = "m/" + userAgentHeader.substringAfter("m/")
9299

93-
assertTrue(truncatedMetrics.encodeToByteArray().size <= BUSINESS_METRICS_MAX_LENGTH)
94-
assertFalse(truncatedMetrics.endsWith(","))
100+
assertTrue(metrics.encodeToByteArray().size <= BUSINESS_METRICS_MAX_LENGTH)
101+
assertFalse(metrics.endsWith(","))
95102
}
96103

97104
@Test
98-
fun malformedBusinessMetrics() = runTest {
105+
fun invalidBusinessMetric() = runTest {
99106
val executionContext = ExecutionContext()
100-
val reallyLongMetric = "All work and no play makes Jack a dull boy".repeat(1000)
101107

102-
executionContext.attributes.emitBusinessMetric(
103-
object : BusinessMetric {
104-
override val identifier: String = reallyLongMetric
105-
},
106-
)
108+
val validMetric = AwsBusinessMetric.S3_EXPRESS_BUCKET
109+
val invalidMetric = object : BusinessMetric {
110+
override val identifier: String = "All work and no play makes Jack a dull boy".repeat(1000)
111+
}
112+
113+
executionContext.attributes.emitBusinessMetric(validMetric)
114+
executionContext.attributes.emitBusinessMetric(invalidMetric)
107115

108116
val interceptor = BusinessMetricsInterceptor()
117+
val request = interceptor.modifyBeforeTransmit(interceptorContext(executionContext))
118+
val userAgentHeader = request.headers[USER_AGENT]!!
109119

110-
assertFailsWith<IllegalStateException>("Business metrics are incorrectly formatted:") {
111-
interceptor.modifyBeforeTransmit(interceptorContext(executionContext))
112-
}
120+
assertTrue(
121+
userAgentHeader.contains(validMetric.identifier),
122+
)
123+
assertFalse(
124+
userAgentHeader.contains(invalidMetric.identifier),
125+
)
126+
}
127+
128+
@Test
129+
fun businessMetricToString() {
130+
val businessMetricToString = AwsBusinessMetric.S3_EXPRESS_BUCKET.toString()
131+
val businessMetricIdentifier = AwsBusinessMetric.S3_EXPRESS_BUCKET.identifier
132+
133+
assertEquals(businessMetricIdentifier, businessMetricToString)
113134
}
114135
}
115136

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package aws.sdk.kotlin.codegen.smoketests
2+
3+
import software.amazon.smithy.kotlin.codegen.KotlinSettings
4+
import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration
5+
import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding
6+
import software.amazon.smithy.kotlin.codegen.rendering.smoketests.SmokeTestSectionIds.SmokeTestsFile
7+
import software.amazon.smithy.model.Model
8+
9+
/**
10+
* SDK ID's of services that are deny listed from smoke test code generation.
11+
*/
12+
val smokeTestDenyList = setOf(
13+
"S3Tables",
14+
)
15+
16+
/**
17+
* Will wipe the smoke test runner file for services that are deny listed.
18+
* Some services model smoke tests incorrectly and the code generated file will not compile.
19+
*/
20+
class SmokeTestsDenyListIntegration : KotlinIntegration {
21+
override fun enabledForService(model: Model, settings: KotlinSettings): Boolean =
22+
settings.sdkId in smokeTestDenyList
23+
24+
override val sectionWriters: List<SectionWriterBinding>
25+
get() = listOf(
26+
SectionWriterBinding(
27+
SmokeTestsFile,
28+
) { writer, _ ->
29+
writer.write("// Smoke tests for service are deny listed")
30+
},
31+
)
32+
}

codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ aws.sdk.kotlin.codegen.businessmetrics.BusinessMetricsInterceptorIntegration
4545
aws.sdk.kotlin.codegen.businessmetrics.CredentialsBusinessMetricsIntegration
4646
aws.sdk.kotlin.codegen.businessmetrics.EndpointBusinessMetricsIntegration
4747
aws.sdk.kotlin.codegen.smoketests.AwsSmokeTestsRunnerGeneratorIntegration
48+
aws.sdk.kotlin.codegen.smoketests.SmokeTestsDenyListIntegration
4849
aws.sdk.kotlin.codegen.smoketests.testing.SmokeTestSuccessHttpEngineIntegration
4950
aws.sdk.kotlin.codegen.smoketests.testing.SmokeTestFailHttpEngineIntegration
5051
aws.sdk.kotlin.codegen.customization.AwsQueryModeCustomization

0 commit comments

Comments
 (0)