From a38557ded6c05400887a2e64f29886790fac7683 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 23 May 2024 10:52:23 -0400 Subject: [PATCH 01/10] feat: business metrics --- .../kotlin/codegen/core/RuntimeTypes.kt | 8 +++ .../DefaultEndpointProviderGenerator.kt | 42 +++++++++++++- .../DefaultEndpointProviderTestGenerator.kt | 39 ++++++++++++- .../EndpointResolverAdapterGenerator.kt | 6 ++ .../RequestCompressionInterceptor.kt | 6 +- .../http/middleware/RetryMiddleware.kt | 8 +++ .../http/operation/SdkOperationExecution.kt | 12 ++-- runtime/runtime-core/api/runtime-core.api | 22 ++++++++ .../businessmetrics/BusinessMetricsUtils.kt | 55 +++++++++++++++++++ .../BusinessMetricsUtilsTest.kt | 32 +++++++++++ 10 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt create mode 100644 runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt index 6d09660511..189f5fe748 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt @@ -100,6 +100,13 @@ object RuntimeTypes { val ClientException = symbol("ClientException") val SdkDsl = symbol("SdkDsl") + object BusinessMetrics : RuntimeTypePackage(KotlinDependency.CORE, "businessmetrics") { + val accountIdBasedEndPoint = symbol("accountIdBasedEndPoint") + val serviceEndpointOverride = symbol("serviceEndpointOverride") + val EmitBusinessMetrics = symbol("emitBusinessMetric") + val BusinessMetrics = symbol("BusinessMetrics") + } + object Collections : RuntimeTypePackage(KotlinDependency.CORE, "collections") { val Attributes = symbol("Attributes") val attributesOf = symbol("attributesOf") @@ -110,6 +117,7 @@ object RuntimeTypes { val putIfAbsent = symbol("putIfAbsent") val putIfAbsentNotNull = symbol("putIfAbsentNotNull") val ReadThroughCache = symbol("ReadThroughCache") + val toMutableAttributes = symbol("toMutableAttributes") } object Content : RuntimeTypePackage(KotlinDependency.CORE, "content") { diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt index f7bae24857..9e0e87ed9b 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt @@ -52,6 +52,21 @@ fun interface ExpressionRenderer { fun renderExpression(expr: Expression) } +/** + * Will be toggled to true if it is determined an endpoint is account ID based then to false again + */ +private var hasAccountIdBasedEndpoint = false + +/** + * Will be toggled to true if determined an endpoint comes from a service endpoint override then to false again + */ +private var hasServiceEndpointOverride = false + +/** + * Will be toggled to true when rendering an endpoint URL then to false again + */ +private var renderingEndpointUrl = false + /** * Renders the default endpoint provider based on the provided rule set. */ @@ -170,9 +185,13 @@ class DefaultEndpointProviderGenerator( withConditions(rule.conditions) { writer.withBlock("return #T(", ")", RuntimeTypes.SmithyClient.Endpoints.Endpoint) { writeInline("#T.parse(", RuntimeTypes.Core.Net.Url.Url) + renderingEndpointUrl = true renderExpression(rule.endpoint.url) + renderingEndpointUrl = false write("),") + val needAdditionalEndpointProperties = hasAccountIdBasedEndpoint || hasServiceEndpointOverride + if (rule.endpoint.headers.isNotEmpty()) { withBlock("headers = #T {", "},", RuntimeTypes.Http.Headers) { rule.endpoint.headers.entries.forEach { (k, v) -> @@ -185,7 +204,7 @@ class DefaultEndpointProviderGenerator( } } - if (rule.endpoint.properties.isNotEmpty()) { + if (rule.endpoint.properties.isNotEmpty() || needAdditionalEndpointProperties) { withBlock("attributes = #T {", "},", RuntimeTypes.Core.Collections.attributesOf) { rule.endpoint.properties.entries.forEach { (k, v) -> val kStr = k.toString() @@ -204,6 +223,15 @@ class DefaultEndpointProviderGenerator( renderExpression(v) ensureNewline() } + + if (hasAccountIdBasedEndpoint) { + writer.write("#T to true", RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint) + hasAccountIdBasedEndpoint = false + } + if (hasServiceEndpointOverride) { + writer.write("#T to true", RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride) + hasServiceEndpointOverride = false + } } } } @@ -235,10 +263,18 @@ class ExpressionGenerator( } override fun visitRef(reference: Reference) { - if (isParamRef(reference)) { + val referenceName = reference.name.defaultName() + val isParamReference = isParamRef(reference) + + if (isParamReference) { writer.writeInline("params.") } - writer.writeInline(reference.name.defaultName()) + writer.writeInline(referenceName) + + if (renderingEndpointUrl) { + if (isParamReference && referenceName == "accountId") hasAccountIdBasedEndpoint = true + if (isParamReference && referenceName == "endpoint") hasServiceEndpointOverride = true + } } override fun visitGetAttr(getAttr: GetAttr) { diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt index d9573ebb53..8696ca6fe1 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt @@ -61,6 +61,43 @@ class DefaultEndpointProviderTestGenerator( fun render() { writer.addImport("*", namespace = "kotlin.test") writer.withBlock("public class #L {", "}", CLASS_NAME) { + writer.write("") + writer.withBlock( + "private fun expectEqualEndpoints(expected: #T, actual: #T) {", + "}", + RuntimeTypes.SmithyClient.Endpoints.Endpoint, + RuntimeTypes.SmithyClient.Endpoints.Endpoint, + ) { + // Make exceptions ONLY for business metrics attributes + writer.withBlock( + "if (actual.attributes.contains(#T) || actual.attributes.contains(#T)) {", + "} else { assertEquals(expected, actual) }", + RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, + RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint, + ) { + writer.write( + "val updatedAttributes = expected.attributes.#T()", + RuntimeTypes.Core.Collections.toMutableAttributes, + ) + writer.write( + "if (actual.attributes.contains(#T)) updatedAttributes[#T] = true", + RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, + RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, + ) + writer.write( + "if (actual.attributes.contains(#T)) updatedAttributes[#T] = true", + RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint, + RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint, + ) + writer.write( + "val newExpected = #T(expected.uri, expected.headers, updatedAttributes)", + RuntimeTypes.SmithyClient.Endpoints.Endpoint, + ) + writer.write("assertEquals(newExpected, actual)") + } + } + writer.write("") + cases.forEachIndexed { index, it -> renderTestCase(index, it) write("") @@ -148,6 +185,6 @@ class DefaultEndpointProviderTestGenerator( } writer.write("val actual = #T().resolveEndpoint(params)", providerSymbol) - writer.write("assertEquals(expected, actual)") + writer.write("expectEqualEndpoints(expected, actual)") } } diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/EndpointResolverAdapterGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/EndpointResolverAdapterGenerator.kt index 394a4310fb..4b6e09da8a 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/EndpointResolverAdapterGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/EndpointResolverAdapterGenerator.kt @@ -8,6 +8,7 @@ import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.kotlin.codegen.KotlinSettings import software.amazon.smithy.kotlin.codegen.core.* +import software.amazon.smithy.kotlin.codegen.integration.SectionId import software.amazon.smithy.kotlin.codegen.model.* import software.amazon.smithy.kotlin.codegen.model.knowledge.EndpointParameterIndex import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator @@ -17,6 +18,8 @@ import software.amazon.smithy.rulesengine.language.syntax.parameters.ParameterTy import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait import software.amazon.smithy.utils.StringUtils +object EndpointBusinessMetrics : SectionId + /** * Generates resolver adapter for going from generic HTTP operation endpoint resolver to the generated * type specific provider generated based on the rules. @@ -153,6 +156,9 @@ class EndpointResolverAdapterGenerator( ) { write("val params = resolveEndpointParams(config, request)") write("val endpoint = config.endpointProvider.resolveEndpoint(params)") + + declareSection(EndpointBusinessMetrics) + renderPostResolution() write("return endpoint") } diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt index 193e24765a..4c037946e0 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt @@ -6,6 +6,8 @@ package aws.smithy.kotlin.runtime.http.interceptors import aws.smithy.kotlin.runtime.InternalApi +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext import aws.smithy.kotlin.runtime.compression.CompressionAlgorithm import aws.smithy.kotlin.runtime.http.HttpBody @@ -42,7 +44,9 @@ public class RequestCompressionInterceptor( } return if (algorithm != null && (context.protocolRequest.body.isStreaming || payloadSizeBytes?.let { it >= compressionThresholdBytes } == true)) { - algorithm.compressRequest(context.protocolRequest) + val request = algorithm.compressRequest(context.protocolRequest) + context.executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) + request } else { val logger = coroutineContext.logger() val skipCause = if (algorithm == null) "no modeled compression algorithms are supported by the client" else "request size threshold ($compressionThresholdBytes) was not met" diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt index fd9d82abec..726f80a8d7 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt @@ -5,6 +5,8 @@ package aws.smithy.kotlin.runtime.http.middleware +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.http.interceptors.InterceptorExecutor import aws.smithy.kotlin.runtime.http.operation.* import aws.smithy.kotlin.runtime.http.operation.deepCopy @@ -13,7 +15,9 @@ import aws.smithy.kotlin.runtime.http.request.immutableView import aws.smithy.kotlin.runtime.http.request.toBuilder import aws.smithy.kotlin.runtime.io.Handler import aws.smithy.kotlin.runtime.io.middleware.Middleware +import aws.smithy.kotlin.runtime.retries.AdaptiveRetryStrategy import aws.smithy.kotlin.runtime.retries.RetryStrategy +import aws.smithy.kotlin.runtime.retries.StandardRetryStrategy import aws.smithy.kotlin.runtime.retries.policy.RetryDirective import aws.smithy.kotlin.runtime.retries.policy.RetryPolicy import aws.smithy.kotlin.runtime.retries.toResult @@ -47,6 +51,10 @@ internal class RetryMiddleware( withSpan, _>("Attempt-$attempt") { if (attempt > 1) { coroutineContext.debug> { "retrying request, attempt $attempt" } + when (strategy::class) { + StandardRetryStrategy::class -> modified.context.emitBusinessMetric(BusinessMetrics.RETRY_MODE_STANDARD) + AdaptiveRetryStrategy::class -> modified.context.emitBusinessMetric(BusinessMetrics.RETRY_MODE_ADAPTIVE) + } } // Deep copy the request because later middlewares (e.g., signing) mutate it diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt index ee17631171..5bc816062c 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt @@ -6,19 +6,18 @@ package aws.smithy.kotlin.runtime.http.operation import aws.smithy.kotlin.runtime.InternalApi +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.LogMode import aws.smithy.kotlin.runtime.client.endpoints.authOptions import aws.smithy.kotlin.runtime.client.logMode import aws.smithy.kotlin.runtime.collections.attributesOf import aws.smithy.kotlin.runtime.collections.emptyAttributes import aws.smithy.kotlin.runtime.collections.merge -import aws.smithy.kotlin.runtime.http.HttpCall -import aws.smithy.kotlin.runtime.http.HttpHandler +import aws.smithy.kotlin.runtime.http.* import aws.smithy.kotlin.runtime.http.auth.SignHttpRequest -import aws.smithy.kotlin.runtime.http.complete import aws.smithy.kotlin.runtime.http.interceptors.InterceptorExecutor import aws.smithy.kotlin.runtime.http.middleware.RetryMiddleware -import aws.smithy.kotlin.runtime.http.readAll import aws.smithy.kotlin.runtime.http.request.HttpRequestBuilder import aws.smithy.kotlin.runtime.http.request.dumpRequest import aws.smithy.kotlin.runtime.http.request.immutableView @@ -313,6 +312,11 @@ internal class AuthHandler( authScheme.signer.sign(signingRequest) } + when (authScheme.schemeId.id) { + "aws.auth#sigv4s3express" -> request.context.emitBusinessMetric(BusinessMetrics.S3_EXPRESS_BUCKET) + "aws.auth#sigv4a" -> request.context.emitBusinessMetric(BusinessMetrics.SIGV4A_SIGNING) + } + interceptors.readAfterSigning(modified.subject.immutableView()) return inner.call(modified) } diff --git a/runtime/runtime-core/api/runtime-core.api b/runtime/runtime-core/api/runtime-core.api index a41a4aa47f..b49d85744d 100644 --- a/runtime/runtime-core/api/runtime-core.api +++ b/runtime/runtime-core/api/runtime-core.api @@ -77,6 +77,28 @@ public final class aws/smithy/kotlin/runtime/ServiceException$ErrorType : java/l public static fun values ()[Laws/smithy/kotlin/runtime/ServiceException$ErrorType; } +public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics : java/lang/Enum { + public static final field ACCOUNT_ID_BASED_ENDPOINT Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field GZIP_REQUEST_COMPRESSION Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field RETRY_MODE_ADAPTIVE Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field RETRY_MODE_LEGACY Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field RETRY_MODE_STANDARD Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field S3_EXPRESS_BUCKET Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field SERVICE_ENDPOINT_OVERRIDE Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static final field SIGV4A_SIGNING Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public final fun getIdentifier ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; + public static fun values ()[Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; +} + +public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsKt { + public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)V + public static final fun getAccountIdBasedEndPoint ()Laws/smithy/kotlin/runtime/collections/AttributeKey; + public static final fun getBusinessMetrics ()Laws/smithy/kotlin/runtime/collections/AttributeKey; + public static final fun getServiceEndpointOverride ()Laws/smithy/kotlin/runtime/collections/AttributeKey; +} + public final class aws/smithy/kotlin/runtime/collections/AttributeKey { public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; diff --git a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt new file mode 100644 index 0000000000..532a7b8f64 --- /dev/null +++ b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.smithy.kotlin.runtime.businessmetrics + +import aws.smithy.kotlin.runtime.InternalApi +import aws.smithy.kotlin.runtime.collections.AttributeKey +import aws.smithy.kotlin.runtime.collections.get +import aws.smithy.kotlin.runtime.operation.ExecutionContext + +/** + * Keeps track of all business metrics along an operations execution + */ +@InternalApi +public val businessMetrics: AttributeKey> = AttributeKey("aws.sdk.kotlin#BusinessMetrics") + +/** + * If an endpoint is account ID based + */ +@InternalApi +public val accountIdBasedEndPoint: AttributeKey = AttributeKey("aws.smithy.kotlin#AccountIdBasedEndpoint") + +/** + * If an endpoint is service endpoint override based + */ +@InternalApi +public val serviceEndpointOverride: AttributeKey = AttributeKey("aws.smithy.kotlin#ServiceEndpointOverride") + +/** + * Emit a business metric to the execution context attributes + */ +@InternalApi +public fun ExecutionContext.emitBusinessMetric(metric: BusinessMetrics) { + if (this.attributes.contains(businessMetrics)) { + this.attributes[businessMetrics].add(metric.identifier) + } else { + this.attributes[businessMetrics] = mutableSetOf(metric.identifier) + } +} + +/** + * All the valid business metrics along with their identifiers + */ +@InternalApi +public enum class BusinessMetrics(public val identifier: String) { + S3_EXPRESS_BUCKET("A"), + GZIP_REQUEST_COMPRESSION("B"), + RETRY_MODE_LEGACY("C"), + RETRY_MODE_STANDARD("D"), + RETRY_MODE_ADAPTIVE("E"), + SIGV4A_SIGNING("F"), + SERVICE_ENDPOINT_OVERRIDE("G"), + ACCOUNT_ID_BASED_ENDPOINT("H"), +} diff --git a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt new file mode 100644 index 0000000000..85454aa77f --- /dev/null +++ b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.smithy.kotlin.runtime.businessmetrics + +import aws.smithy.kotlin.runtime.collections.get +import aws.smithy.kotlin.runtime.operation.ExecutionContext +import kotlin.test.Test +import kotlin.test.assertTrue + +class BusinessMetricsUtilsTest { + @Test + fun emitBusinessMetric() { + val executionContext = ExecutionContext() + executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) + + assertTrue(executionContext.attributes.contains(businessMetrics)) + assertTrue(executionContext.attributes[businessMetrics].contains(BusinessMetrics.GZIP_REQUEST_COMPRESSION.identifier)) + } + + @Test + fun emitMultipleBusinessMetrics() { + val executionContext = ExecutionContext() + executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) + executionContext.emitBusinessMetric(BusinessMetrics.S3_EXPRESS_BUCKET) + + assertTrue(executionContext.attributes.contains(businessMetrics)) + assertTrue(executionContext.attributes[businessMetrics].contains(BusinessMetrics.GZIP_REQUEST_COMPRESSION.identifier)) + assertTrue(executionContext.attributes[businessMetrics].contains(BusinessMetrics.S3_EXPRESS_BUCKET.identifier)) + } +} From e50b458f2e1d36e8a858da6ae983304a6e49ec4c Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 24 May 2024 16:14:33 -0400 Subject: [PATCH 02/10] change capitalization of runtime type --- .../software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt index 189f5fe748..63eb7e9fdc 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt @@ -103,7 +103,7 @@ object RuntimeTypes { object BusinessMetrics : RuntimeTypePackage(KotlinDependency.CORE, "businessmetrics") { val accountIdBasedEndPoint = symbol("accountIdBasedEndPoint") val serviceEndpointOverride = symbol("serviceEndpointOverride") - val EmitBusinessMetrics = symbol("emitBusinessMetric") + val emitBusinessMetrics = symbol("emitBusinessMetric") val BusinessMetrics = symbol("BusinessMetrics") } From 5299bae3f24dc416ac4756eae6932491800fbe7c Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 6 Jun 2024 01:15:49 -0400 Subject: [PATCH 03/10] make sure to remove business metric if interceptor changes endpoint --- .../kotlin/codegen/core/RuntimeTypes.kt | 2 +- .../DefaultEndpointProviderGenerator.kt | 2 +- .../DefaultEndpointProviderTestGenerator.kt | 18 +++++------ .../http/interceptors/InterceptorExecutor.kt | 32 +++++++++++++++++-- .../RequestCompressionInterceptor.kt | 6 ++-- runtime/runtime-core/api/runtime-core.api | 4 ++- .../businessmetrics/BusinessMetricsUtils.kt | 21 ++++++++++-- 7 files changed, 65 insertions(+), 20 deletions(-) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt index 63eb7e9fdc..6946584752 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt @@ -101,7 +101,7 @@ object RuntimeTypes { val SdkDsl = symbol("SdkDsl") object BusinessMetrics : RuntimeTypePackage(KotlinDependency.CORE, "businessmetrics") { - val accountIdBasedEndPoint = symbol("accountIdBasedEndPoint") + val accountIdBasedEndPointAccountId = symbol("accountIdBasedEndPointAccountId") val serviceEndpointOverride = symbol("serviceEndpointOverride") val emitBusinessMetrics = symbol("emitBusinessMetric") val BusinessMetrics = symbol("BusinessMetrics") diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt index 9e0e87ed9b..3377a16220 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt @@ -225,7 +225,7 @@ class DefaultEndpointProviderGenerator( } if (hasAccountIdBasedEndpoint) { - writer.write("#T to true", RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint) + writer.write("#T to params.accountId", RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId) hasAccountIdBasedEndpoint = false } if (hasServiceEndpointOverride) { diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt index 8696ca6fe1..651b946064 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt @@ -68,32 +68,32 @@ class DefaultEndpointProviderTestGenerator( RuntimeTypes.SmithyClient.Endpoints.Endpoint, RuntimeTypes.SmithyClient.Endpoints.Endpoint, ) { - // Make exceptions ONLY for business metrics attributes + // Remove ONLY business metrics endpoint attributes writer.withBlock( "if (actual.attributes.contains(#T) || actual.attributes.contains(#T)) {", "} else { assertEquals(expected, actual) }", RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, - RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint, + RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId, ) { writer.write( - "val updatedAttributes = expected.attributes.#T()", + "val newActualAttributes = actual.attributes.#T()", RuntimeTypes.Core.Collections.toMutableAttributes, ) writer.write( - "if (actual.attributes.contains(#T)) updatedAttributes[#T] = true", + "if (actual.attributes.contains(#T)) newActualAttributes.remove(#T)", RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, ) writer.write( - "if (actual.attributes.contains(#T)) updatedAttributes[#T] = true", - RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint, - RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPoint, + "if (actual.attributes.contains(#T)) newActualAttributes.remove(#T)", + RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId, + RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId, ) writer.write( - "val newExpected = #T(expected.uri, expected.headers, updatedAttributes)", + "val newActual = #T(actual.uri, actual.headers, newActualAttributes)", RuntimeTypes.SmithyClient.Endpoints.Endpoint, ) - writer.write("assertEquals(newExpected, actual)") + writer.write("assertEquals(expected, newActual)") } } writer.write("") diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt index df1dc8841b..3e70bbf7e8 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt @@ -5,7 +5,9 @@ package aws.smithy.kotlin.runtime.http.interceptors +import aws.smithy.kotlin.runtime.businessmetrics.* import aws.smithy.kotlin.runtime.client.* +import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.http.HttpCall import aws.smithy.kotlin.runtime.http.operation.OperationTypeInfo import aws.smithy.kotlin.runtime.http.request.HttpRequest @@ -188,7 +190,9 @@ internal class InterceptorExecutor( suspend fun modifyBeforeRetryLoop(request: HttpRequest): HttpRequest = modifyHttpRequestHook(request) { interceptor, context -> - interceptor.modifyBeforeRetryLoop(context) + interceptor.modifyBeforeRetryLoop(context).also { modifiedRequest -> + updateBusinessMetrics(request, modifiedRequest, context) + } } fun readBeforeAttempt(request: HttpRequest): Result { @@ -203,7 +207,9 @@ internal class InterceptorExecutor( suspend fun modifyBeforeSigning(request: HttpRequest): HttpRequest = modifyHttpRequestHook(request) { interceptor, context -> - interceptor.modifyBeforeSigning(context) + interceptor.modifyBeforeSigning(context).also { modifiedRequest -> + updateBusinessMetrics(request, modifiedRequest, context) + } } fun readBeforeSigning(request: HttpRequest) = readHttpHook(request) { interceptor, context -> @@ -216,7 +222,9 @@ internal class InterceptorExecutor( suspend fun modifyBeforeTransmit(request: HttpRequest): HttpRequest = modifyHttpRequestHook(request) { interceptor, context -> - interceptor.modifyBeforeTransmit(context) + interceptor.modifyBeforeTransmit(context).also { modifiedRequest -> + updateBusinessMetrics(request, modifiedRequest, context) + } } fun readBeforeTransmit(request: HttpRequest) = readHttpHook(request) { interceptor, context -> @@ -328,4 +336,22 @@ internal class InterceptorExecutor( }, ) } + + private fun updateBusinessMetrics( + request: HttpRequest, + modifiedRequest: HttpRequest, + context: HttpProtocolRequestInterceptorContext, + ) { + if (modifiedRequest.url != request.url) { + if ( + context.executionContext.containsBusinessMetric(BusinessMetrics.ACCOUNT_ID_BASED_ENDPOINT) && + !modifiedRequest.url.toString().contains(context.executionContext.attributes[accountIdBasedEndPointAccountId]) + ) { + context.executionContext.removeBusinessMetric(BusinessMetrics.ACCOUNT_ID_BASED_ENDPOINT) + } + if (context.executionContext.containsBusinessMetric(BusinessMetrics.SERVICE_ENDPOINT_OVERRIDE)) { + context.executionContext.removeBusinessMetric(BusinessMetrics.SERVICE_ENDPOINT_OVERRIDE) + } + } + } } diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt index 4c037946e0..ea3aa89dc2 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt @@ -44,9 +44,9 @@ public class RequestCompressionInterceptor( } return if (algorithm != null && (context.protocolRequest.body.isStreaming || payloadSizeBytes?.let { it >= compressionThresholdBytes } == true)) { - val request = algorithm.compressRequest(context.protocolRequest) - context.executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) - request + algorithm.compressRequest(context.protocolRequest).also { + context.executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) + } } else { val logger = coroutineContext.logger() val skipCause = if (algorithm == null) "no modeled compression algorithms are supported by the client" else "request size threshold ($compressionThresholdBytes) was not met" diff --git a/runtime/runtime-core/api/runtime-core.api b/runtime/runtime-core/api/runtime-core.api index b49d85744d..d800803077 100644 --- a/runtime/runtime-core/api/runtime-core.api +++ b/runtime/runtime-core/api/runtime-core.api @@ -93,10 +93,12 @@ public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics : j } public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsKt { + public static final fun containsBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)Z public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)V - public static final fun getAccountIdBasedEndPoint ()Laws/smithy/kotlin/runtime/collections/AttributeKey; + public static final fun getAccountIdBasedEndPointAccountId ()Laws/smithy/kotlin/runtime/collections/AttributeKey; public static final fun getBusinessMetrics ()Laws/smithy/kotlin/runtime/collections/AttributeKey; public static final fun getServiceEndpointOverride ()Laws/smithy/kotlin/runtime/collections/AttributeKey; + public static final fun removeBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)V } public final class aws/smithy/kotlin/runtime/collections/AttributeKey { diff --git a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt index 532a7b8f64..12cde78056 100644 --- a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt +++ b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt @@ -16,10 +16,10 @@ import aws.smithy.kotlin.runtime.operation.ExecutionContext public val businessMetrics: AttributeKey> = AttributeKey("aws.sdk.kotlin#BusinessMetrics") /** - * If an endpoint is account ID based + * The account ID in an account ID based endpoint */ @InternalApi -public val accountIdBasedEndPoint: AttributeKey = AttributeKey("aws.smithy.kotlin#AccountIdBasedEndpoint") +public val accountIdBasedEndPointAccountId: AttributeKey = AttributeKey("aws.smithy.kotlin#AccountIdBasedEndpointAccountId") /** * If an endpoint is service endpoint override based @@ -39,6 +39,23 @@ public fun ExecutionContext.emitBusinessMetric(metric: BusinessMetrics) { } } +/** + * Removes a business metric from the execution context attributes + */ +@InternalApi +public fun ExecutionContext.removeBusinessMetric(metric: BusinessMetrics) { + if (this.attributes.contains(businessMetrics)) { + this.attributes[businessMetrics].remove(metric.identifier) + } +} + +/** + * Checks if a business metric exists in the execution context attributes + */ +@InternalApi +public fun ExecutionContext.containsBusinessMetric(metric: BusinessMetrics): Boolean = + (this.attributes.contains(businessMetrics)) && this.attributes[businessMetrics].contains(metric.identifier) + /** * All the valid business metrics along with their identifiers */ From ae588fbf4fadbd2904e556eb969ed907d5644671 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Tue, 11 Jun 2024 14:41:39 -0400 Subject: [PATCH 04/10] pr feedback --- .../kotlin/codegen/core/RuntimeTypes.kt | 6 +-- .../DefaultEndpointProviderGenerator.kt | 4 +- .../DefaultEndpointProviderTestGenerator.kt | 17 +++---- gradle.properties | 2 +- .../http/interceptors/InterceptorExecutor.kt | 10 ++-- .../RequestCompressionInterceptor.kt | 4 +- .../http/middleware/RetryMiddleware.kt | 11 ++-- .../http/operation/SdkOperationExecution.kt | 7 ++- runtime/runtime-core/api/runtime-core.api | 44 +++++++++------- .../businessmetrics/BusinessMetricsUtils.kt | 51 ++++++++++--------- .../BusinessMetricsUtilsTest.kt | 42 ++++++++++++--- 11 files changed, 118 insertions(+), 80 deletions(-) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt index 6946584752..957e6cd51b 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt @@ -101,10 +101,10 @@ object RuntimeTypes { val SdkDsl = symbol("SdkDsl") object BusinessMetrics : RuntimeTypePackage(KotlinDependency.CORE, "businessmetrics") { - val accountIdBasedEndPointAccountId = symbol("accountIdBasedEndPointAccountId") - val serviceEndpointOverride = symbol("serviceEndpointOverride") + val AccountIdBasedEndpointAccountId = symbol("AccountIdBasedEndpointAccountId") + val ServiceEndpointOverride = symbol("ServiceEndpointOverride") val emitBusinessMetrics = symbol("emitBusinessMetric") - val BusinessMetrics = symbol("BusinessMetrics") + val SmithyBusinessMetric = symbol("SmithyBusinessMetric") } object Collections : RuntimeTypePackage(KotlinDependency.CORE, "collections") { diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt index 3377a16220..08f7924bc3 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderGenerator.kt @@ -225,11 +225,11 @@ class DefaultEndpointProviderGenerator( } if (hasAccountIdBasedEndpoint) { - writer.write("#T to params.accountId", RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId) + writer.write("#T to params.accountId", RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId) hasAccountIdBasedEndpoint = false } if (hasServiceEndpointOverride) { - writer.write("#T to true", RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride) + writer.write("#T to true", RuntimeTypes.Core.BusinessMetrics.ServiceEndpointOverride) hasServiceEndpointOverride = false } } diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt index 651b946064..8f1b40feab 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt @@ -63,31 +63,28 @@ class DefaultEndpointProviderTestGenerator( writer.withBlock("public class #L {", "}", CLASS_NAME) { writer.write("") writer.withBlock( - "private fun expectEqualEndpoints(expected: #T, actual: #T) {", + "private fun expectEqualEndpoints(expected: #1T, actual: #1T) {", "}", RuntimeTypes.SmithyClient.Endpoints.Endpoint, - RuntimeTypes.SmithyClient.Endpoints.Endpoint, ) { // Remove ONLY business metrics endpoint attributes writer.withBlock( "if (actual.attributes.contains(#T) || actual.attributes.contains(#T)) {", "} else { assertEquals(expected, actual) }", - RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, - RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId, + RuntimeTypes.Core.BusinessMetrics.ServiceEndpointOverride, + RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId, ) { writer.write( "val newActualAttributes = actual.attributes.#T()", RuntimeTypes.Core.Collections.toMutableAttributes, ) writer.write( - "if (actual.attributes.contains(#T)) newActualAttributes.remove(#T)", - RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, - RuntimeTypes.Core.BusinessMetrics.serviceEndpointOverride, + "if (actual.attributes.contains(#1T)) newActualAttributes.remove(#1T)", + RuntimeTypes.Core.BusinessMetrics.ServiceEndpointOverride, ) writer.write( - "if (actual.attributes.contains(#T)) newActualAttributes.remove(#T)", - RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId, - RuntimeTypes.Core.BusinessMetrics.accountIdBasedEndPointAccountId, + "if (actual.attributes.contains(#1T)) newActualAttributes.remove(#1T)", + RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId, ) writer.write( "val newActual = #T(actual.uri, actual.headers, newActualAttributes)", diff --git a/gradle.properties b/gradle.properties index 2284d74b77..3d1a3a458e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ kotlinx.atomicfu.enableNativeIrTransformation=false org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=1G # SDK -sdkVersion=1.2.4-SNAPSHOT +sdkVersion=1.2.7-SNAPSHOT # codegen codegenVersion=0.32.4-SNAPSHOT \ No newline at end of file diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt index 3e70bbf7e8..9ccf5cf3ee 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/InterceptorExecutor.kt @@ -344,13 +344,13 @@ internal class InterceptorExecutor( ) { if (modifiedRequest.url != request.url) { if ( - context.executionContext.containsBusinessMetric(BusinessMetrics.ACCOUNT_ID_BASED_ENDPOINT) && - !modifiedRequest.url.toString().contains(context.executionContext.attributes[accountIdBasedEndPointAccountId]) + context.executionContext.containsBusinessMetric(SmithyBusinessMetric.ACCOUNT_ID_BASED_ENDPOINT) && + !modifiedRequest.url.toString().contains(context.executionContext.attributes[AccountIdBasedEndpointAccountId]) ) { - context.executionContext.removeBusinessMetric(BusinessMetrics.ACCOUNT_ID_BASED_ENDPOINT) + context.executionContext.removeBusinessMetric(SmithyBusinessMetric.ACCOUNT_ID_BASED_ENDPOINT) } - if (context.executionContext.containsBusinessMetric(BusinessMetrics.SERVICE_ENDPOINT_OVERRIDE)) { - context.executionContext.removeBusinessMetric(BusinessMetrics.SERVICE_ENDPOINT_OVERRIDE) + if (context.executionContext.containsBusinessMetric(SmithyBusinessMetric.SERVICE_ENDPOINT_OVERRIDE)) { + context.executionContext.removeBusinessMetric(SmithyBusinessMetric.SERVICE_ENDPOINT_OVERRIDE) } } } diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt index ea3aa89dc2..88da6c9b71 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/interceptors/RequestCompressionInterceptor.kt @@ -6,7 +6,7 @@ package aws.smithy.kotlin.runtime.http.interceptors import aws.smithy.kotlin.runtime.InternalApi -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.SmithyBusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext import aws.smithy.kotlin.runtime.compression.CompressionAlgorithm @@ -45,7 +45,7 @@ public class RequestCompressionInterceptor( return if (algorithm != null && (context.protocolRequest.body.isStreaming || payloadSizeBytes?.let { it >= compressionThresholdBytes } == true)) { algorithm.compressRequest(context.protocolRequest).also { - context.executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) + context.executionContext.emitBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) } } else { val logger = coroutineContext.logger() diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt index 726f80a8d7..7b8144c82e 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/middleware/RetryMiddleware.kt @@ -5,7 +5,7 @@ package aws.smithy.kotlin.runtime.http.middleware -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.SmithyBusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.http.interceptors.InterceptorExecutor import aws.smithy.kotlin.runtime.http.operation.* @@ -49,12 +49,13 @@ internal class RetryMiddleware( val outcome = strategy.retry(wrappedPolicy) { withSpan, _>("Attempt-$attempt") { + when (strategy::class) { + StandardRetryStrategy::class -> modified.context.emitBusinessMetric(SmithyBusinessMetric.RETRY_MODE_STANDARD) + AdaptiveRetryStrategy::class -> modified.context.emitBusinessMetric(SmithyBusinessMetric.RETRY_MODE_ADAPTIVE) + } + if (attempt > 1) { coroutineContext.debug> { "retrying request, attempt $attempt" } - when (strategy::class) { - StandardRetryStrategy::class -> modified.context.emitBusinessMetric(BusinessMetrics.RETRY_MODE_STANDARD) - AdaptiveRetryStrategy::class -> modified.context.emitBusinessMetric(BusinessMetrics.RETRY_MODE_ADAPTIVE) - } } // Deep copy the request because later middlewares (e.g., signing) mutate it diff --git a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt index 5bc816062c..0b4e2e0486 100644 --- a/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt +++ b/runtime/protocol/http-client/common/src/aws/smithy/kotlin/runtime/http/operation/SdkOperationExecution.kt @@ -6,7 +6,7 @@ package aws.smithy.kotlin.runtime.http.operation import aws.smithy.kotlin.runtime.InternalApi -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.SmithyBusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.LogMode import aws.smithy.kotlin.runtime.client.endpoints.authOptions @@ -312,9 +312,8 @@ internal class AuthHandler( authScheme.signer.sign(signingRequest) } - when (authScheme.schemeId.id) { - "aws.auth#sigv4s3express" -> request.context.emitBusinessMetric(BusinessMetrics.S3_EXPRESS_BUCKET) - "aws.auth#sigv4a" -> request.context.emitBusinessMetric(BusinessMetrics.SIGV4A_SIGNING) + if (authScheme.schemeId.id == "aws.auth#sigv4a") { + request.context.emitBusinessMetric(SmithyBusinessMetric.SIGV4A_SIGNING) } interceptors.readAfterSigning(modified.subject.immutableView()) diff --git a/runtime/runtime-core/api/runtime-core.api b/runtime/runtime-core/api/runtime-core.api index d800803077..9534423f1e 100644 --- a/runtime/runtime-core/api/runtime-core.api +++ b/runtime/runtime-core/api/runtime-core.api @@ -77,28 +77,38 @@ public final class aws/smithy/kotlin/runtime/ServiceException$ErrorType : java/l public static fun values ()[Laws/smithy/kotlin/runtime/ServiceException$ErrorType; } -public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics : java/lang/Enum { - public static final field ACCOUNT_ID_BASED_ENDPOINT Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field GZIP_REQUEST_COMPRESSION Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field RETRY_MODE_ADAPTIVE Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field RETRY_MODE_LEGACY Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field RETRY_MODE_STANDARD Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field S3_EXPRESS_BUCKET Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field SERVICE_ENDPOINT_OVERRIDE Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static final field SIGV4A_SIGNING Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public final fun getIdentifier ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; - public static fun values ()[Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics; +public abstract interface class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public abstract fun getIdentifier ()Ljava/lang/String; } public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsKt { - public static final fun containsBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)Z - public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)V - public static final fun getAccountIdBasedEndPointAccountId ()Laws/smithy/kotlin/runtime/collections/AttributeKey; + public static final fun containsBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)Z + public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)V + public static final fun getAccountIdBasedEndpointAccountId ()Laws/smithy/kotlin/runtime/collections/AttributeKey; public static final fun getBusinessMetrics ()Laws/smithy/kotlin/runtime/collections/AttributeKey; public static final fun getServiceEndpointOverride ()Laws/smithy/kotlin/runtime/collections/AttributeKey; - public static final fun removeBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetrics;)V + public static final fun removeBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)V +} + +public final class aws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public static final field S3_EXPRESS_BUCKET Laws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public fun getIdentifier ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Laws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric; + public static fun values ()[Laws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric; +} + +public final class aws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public static final field ACCOUNT_ID_BASED_ENDPOINT Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field GZIP_REQUEST_COMPRESSION Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field RETRY_MODE_ADAPTIVE Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field RETRY_MODE_STANDARD Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field SERVICE_ENDPOINT_OVERRIDE Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field SIGV4A_SIGNING Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public fun getIdentifier ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static fun values ()[Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; } public final class aws/smithy/kotlin/runtime/collections/AttributeKey { diff --git a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt index 12cde78056..a3a966e30d 100644 --- a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt +++ b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt @@ -13,29 +13,29 @@ import aws.smithy.kotlin.runtime.operation.ExecutionContext * Keeps track of all business metrics along an operations execution */ @InternalApi -public val businessMetrics: AttributeKey> = AttributeKey("aws.sdk.kotlin#BusinessMetrics") +public val BusinessMetrics: AttributeKey> = AttributeKey("aws.sdk.kotlin#BusinessMetrics") /** * The account ID in an account ID based endpoint */ @InternalApi -public val accountIdBasedEndPointAccountId: AttributeKey = AttributeKey("aws.smithy.kotlin#AccountIdBasedEndpointAccountId") +public val AccountIdBasedEndpointAccountId: AttributeKey = AttributeKey("aws.smithy.kotlin#AccountIdBasedEndpointAccountId") /** - * If an endpoint is service endpoint override based + * If an endpoint is "service endpoint override" based */ @InternalApi -public val serviceEndpointOverride: AttributeKey = AttributeKey("aws.smithy.kotlin#ServiceEndpointOverride") +public val ServiceEndpointOverride: AttributeKey = AttributeKey("aws.smithy.kotlin#ServiceEndpointOverride") /** * Emit a business metric to the execution context attributes */ @InternalApi -public fun ExecutionContext.emitBusinessMetric(metric: BusinessMetrics) { - if (this.attributes.contains(businessMetrics)) { - this.attributes[businessMetrics].add(metric.identifier) +public fun ExecutionContext.emitBusinessMetric(metric: BusinessMetric) { + if (this.attributes.contains(BusinessMetrics)) { + this.attributes[BusinessMetrics].add(metric.identifier) } else { - this.attributes[businessMetrics] = mutableSetOf(metric.identifier) + this.attributes[BusinessMetrics] = mutableSetOf(metric.identifier) } } @@ -43,9 +43,9 @@ public fun ExecutionContext.emitBusinessMetric(metric: BusinessMetrics) { * Removes a business metric from the execution context attributes */ @InternalApi -public fun ExecutionContext.removeBusinessMetric(metric: BusinessMetrics) { - if (this.attributes.contains(businessMetrics)) { - this.attributes[businessMetrics].remove(metric.identifier) +public fun ExecutionContext.removeBusinessMetric(metric: BusinessMetric) { + if (this.attributes.contains(BusinessMetrics)) { + this.attributes[BusinessMetrics].remove(metric.identifier) } } @@ -53,20 +53,25 @@ public fun ExecutionContext.removeBusinessMetric(metric: BusinessMetrics) { * Checks if a business metric exists in the execution context attributes */ @InternalApi -public fun ExecutionContext.containsBusinessMetric(metric: BusinessMetrics): Boolean = - (this.attributes.contains(businessMetrics)) && this.attributes[businessMetrics].contains(metric.identifier) +public fun ExecutionContext.containsBusinessMetric(metric: BusinessMetric): Boolean = + (this.attributes.contains(BusinessMetrics)) && this.attributes[BusinessMetrics].contains(metric.identifier) /** - * All the valid business metrics along with their identifiers + * Valid business metrics + */ +public interface BusinessMetric { + public val identifier: String +} + +/** + * Generic business metrics */ @InternalApi -public enum class BusinessMetrics(public val identifier: String) { - S3_EXPRESS_BUCKET("A"), - GZIP_REQUEST_COMPRESSION("B"), - RETRY_MODE_LEGACY("C"), - RETRY_MODE_STANDARD("D"), - RETRY_MODE_ADAPTIVE("E"), - SIGV4A_SIGNING("F"), - SERVICE_ENDPOINT_OVERRIDE("G"), - ACCOUNT_ID_BASED_ENDPOINT("H"), +public enum class SmithyBusinessMetric(public override val identifier: String) : BusinessMetric { + RETRY_MODE_STANDARD("E"), + RETRY_MODE_ADAPTIVE("F"), + GZIP_REQUEST_COMPRESSION("L"), + SERVICE_ENDPOINT_OVERRIDE("N"), + SIGV4A_SIGNING("M"), + ACCOUNT_ID_BASED_ENDPOINT("O"), } diff --git a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt index 85454aa77f..f7ac2717c1 100644 --- a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt +++ b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtilsTest.kt @@ -7,26 +7,52 @@ package aws.smithy.kotlin.runtime.businessmetrics import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.operation.ExecutionContext import kotlin.test.Test +import kotlin.test.assertFalse import kotlin.test.assertTrue class BusinessMetricsUtilsTest { @Test fun emitBusinessMetric() { val executionContext = ExecutionContext() - executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) + executionContext.emitBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) - assertTrue(executionContext.attributes.contains(businessMetrics)) - assertTrue(executionContext.attributes[businessMetrics].contains(BusinessMetrics.GZIP_REQUEST_COMPRESSION.identifier)) + assertTrue(executionContext.attributes.contains(BusinessMetrics)) + assertTrue(executionContext.attributes[BusinessMetrics].contains(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION.identifier)) } @Test fun emitMultipleBusinessMetrics() { val executionContext = ExecutionContext() - executionContext.emitBusinessMetric(BusinessMetrics.GZIP_REQUEST_COMPRESSION) - executionContext.emitBusinessMetric(BusinessMetrics.S3_EXPRESS_BUCKET) + executionContext.emitBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) + executionContext.emitBusinessMetric(SmithyBusinessMetric.SIGV4A_SIGNING) - assertTrue(executionContext.attributes.contains(businessMetrics)) - assertTrue(executionContext.attributes[businessMetrics].contains(BusinessMetrics.GZIP_REQUEST_COMPRESSION.identifier)) - assertTrue(executionContext.attributes[businessMetrics].contains(BusinessMetrics.S3_EXPRESS_BUCKET.identifier)) + assertTrue(executionContext.attributes.contains(BusinessMetrics)) + assertTrue(executionContext.attributes[BusinessMetrics].contains(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION.identifier)) + assertTrue(executionContext.attributes[BusinessMetrics].contains(SmithyBusinessMetric.SIGV4A_SIGNING.identifier)) + } + + @Test + fun removeBusinessMetric() { + val executionContext = ExecutionContext() + executionContext.emitBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) + + assertTrue(executionContext.attributes.contains(BusinessMetrics)) + assertTrue(executionContext.attributes[BusinessMetrics].contains(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION.identifier)) + + executionContext.removeBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) + + assertTrue(executionContext.attributes.contains(BusinessMetrics)) + assertFalse(executionContext.attributes[BusinessMetrics].contains(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION.identifier)) + } + + @Test + fun containsBusinessMetric() { + val executionContext = ExecutionContext() + + executionContext.emitBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) + assertTrue(executionContext.containsBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION)) + + executionContext.removeBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION) + assertFalse(executionContext.containsBusinessMetric(SmithyBusinessMetric.GZIP_REQUEST_COMPRESSION)) } } From 6d7677ba7cb7c1f92e8bdd0784ed326fe822b665 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Tue, 11 Jun 2024 14:46:52 -0400 Subject: [PATCH 05/10] gradle clean & api dump --- runtime/runtime-core/api/runtime-core.api | 8 -------- 1 file changed, 8 deletions(-) diff --git a/runtime/runtime-core/api/runtime-core.api b/runtime/runtime-core/api/runtime-core.api index 9534423f1e..e07ccee553 100644 --- a/runtime/runtime-core/api/runtime-core.api +++ b/runtime/runtime-core/api/runtime-core.api @@ -90,14 +90,6 @@ public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtil public static final fun removeBusinessMetric (Laws/smithy/kotlin/runtime/operation/ExecutionContext;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)V } -public final class aws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { - public static final field S3_EXPRESS_BUCKET Laws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public fun getIdentifier ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Laws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric; - public static fun values ()[Laws/smithy/kotlin/runtime/businessmetrics/SdkBusinessMetric; -} - public final class aws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { public static final field ACCOUNT_ID_BASED_ENDPOINT Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static final field GZIP_REQUEST_COMPRESSION Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; From bf1fff19fedf102f1ee6cec785920af3bf937d06 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 13 Jun 2024 14:01:44 -0400 Subject: [PATCH 06/10] feedback --- .../amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt | 3 ++- .../endpoints/DefaultEndpointProviderTestGenerator.kt | 6 +++++- .../kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt index 957e6cd51b..863ffeccae 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt @@ -103,7 +103,7 @@ object RuntimeTypes { object BusinessMetrics : RuntimeTypePackage(KotlinDependency.CORE, "businessmetrics") { val AccountIdBasedEndpointAccountId = symbol("AccountIdBasedEndpointAccountId") val ServiceEndpointOverride = symbol("ServiceEndpointOverride") - val emitBusinessMetrics = symbol("emitBusinessMetric") + val emitBusinessMetric = symbol("emitBusinessMetric") val SmithyBusinessMetric = symbol("SmithyBusinessMetric") } @@ -118,6 +118,7 @@ object RuntimeTypes { val putIfAbsentNotNull = symbol("putIfAbsentNotNull") val ReadThroughCache = symbol("ReadThroughCache") val toMutableAttributes = symbol("toMutableAttributes") + val emptyAttributes = symbol("emptyAttributes") } object Content : RuntimeTypePackage(KotlinDependency.CORE, "content") { diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt index 8f1b40feab..b33d5ab753 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt @@ -87,7 +87,11 @@ class DefaultEndpointProviderTestGenerator( RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId, ) writer.write( - "val newActual = #T(actual.uri, actual.headers, newActualAttributes)", + "val newActualAttributesOrEmpty = if (newActualAttributes.isEmpty) #T() else newActualAttributes", + RuntimeTypes.Core.Collections.emptyAttributes + ) + writer.write( + "val newActual = #T(actual.uri, actual.headers, newActualAttributesOrEmpty)", RuntimeTypes.SmithyClient.Endpoints.Endpoint, ) writer.write("assertEquals(expected, newActual)") diff --git a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt index a3a966e30d..c739c0c3ff 100644 --- a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt +++ b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtils.kt @@ -68,6 +68,8 @@ public interface BusinessMetric { */ @InternalApi public enum class SmithyBusinessMetric(public override val identifier: String) : BusinessMetric { + WAITER("B"), // TODO: Emit this metric + PAGINATOR("C"), // TODO: Emit this metric RETRY_MODE_STANDARD("E"), RETRY_MODE_ADAPTIVE("F"), GZIP_REQUEST_COMPRESSION("L"), From 79f1cb4cdd2fdda2dbfb3a69f52ddf309391697e Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 14 Jun 2024 13:26:21 -0400 Subject: [PATCH 07/10] api dump --- runtime/runtime-core/api/runtime-core.api | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/runtime-core/api/runtime-core.api b/runtime/runtime-core/api/runtime-core.api index e07ccee553..693082f708 100644 --- a/runtime/runtime-core/api/runtime-core.api +++ b/runtime/runtime-core/api/runtime-core.api @@ -93,10 +93,12 @@ public final class aws/smithy/kotlin/runtime/businessmetrics/BusinessMetricsUtil public final class aws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { public static final field ACCOUNT_ID_BASED_ENDPOINT Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static final field GZIP_REQUEST_COMPRESSION Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field PAGINATOR Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static final field RETRY_MODE_ADAPTIVE Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static final field RETRY_MODE_STANDARD Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static final field SERVICE_ENDPOINT_OVERRIDE Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static final field SIGV4A_SIGNING Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; + public static final field WAITER Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; public static fun getEntries ()Lkotlin/enums/EnumEntries; public fun getIdentifier ()Ljava/lang/String; public static fun valueOf (Ljava/lang/String;)Laws/smithy/kotlin/runtime/businessmetrics/SmithyBusinessMetric; From 456b7327d0327f88d8edff3bbff03bba92bf4511 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Fri, 14 Jun 2024 13:29:20 -0400 Subject: [PATCH 08/10] sigh...lint --- .../rendering/endpoints/DefaultEndpointProviderTestGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt index b33d5ab753..a6f4be3ac8 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt @@ -88,7 +88,7 @@ class DefaultEndpointProviderTestGenerator( ) writer.write( "val newActualAttributesOrEmpty = if (newActualAttributes.isEmpty) #T() else newActualAttributes", - RuntimeTypes.Core.Collections.emptyAttributes + RuntimeTypes.Core.Collections.emptyAttributes, ) writer.write( "val newActual = #T(actual.uri, actual.headers, newActualAttributesOrEmpty)", From eca076f87316046227137d7f44bf6b0c1d77140f Mon Sep 17 00:00:00 2001 From: 0marperez Date: Mon, 17 Jun 2024 15:58:19 -0400 Subject: [PATCH 09/10] fix: empty attributes & headers comparison --- .../endpoints/DefaultEndpointProviderTestGenerator.kt | 11 +++-------- .../src/aws/smithy/kotlin/runtime/http/Headers.kt | 1 + .../aws/smithy/kotlin/runtime/http/HeadersTest.kt | 9 +++++++++ .../smithy/kotlin/runtime/collections/Attributes.kt | 1 + .../kotlin/runtime/collections/AttributesTest.kt | 9 +++++++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt index a6f4be3ac8..97d3f4569b 100644 --- a/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt +++ b/codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/endpoints/DefaultEndpointProviderTestGenerator.kt @@ -61,7 +61,6 @@ class DefaultEndpointProviderTestGenerator( fun render() { writer.addImport("*", namespace = "kotlin.test") writer.withBlock("public class #L {", "}", CLASS_NAME) { - writer.write("") writer.withBlock( "private fun expectEqualEndpoints(expected: #1T, actual: #1T) {", "}", @@ -79,19 +78,15 @@ class DefaultEndpointProviderTestGenerator( RuntimeTypes.Core.Collections.toMutableAttributes, ) writer.write( - "if (actual.attributes.contains(#1T)) newActualAttributes.remove(#1T)", + "newActualAttributes.remove(#T)", RuntimeTypes.Core.BusinessMetrics.ServiceEndpointOverride, ) writer.write( - "if (actual.attributes.contains(#1T)) newActualAttributes.remove(#1T)", + "newActualAttributes.remove(#T)", RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId, ) writer.write( - "val newActualAttributesOrEmpty = if (newActualAttributes.isEmpty) #T() else newActualAttributes", - RuntimeTypes.Core.Collections.emptyAttributes, - ) - writer.write( - "val newActual = #T(actual.uri, actual.headers, newActualAttributesOrEmpty)", + "val newActual = #T(actual.uri, actual.headers, newActualAttributes)", RuntimeTypes.SmithyClient.Endpoints.Endpoint, ) writer.write("assertEquals(expected, newActual)") diff --git a/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/Headers.kt b/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/Headers.kt index d73832f612..b9611fa450 100644 --- a/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/Headers.kt +++ b/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/Headers.kt @@ -32,6 +32,7 @@ private object EmptyHeaders : Headers { override fun entries(): Set>> = emptySet() override fun contains(name: String): Boolean = false override fun isEmpty(): Boolean = true + override fun equals(other: Any?): Boolean = other is Headers && other.isEmpty() } /** diff --git a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt index 6059c58d79..67cb47405c 100644 --- a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt +++ b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt @@ -55,4 +55,13 @@ class HeadersTest { assertEquals(firstExpected.entries, first.entries()) assertEquals(secondExpected.entries, second.entries()) } + + @Test + fun testEmptyEquals() { + val explicitlyEmpty = Headers.Empty + val implicitlyEmpty = HeadersBuilder().build() + + assertEquals(implicitlyEmpty, explicitlyEmpty) + assertEquals(explicitlyEmpty, implicitlyEmpty) + } } diff --git a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/collections/Attributes.kt b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/collections/Attributes.kt index f13e169223..1fffcc82a2 100644 --- a/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/collections/Attributes.kt +++ b/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/collections/Attributes.kt @@ -172,6 +172,7 @@ private object EmptyAttributes : Attributes { override val keys: Set> = emptySet() override fun contains(key: AttributeKey<*>): Boolean = false override fun getOrNull(key: AttributeKey): T? = null + override fun equals(other: Any?): Boolean = other is Attributes && other.isEmpty } /** diff --git a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt index c91db10f06..02c5010a18 100644 --- a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt +++ b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt @@ -125,4 +125,13 @@ class AttributesTest { assertEquals("foo", attrs[attr1]) assertEquals(57, attrs[attr2]) } + + @Test + fun testEmptyEquals() { + val explicitlyEmpty = emptyAttributes() + val implicitlyEmpty = AttributesBuilder().attributes + + assertEquals(implicitlyEmpty, explicitlyEmpty) + assertEquals(explicitlyEmpty, implicitlyEmpty) + } } From 65698cd05caf8cb887bf29fe2c2ccaf66255ee56 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Tue, 18 Jun 2024 11:52:13 -0400 Subject: [PATCH 10/10] fix more empty* objects' comparison --- runtime/protocol/http/api/http.api | 2 ++ .../kotlin/runtime/http/DeferredHeaders.kt | 1 + .../smithy/kotlin/runtime/http/HttpBody.kt | 7 ++++++ .../runtime/http/DeferredHeadersTest.kt | 22 +++++++++++++++++++ .../smithy/kotlin/runtime/http/HeadersTest.kt | 3 +++ .../kotlin/runtime/http/HttpBodyTest.kt | 12 ++++++++++ .../runtime/collections/AttributesTest.kt | 3 +++ 7 files changed, 50 insertions(+) create mode 100644 runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/DeferredHeadersTest.kt diff --git a/runtime/protocol/http/api/http.api b/runtime/protocol/http/api/http.api index 6e2863bf8f..3f4c29414f 100644 --- a/runtime/protocol/http/api/http.api +++ b/runtime/protocol/http/api/http.api @@ -52,6 +52,7 @@ public final class aws/smithy/kotlin/runtime/http/HeadersBuilder : aws/smithy/ko public abstract class aws/smithy/kotlin/runtime/http/HttpBody { public static final field Companion Laws/smithy/kotlin/runtime/http/HttpBody$Companion; + public fun equals (Ljava/lang/Object;)Z public fun getContentLength ()Ljava/lang/Long; public fun isDuplex ()Z public fun isOneShot ()Z @@ -74,6 +75,7 @@ public final class aws/smithy/kotlin/runtime/http/HttpBody$Companion { public final class aws/smithy/kotlin/runtime/http/HttpBody$Empty : aws/smithy/kotlin/runtime/http/HttpBody { public static final field INSTANCE Laws/smithy/kotlin/runtime/http/HttpBody$Empty; + public fun equals (Ljava/lang/Object;)Z public fun getContentLength ()Ljava/lang/Long; public final fun isOneShot ()Z } diff --git a/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/DeferredHeaders.kt b/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/DeferredHeaders.kt index d410be45e9..db134a7146 100644 --- a/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/DeferredHeaders.kt +++ b/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/DeferredHeaders.kt @@ -35,6 +35,7 @@ private object EmptyDeferredHeaders : DeferredHeaders { override fun entries(): Set>>> = emptySet() override fun contains(name: String): Boolean = false override fun isEmpty(): Boolean = true + override fun equals(other: Any?): Boolean = other is DeferredHeaders && other.isEmpty() } /** diff --git a/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/HttpBody.kt b/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/HttpBody.kt index df70ae8461..ed8ef428bf 100644 --- a/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/HttpBody.kt +++ b/runtime/protocol/http/common/src/aws/smithy/kotlin/runtime/http/HttpBody.kt @@ -35,12 +35,19 @@ public sealed class HttpBody { */ public open val isDuplex: Boolean = false + override fun equals(other: Any?): Boolean = + other is HttpBody && + other.contentLength == contentLength && + other.isOneShot == isOneShot && + other.isDuplex == isDuplex + /** * Variant of a [HttpBody] without a payload */ public object Empty : HttpBody() { final override val isOneShot: Boolean = false override val contentLength: Long = 0 + override fun equals(other: Any?): Boolean = other is HttpBody && other.contentLength == contentLength } /** diff --git a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/DeferredHeadersTest.kt b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/DeferredHeadersTest.kt new file mode 100644 index 0000000000..18e15a287f --- /dev/null +++ b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/DeferredHeadersTest.kt @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.smithy.kotlin.runtime.http + +import kotlin.test.Test +import kotlin.test.assertEquals + +class DeferredHeadersTest { + @Test + fun testEmptyEquals() { + val explicitlyEmpty = DeferredHeaders.Empty + val implicitlyEmpty = DeferredHeadersBuilder().build() + + assertEquals(implicitlyEmpty, explicitlyEmpty) + assertEquals(explicitlyEmpty, implicitlyEmpty) + + assertEquals(explicitlyEmpty, explicitlyEmpty) + assertEquals(implicitlyEmpty, implicitlyEmpty) + } +} diff --git a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt index 67cb47405c..12287dcc8d 100644 --- a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt +++ b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HeadersTest.kt @@ -63,5 +63,8 @@ class HeadersTest { assertEquals(implicitlyEmpty, explicitlyEmpty) assertEquals(explicitlyEmpty, implicitlyEmpty) + + assertEquals(explicitlyEmpty, explicitlyEmpty) + assertEquals(implicitlyEmpty, implicitlyEmpty) } } diff --git a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HttpBodyTest.kt b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HttpBodyTest.kt index 1458eb3d32..844d054166 100644 --- a/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HttpBodyTest.kt +++ b/runtime/protocol/http/common/test/aws/smithy/kotlin/runtime/http/HttpBodyTest.kt @@ -79,4 +79,16 @@ class HttpBodyTest { assertEquals(expected, body.readAll()!!.decodeToString()) } + + @Test + fun testEmptyEquals() { + val explicitlyEmpty = HttpBody.Empty + val implicitlyEmpty = HttpBody.fromBytes(byteArrayOf()) + + assertEquals(implicitlyEmpty, explicitlyEmpty) + assertEquals(explicitlyEmpty, implicitlyEmpty) + + assertEquals(implicitlyEmpty, implicitlyEmpty) + assertEquals(explicitlyEmpty, explicitlyEmpty) + } } diff --git a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt index 02c5010a18..27a00bc107 100644 --- a/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt +++ b/runtime/runtime-core/common/test/aws/smithy/kotlin/runtime/collections/AttributesTest.kt @@ -133,5 +133,8 @@ class AttributesTest { assertEquals(implicitlyEmpty, explicitlyEmpty) assertEquals(explicitlyEmpty, implicitlyEmpty) + + assertEquals(explicitlyEmpty, explicitlyEmpty) + assertEquals(implicitlyEmpty, implicitlyEmpty) } }