Commit 33a1c48f authored by John DiSanti's avatar John DiSanti
Browse files

Merge 'smithy-rs-release-0.56.x' into `main`

parents 784fbdbc 1b6f0b8a
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -66,3 +66,21 @@ message = "Allow `no_credentials` to be used with all S3 operations."
references = ["smithy-rs#2955", "aws-sdk-rust#878"]
meta = { "breaking" = false, "tada" = false, "bug" = true }
author = "jdisanti"

[[aws-sdk-rust]]
message = "`CustomizableOperation`, created as a result of calling the `.customize` method on a fluent builder, ceased to be `Send` and `Sync` in the previous releases. It is now `Send` and `Sync` again."
references = ["smithy-rs#2944", "smithy-rs#2951"]
meta = { "breaking" = false, "tada" = false, "bug" = true }
author = "ysaito1001"

[[smithy-rs]]
message = "`CustomizableOperation`, created as a result of calling the `.customize` method on a fluent builder, ceased to be `Send` and `Sync` in the previous releases. It is now `Send` and `Sync` again."
references = ["smithy-rs#2944", "smithy-rs#2951"]
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" }
author = "ysaito1001"

[[smithy-rs]]
message = "Generate a region setter when a model uses SigV4."
references = ["smithy-rs#2960"]
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" }
author = "jdisanti"
+7 −1
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@

package software.amazon.smithy.rustsdk

