Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ val DECORATORS: List<ClientCodegenDecorator> =
AwsRequestIdDecorator(),
DisabledAuthDecorator(),
RecursionDetectionDecorator(),
EndpointOverrideDecorator(),
ObservabilityDetectionDecorator(),
InvocationIdDecorator(),
RetryInformationHeaderDecorator(),
RemoveDefaultsDecorator(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rustsdk

import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.writable

/**
* Registers the EndpointOverrideInterceptor to detect custom endpoint usage for business metrics
*/
class EndpointOverrideDecorator : ClientCodegenDecorator {
override val name: String = "EndpointOverride"
override val order: Byte = 0

override fun serviceRuntimePluginCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ServiceRuntimePluginCustomization>,
): List<ServiceRuntimePluginCustomization> =
baseCustomizations + EndpointOverrideRuntimePluginCustomization(codegenContext)

private class EndpointOverrideRuntimePluginCustomization(codegenContext: ClientCodegenContext) :
ServiceRuntimePluginCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig)

override fun section(section: ServiceRuntimePluginSection): Writable =
writable {
when (section) {
is ServiceRuntimePluginSection.RegisterRuntimeComponents -> {
section.registerInterceptor(this) {
rust(
"#T::new()",
awsRuntime.resolve("endpoint_override::EndpointOverrideInterceptor"),
)
}
}
else -> emptySection
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rustsdk

import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.writable

/**
* Registers the ObservabilityDetectionInterceptor to detect observability feature usage for business metrics
*/
class ObservabilityDetectionDecorator : ClientCodegenDecorator {
override val name: String = "ObservabilityDetection"
override val order: Byte = 0

override fun serviceRuntimePluginCustomizations(
codegenContext: ClientCodegenContext,
baseCustomizations: List<ServiceRuntimePluginCustomization>,
): List<ServiceRuntimePluginCustomization> =
baseCustomizations + ObservabilityDetectionRuntimePluginCustomization(codegenContext)

private class ObservabilityDetectionRuntimePluginCustomization(codegenContext: ClientCodegenContext) :
ServiceRuntimePluginCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig)

override fun section(section: ServiceRuntimePluginSection): Writable =
writable {
when (section) {
is ServiceRuntimePluginSection.RegisterRuntimeComponents -> {
section.registerInterceptor(this) {
rust(
"#T::new()",
awsRuntime.resolve("observability_detection::ObservabilityDetectionInterceptor"),
)
}
}
else -> emptySection
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.rustsdk

import org.junit.jupiter.api.Test
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.testutil.integrationTest
import software.amazon.smithy.rust.codegen.core.testutil.tokioTest

class EndpointOverrideDecoratorTest {
companion object {
private const val PREFIX = "\$version: \"2\""
val model =
"""
$PREFIX
namespace test

use aws.api#service
use aws.auth#sigv4
use aws.protocols#restJson1
use smithy.rules#endpointRuleSet

@service(sdkId: "dontcare")
@restJson1
@sigv4(name: "dontcare")
@auth([sigv4])
@endpointRuleSet({
"version": "1.0",
"rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }],
"parameters": {
"Region": { "required": false, "type": "String", "builtIn": "AWS::Region" },
}
})
service TestService {
version: "2023-01-01",
operations: [SomeOperation]
}

@http(uri: "/SomeOperation", method: "GET")
@optionalAuth
operation SomeOperation {
input: SomeInput,
output: SomeOutput
}

@input
structure SomeInput {}

@output
structure SomeOutput {}
""".asSmithyModel()
}

@Test
fun `decorator is registered in AwsCodegenDecorator list`() {
// Verify that EndpointOverrideDecorator is in the DECORATORS list
val decoratorNames = DECORATORS.map { it.name }
assert(decoratorNames.contains("EndpointOverride")) {
"EndpointOverrideDecorator should be registered in DECORATORS list"
}
}

@Test
fun `generated code includes endpoint override interceptor registration`() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't quite test what the name suggests it does. It tests that it compiles, but it would compile just find if the EndpointOverride interceptor wasn't inserted at all. Same with the test below. I would try to write a test that actually ensures the interceptor is inserted, likely by making a request with an overridden endpoint and checking the user-agent on that request.

awsSdkIntegrationTest(model) { _, _ ->
// The test passes if the code compiles successfully
// This verifies that the decorator generates valid Rust code
}
}

@Test
fun `generated code compiles with endpoint override interceptor`() {
// Create custom test params with endpoint_url config enabled
val testParams =
awsIntegrationTestParams().copy(
additionalSettings =
awsIntegrationTestParams().additionalSettings.toBuilder()
.withMember(
"codegen",
software.amazon.smithy.model.node.ObjectNode.builder()
.withMember("includeFluentClient", false)
.withMember("includeEndpointUrlConfig", true)
.build(),
)
.build(),
)

awsSdkIntegrationTest(model, testParams) { context, rustCrate ->
val rc = context.runtimeConfig
val moduleName = context.moduleUseName()
rustCrate.integrationTest("endpoint_override_compiles") {
tokioTest("can_build_client_with_endpoint_url") {
rustTemplate(
"""
use $moduleName::config::Region;
use $moduleName::{Client, Config};

let (http_client, _rcvr) = #{capture_request}(#{None});
let config = Config::builder()
.region(Region::new("us-east-1"))
.endpoint_url("https://custom.example.com")
.http_client(http_client.clone())
.with_test_defaults()
.build();
let _client = Client::from_conf(config);
// Test passes if code compiles and client can be created
""",
*preludeScope,
"capture_request" to RuntimeType.captureRequest(rc),
)
}
}
}
}
}
17 changes: 9 additions & 8 deletions aws/rust-runtime/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions aws/rust-runtime/aws-config/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-credential-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "aws-credential-types"
version = "1.2.9"
version = "1.2.10"
authors = ["AWS Rust SDK Team <[email protected]>"]
description = "Types for AWS SDK credentials."
edition = "2021"
Expand Down
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ aws-sigv4 = { path = "../aws-sigv4", features = ["http0-compat"] }
aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" }
aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true }
aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" }
aws-smithy-observability = { path = "../../../rust-runtime/aws-smithy-observability" }
aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", features = ["client"] }
aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] }
aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" }
Expand Down
Loading
Loading