import software.amazon.smithy.aws.traits.auth.SigV4Trait
import software.amazon.smithy.model.knowledge.ServiceIndex
import software.amazon.smithy.model.node.Node
import software.amazon.smithy.rulesengine.language.syntax.parameters.Builtins
import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter
@@ -79,7 +81,11 @@ class RegionDecorator : ClientCodegenDecorator {
    override val name: String = "Region"
    override val order: Byte = 0

    private fun usesRegion(codegenContext: ClientCodegenContext) = codegenContext.getBuiltIn(Builtins.REGION) != null
    // Services that have an endpoint ruleset that references the SDK::Region built in, or
    // that use SigV4, both need a configurable region.
    private fun usesRegion(codegenContext: ClientCodegenContext) =
        codegenContext.getBuiltIn(Builtins.REGION) != null || ServiceIndex.of(codegenContext.model)
            .getEffectiveAuthSchemes(codegenContext.serviceShape).containsKey(SigV4Trait.ID)

    override fun configCustomizations(
        codegenContext: ClientCodegenContext,
+102 −0
Original line number Diff line number Diff line
/*
 * 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.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import kotlin.io.path.readText

class RegionDecoratorTest {
    private val modelWithoutRegionParamOrSigV4AuthScheme = """
        namespace test

        use aws.api#service
        use aws.protocols#awsJson1_0
        use smithy.rules#endpointRuleSet

        @awsJson1_0
        @endpointRuleSet({
            "version": "1.0",
            "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }],
            "parameters": {}
        })
        @service(sdkId: "dontcare")
        service TestService { version: "2023-01-01", operations: [SomeOperation] }
        structure SomeOutput { something: String }
        operation SomeOperation { output: SomeOutput }
    """.asSmithyModel()

    private val modelWithRegionParam = """
        namespace test

        use aws.api#service
        use aws.protocols#awsJson1_0
        use smithy.rules#endpointRuleSet

        @awsJson1_0
        @endpointRuleSet({
            "version": "1.0",
            "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }],
            "parameters": {
                "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" },
            }
        })
        @service(sdkId: "dontcare")
        service TestService { version: "2023-01-01", operations: [SomeOperation] }
        structure SomeOutput { something: String }
        operation SomeOperation { output: SomeOutput }
    """.asSmithyModel()

    private val modelWithSigV4AuthScheme = """
        namespace test

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

        @auth([sigv4])
        @sigv4(name: "dontcare")
        @awsJson1_0
        @endpointRuleSet({
            "version": "1.0",
            "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }],
            "parameters": {}
        })
        @service(sdkId: "dontcare")
        service TestService { version: "2023-01-01", operations: [SomeOperation] }
        structure SomeOutput { something: String }
        operation SomeOperation { output: SomeOutput }
    """.asSmithyModel()

    @Test
    fun `models without region built-in params or SigV4 should not have configurable regions`() {
        val path = awsSdkIntegrationTest(modelWithoutRegionParamOrSigV4AuthScheme) { _, _ ->
            // it should generate and compile successfully
        }
        val configContents = path.resolve("src/config.rs").readText()
        assertFalse(configContents.contains("fn set_region("))
    }

    @Test
    fun `models with region built-in params should have configurable regions`() {
        val path = awsSdkIntegrationTest(modelWithRegionParam) { _, _ ->
            // it should generate and compile successfully
        }
        val configContents = path.resolve("src/config.rs").readText()
        assertTrue(configContents.contains("fn set_region("))
    }

    @Test
    fun `models with SigV4 should have configurable regions`() {
        val path = awsSdkIntegrationTest(modelWithSigV4AuthScheme) { _, _ ->
            // it should generate and compile successfully
        }
        val configContents = path.resolve("src/config.rs").readText()
        assertTrue(configContents.contains("fn set_region("))
    }
}
+29 −15
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ class CustomizableOperationGenerator(
                .resolve("client::interceptors::MapRequestInterceptor"),
            "MutateRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig)
                .resolve("client::interceptors::MutateRequestInterceptor"),
            "PhantomData" to RuntimeType.Phantom,
            "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig),
            "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(runtimeConfig),
            "SendResult" to ClientRustModule.Client.customize.toType()
@@ -61,14 +62,28 @@ class CustomizableOperationGenerator(
            rustTemplate(
                """
                /// `CustomizableOperation` allows for configuring a single operation invocation before it is sent.
                pub struct CustomizableOperation<T, E> {
                    pub(crate) customizable_send: #{Box}<dyn #{CustomizableSend}<T, E>>,
                    pub(crate) config_override: #{Option}<crate::config::Builder>,
                    pub(crate) interceptors: Vec<#{SharedInterceptor}>,
                    pub(crate) runtime_plugins: Vec<#{SharedRuntimePlugin}>,
                pub struct CustomizableOperation<T, E, B> {
                    customizable_send: B,
                    config_override: #{Option}<crate::config::Builder>,
                    interceptors: Vec<#{SharedInterceptor}>,
                    runtime_plugins: Vec<#{SharedRuntimePlugin}>,
                    _output: #{PhantomData}<T>,
                    _error: #{PhantomData}<E>,
                }

                impl<T, E, B> CustomizableOperation<T, E, B> {
                    /// Creates a new `CustomizableOperation` from `customizable_send`.
                    pub(crate) fn new(customizable_send: B) -> Self {
                        Self {
                            customizable_send,
                            config_override: #{None},
                            interceptors: vec![],
                            runtime_plugins: vec![],
                            _output: #{PhantomData},
                            _error: #{PhantomData}
                        }
                    }

                impl<T, E> CustomizableOperation<T, E> {
                    /// Adds an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline.
                    ///
                    /// Note that interceptors can also be added to `CustomizableOperation` by `config_override`,
@@ -142,6 +157,7 @@ class CustomizableOperationGenerator(
                    ) -> #{SendResult}<T, E>
                    where
                        E: std::error::Error + #{Send} + #{Sync} + 'static,
                        B: #{CustomizableSend}<T, E>,
                    {
                        let mut config_override = self.config_override.unwrap_or_default();
                        self.interceptors.into_iter().for_each(|interceptor| {
@@ -151,7 +167,7 @@ class CustomizableOperationGenerator(
                            config_override.push_runtime_plugin(plugin);
                        });

                        (self.customizable_send)(config_override).await
                        self.customizable_send.send(config_override).await
                    }

                    #{additional_methods}
@@ -182,14 +198,12 @@ class CustomizableOperationGenerator(
                    >,
                >;

                pub trait CustomizableSend<T, E>:
                    #{FnOnce}(crate::config::Builder) -> BoxFuture<SendResult<T, E>>
                {}

                impl<F, T, E> CustomizableSend<T, E> for F
                where
                    F: #{FnOnce}(crate::config::Builder) -> BoxFuture<SendResult<T, E>>
                {}
                pub trait CustomizableSend<T, E>: #{Send} + #{Sync} {
                    // Takes an owned `self` as the implementation will internally call methods that take `self`.
                    // If it took `&self`, that would make this trait object safe, but some implementing types do not
                    // derive `Clone`, unable to yield `self` from `&self`.
                    fn send(self, config_override: crate::config::Builder) -> BoxFuture<SendResult<T, E>>;
                }
                """,
                *preludeScope,
                "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig)
+29 −12
Original line number Diff line number Diff line
@@ -299,6 +299,33 @@ class FluentClientGenerator(
            rustTemplate("config_override: #{Option}<crate::config::Builder>,", *preludeScope)
        }

        rustTemplate(
            """
            impl
                crate::client::customize::internal::CustomizableSend<
                    #{OperationOutput},
                    #{OperationError},
                > for $builderName
            {
                fn send(
                    self,
                    config_override: crate::config::Builder,
                ) -> crate::client::customize::internal::BoxFuture<
                    crate::client::customize::internal::SendResult<
                        #{OperationOutput},
                        #{OperationError},
                    >,
                > {
                    #{Box}::pin(async move { self.config_override(config_override).send().await })
                }
            }
            """,
            *preludeScope,
            "OperationError" to errorType,
            "OperationOutput" to outputType,
            "SdkError" to RuntimeType.sdkError(runtimeConfig),
        )

        rustBlockTemplate(
            "impl $builderName",
            "client" to RuntimeType.smithyClient(runtimeConfig),
@@ -369,22 +396,12 @@ class FluentClientGenerator(
                    #{CustomizableOperation}<
                        #{OperationOutput},
                        #{OperationError},
                        Self,
                    >,
                    #{SdkError}<#{OperationError}>,
                >
                {
                    #{Ok}(#{CustomizableOperation} {
                        customizable_send: #{Box}::new(move |config_override| {
                            #{Box}::pin(async {
                                self.config_override(config_override)
                                    .send()
                                    .await
                            })
                        }),
                        config_override: None,
                        interceptors: vec![],
                        runtime_plugins: vec![],
                    })
                    #{Ok}(#{CustomizableOperation}::new(self))
                }
                """,
                *orchestratorScope,
Loading