mirror of https://github.com/smithy-lang/smithy-rs
Add support for BehaviorMajorVersions (#3151)
## Motivation and Context
See [rendered
RFC](df518bfb59/design/src/rfcs/rfc0039_behavior_major_versions.md
)
## Description
This add `BehaviorMajorVersions` to the SDK and wires them in up and
down the stack.
## Testing
- [x] lots of ITs / UTs
## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS
SDK, generated SDK code, or SDK runtime crates
----
_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
This commit is contained in:
parent
4128662936
commit
446326c537
|
@ -67,6 +67,35 @@ references = ["smithy-rs#3160"]
|
|||
meta = { "breaking" = true, "tada" = false, "bug" = false }
|
||||
author = "jdisanti"
|
||||
|
||||
[[aws-sdk-rust]]
|
||||
message = """Clients now require a `BehaviorMajorVersion` to be provided. For must customers, `latest` is the best choice. This will be enabled automatically if you enable the `behavior-version-latest` cargo feature on `aws-config` or on an SDK crate. For customers that wish to pin to a specific behavior major version, it can be set in `aws-config` or when constructing the service client.
|
||||
|
||||
```rust
|
||||
async fn example() {
|
||||
// with aws-config
|
||||
let conf = aws_config::from_env_with_version(aws_config::BehaviorMajorVersion::v2023_11_09());
|
||||
|
||||
// when creating a client
|
||||
let client = my_service::Client::from_conf(my_service::Config::builder().behavior_major_version(..).<other params>.build());
|
||||
}
|
||||
```"""
|
||||
references = ["smithy-rs#3151"]
|
||||
author = "rcoh"
|
||||
meta = { "breaking" = true, "tada" = false, "bug" = false }
|
||||
|
||||
[[smithy-rs]]
|
||||
message = """Clients now require a `BehaviorMajorVersion` to be provided. For must customers, `latest` is the best choice. This will be enabled automatically if you enable the `behavior-version-latest` cargo feature on `aws-config` or on an SDK crate. For customers that wish to pin to a specific behavior major version, it can be set in `aws-config` or when constructing the service client.
|
||||
|
||||
```rust
|
||||
async fn example() {
|
||||
// when creating a client
|
||||
let client = my_service::Client::from_conf(my_service::Config::builder().behavior_major_version(..).<other params>.build());
|
||||
}
|
||||
```"""
|
||||
references = ["smithy-rs#3151"]
|
||||
author = "rcoh"
|
||||
meta = { "breaking" = true, "tada" = false, "bug" = false }
|
||||
|
||||
[[aws-sdk-rust]]
|
||||
message = "Add `ProvideErrorMetadata` impl for service `Error` type."
|
||||
references = ["aws-sdk-rust#780", "smithy-rs#3189"]
|
||||
|
|
|
@ -33,7 +33,7 @@ The SDK provides one crate per AWS service. You must add [Tokio](https://crates.
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
aws-config = "{{sdk_version_aws_config}}"
|
||||
aws-config = { version= "{{sdk_version_aws_config}}", features = ["behavior-version-latest"] }
|
||||
aws-sdk-dynamodb = "{{sdk_version_aws_sdk_dynamodb}}"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
```
|
||||
|
|
|
@ -9,6 +9,7 @@ license = "Apache-2.0"
|
|||
repository = "https://github.com/smithy-lang/smithy-rs"
|
||||
|
||||
[features]
|
||||
behavior-version-latest = []
|
||||
client-hyper = ["aws-smithy-runtime/connector-hyper-0-14-x"]
|
||||
rustls = ["aws-smithy-runtime/tls-rustls", "client-hyper"]
|
||||
allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI
|
||||
|
@ -71,6 +72,7 @@ serde_json = "1"
|
|||
hyper-rustls = { version = "0.24", features = ["webpki-tokio", "http2", "http1"] }
|
||||
aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio", "test-util"] }
|
||||
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
|
|
@ -22,6 +22,7 @@ allowed_external_types = [
|
|||
"aws_smithy_runtime_api::client::http::SharedHttpClient",
|
||||
"aws_smithy_runtime_api::client::identity::ResolveCachedIdentity",
|
||||
"aws_smithy_runtime_api::client::identity::ResolveIdentity",
|
||||
"aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion",
|
||||
"aws_smithy_runtime_api::client::orchestrator::HttpResponse",
|
||||
"aws_smithy_runtime_api::client::result::SdkError",
|
||||
"aws_smithy_types::body::SdkBody",
|
||||
|
|
|
@ -91,6 +91,7 @@ mod tests {
|
|||
use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
|
||||
use crate::provider_config::ProviderConfig;
|
||||
use crate::test_case::{no_traffic_client, InstantSleep};
|
||||
use aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion;
|
||||
use aws_types::os_shim_internal::{Env, Fs};
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -117,7 +118,7 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn profile_name_override() {
|
||||
let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk_ua_app_id = correct")]);
|
||||
let conf = crate::from_env()
|
||||
let conf = crate::from_env_with_version(BehaviorMajorVersion::latest())
|
||||
.sleep_impl(InstantSleep)
|
||||
.fs(fs)
|
||||
.http_client(no_traffic_client())
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
rustdoc::missing_crate_level_docs,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
||||
//! `aws-config` provides implementations of region and credential resolution.
|
||||
//!
|
||||
|
@ -25,14 +26,15 @@
|
|||
//!
|
||||
//! Load default SDK configuration:
|
||||
//! ```no_run
|
||||
//! # mod aws_sdk_dynamodb {
|
||||
//! use aws_config::BehaviorMajorVersion;
|
||||
//! mod aws_sdk_dynamodb {
|
||||
//! # pub struct Client;
|
||||
//! # impl Client {
|
||||
//! # pub fn new(config: &aws_types::SdkConfig) -> Self { Client }
|
||||
//! # }
|
||||
//! # }
|
||||
//! # async fn docs() {
|
||||
//! let config = aws_config::load_from_env().await;
|
||||
//! let config = aws_config::load_from_env_with_version(BehaviorMajorVersion::v2023_11_09()).await;
|
||||
//! let client = aws_sdk_dynamodb::Client::new(&config);
|
||||
//! # }
|
||||
//! ```
|
||||
|
@ -48,6 +50,7 @@
|
|||
//! # async fn docs() {
|
||||
//! # use aws_config::meta::region::RegionProviderChain;
|
||||
//! let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
|
||||
//! // Note: requires the `behavior-version-latest` feature enabled
|
||||
//! let config = aws_config::from_env().region(region_provider).load().await;
|
||||
//! let client = aws_sdk_dynamodb::Client::new(&config);
|
||||
//! # }
|
||||
|
@ -84,7 +87,7 @@
|
|||
//! # fn custom_provider(base: &SdkConfig) -> impl ProvideCredentials {
|
||||
//! # base.credentials_provider().unwrap().clone()
|
||||
//! # }
|
||||
//! let sdk_config = aws_config::load_from_env().await;
|
||||
//! let sdk_config = aws_config::load_from_env_with_version(aws_config::BehaviorMajorVersion::latest()).await;
|
||||
//! let custom_credentials_provider = custom_provider(&sdk_config);
|
||||
//! let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
|
||||
//! .credentials_provider(custom_credentials_provider)
|
||||
|
@ -94,9 +97,11 @@
|
|||
//! ```
|
||||
|
||||
pub use aws_smithy_http::endpoint;
|
||||
pub use aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion;
|
||||
// Re-export types from aws-types
|
||||
pub use aws_types::{
|
||||
app_name::{AppName, InvalidAppName},
|
||||
region::Region,
|
||||
SdkConfig,
|
||||
};
|
||||
/// Load default sources for all configuration with override support
|
||||
|
@ -137,24 +142,68 @@ pub mod web_identity_token;
|
|||
|
||||
/// Create an environment loader for AWS Configuration
|
||||
///
|
||||
/// This loader will always set [`BehaviorMajorVersion::latest`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
/// # async fn create_config() {
|
||||
/// use aws_types::region::Region;
|
||||
/// let config = aws_config::from_env().region("us-east-1").load().await;
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "behavior-version-latest")]
|
||||
pub fn from_env() -> ConfigLoader {
|
||||
ConfigLoader::default()
|
||||
ConfigLoader::default().behavior_major_version(BehaviorMajorVersion::latest())
|
||||
}
|
||||
|
||||
/// Load configuration from the environment
|
||||
#[cfg(not(feature = "behavior-version-latest"))]
|
||||
#[deprecated(
|
||||
note = "To enable the default behavior version, enable the `behavior-version-latest` feature. Alternatively, you can use [`from_env_with_version`]. This function will be removed in the next release."
|
||||
)]
|
||||
pub fn from_env() -> ConfigLoader {
|
||||
ConfigLoader::default().behavior_major_version(BehaviorMajorVersion::latest())
|
||||
}
|
||||
|
||||
/// Load configuration from the environment
|
||||
#[cfg(not(feature = "behavior-version-latest"))]
|
||||
#[deprecated(
|
||||
note = "To enable the default behavior version, enable the `behavior-version-latest` feature. Alternatively, you can use [`load_from_env_with_version`]. This function will be removed in the next release."
|
||||
)]
|
||||
pub async fn load_from_env() -> SdkConfig {
|
||||
load_from_env_with_version(BehaviorMajorVersion::latest()).await
|
||||
}
|
||||
|
||||
/// Create an environment loader for AWS Configuration
|
||||
///
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
/// # async fn create_config() {
|
||||
/// use aws_config::BehaviorMajorVersion;
|
||||
/// let config = aws_config::from_env_with_version(BehaviorMajorVersion::v2023_11_09())
|
||||
/// .region("us-east-1")
|
||||
/// .load()
|
||||
/// .await;
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from_env_with_version(version: BehaviorMajorVersion) -> ConfigLoader {
|
||||
ConfigLoader::default().behavior_major_version(version)
|
||||
}
|
||||
|
||||
/// Load a default configuration from the environment
|
||||
///
|
||||
/// Convenience wrapper equivalent to `aws_config::from_env().load().await`
|
||||
pub async fn load_from_env() -> aws_types::SdkConfig {
|
||||
#[cfg(feature = "behavior-version-latest")]
|
||||
pub async fn load_from_env() -> SdkConfig {
|
||||
from_env().load().await
|
||||
}
|
||||
|
||||
/// Load a default configuration from the environment
|
||||
///
|
||||
/// Convenience wrapper equivalent to `aws_config::from_env_with_version(BehaviorMajorVersion::latest()).load().await`
|
||||
pub async fn load_from_env_with_version(version: BehaviorMajorVersion) -> SdkConfig {
|
||||
from_env_with_version(version).load().await
|
||||
}
|
||||
|
||||
mod loader {
|
||||
use crate::default_provider::use_dual_stack::use_dual_stack_provider;
|
||||
use crate::default_provider::use_fips::use_fips_provider;
|
||||
|
@ -165,6 +214,7 @@ mod loader {
|
|||
use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider};
|
||||
use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
|
||||
use aws_smithy_async::time::{SharedTimeSource, TimeSource};
|
||||
use aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion;
|
||||
use aws_smithy_runtime_api::client::http::HttpClient;
|
||||
use aws_smithy_runtime_api::client::identity::{ResolveCachedIdentity, SharedIdentityCache};
|
||||
use aws_smithy_runtime_api::shared::IntoShared;
|
||||
|
@ -212,9 +262,19 @@ mod loader {
|
|||
time_source: Option<SharedTimeSource>,
|
||||
env: Option<Env>,
|
||||
fs: Option<Fs>,
|
||||
behavior_major_version: Option<BehaviorMajorVersion>,
|
||||
}
|
||||
|
||||
impl ConfigLoader {
|
||||
/// Sets the [`BehaviorMajorVersion`] used to build [`SdkConfig`](aws_types::SdkConfig).
|
||||
pub fn behavior_major_version(
|
||||
mut self,
|
||||
behavior_major_version: BehaviorMajorVersion,
|
||||
) -> Self {
|
||||
self.behavior_major_version = Some(behavior_major_version);
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the region used to build [`SdkConfig`](aws_types::SdkConfig).
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -571,7 +631,7 @@ mod loader {
|
|||
/// .enable_http1()
|
||||
/// .build();
|
||||
/// let provider_config = ProviderConfig::default().with_tcp_connector(custom_https_connector);
|
||||
/// let shared_config = aws_config::from_env().configure(provider_config).load().await;
|
||||
/// let shared_config = aws_config::from_env_with_version(BehaviorVersion::latest()).configure(provider_config).load().await;
|
||||
/// # }
|
||||
/// ```
|
||||
#[deprecated(
|
||||
|
@ -692,6 +752,7 @@ mod loader {
|
|||
.timeout_config(timeout_config)
|
||||
.time_source(time_source);
|
||||
|
||||
builder.set_behavior_major_version(self.behavior_major_version);
|
||||
builder.set_http_client(self.http_client);
|
||||
builder.set_app_name(app_name);
|
||||
builder.set_identity_cache(self.identity_cache);
|
||||
|
@ -721,7 +782,8 @@ mod loader {
|
|||
mod test {
|
||||
use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
|
||||
use crate::test_case::{no_traffic_client, InstantSleep};
|
||||
use crate::{from_env, ConfigLoader};
|
||||
use crate::BehaviorMajorVersion;
|
||||
use crate::{from_env_with_version, ConfigLoader};
|
||||
use aws_credential_types::provider::ProvideCredentials;
|
||||
use aws_smithy_async::rt::sleep::TokioSleep;
|
||||
use aws_smithy_runtime::client::http::test_util::{infallible_client_fn, NeverClient};
|
||||
|
@ -742,7 +804,7 @@ mod loader {
|
|||
]);
|
||||
let fs =
|
||||
Fs::from_slice(&[("test_config", "[profile custom]\nsdk-ua-app-id = correct")]);
|
||||
let loader = from_env()
|
||||
let loader = from_env_with_version(BehaviorMajorVersion::latest())
|
||||
.sleep_impl(TokioSleep::new())
|
||||
.env(env)
|
||||
.fs(fs)
|
||||
|
@ -786,7 +848,7 @@ mod loader {
|
|||
}
|
||||
|
||||
fn base_conf() -> ConfigLoader {
|
||||
from_env()
|
||||
from_env_with_version(BehaviorMajorVersion::latest())
|
||||
.sleep_impl(InstantSleep)
|
||||
.http_client(no_traffic_client())
|
||||
}
|
||||
|
@ -816,7 +878,10 @@ mod loader {
|
|||
#[cfg(feature = "rustls")]
|
||||
#[tokio::test]
|
||||
async fn disable_default_credentials() {
|
||||
let config = from_env().no_credentials().load().await;
|
||||
let config = from_env_with_version(BehaviorMajorVersion::latest())
|
||||
.no_credentials()
|
||||
.load()
|
||||
.await;
|
||||
assert!(config.identity_cache().is_none());
|
||||
assert!(config.credentials_provider().is_none());
|
||||
}
|
||||
|
@ -829,7 +894,7 @@ mod loader {
|
|||
movable.fetch_add(1, Ordering::Relaxed);
|
||||
http::Response::new("ok!")
|
||||
});
|
||||
let config = from_env()
|
||||
let config = from_env_with_version(BehaviorMajorVersion::latest())
|
||||
.fs(Fs::from_slice(&[]))
|
||||
.env(Env::from_slice(&[]))
|
||||
.http_client(http_client.clone())
|
||||
|
|
|
@ -196,7 +196,8 @@ impl ProviderConfig {
|
|||
.region(self.region())
|
||||
.time_source(self.time_source())
|
||||
.use_fips(self.use_fips().unwrap_or_default())
|
||||
.use_dual_stack(self.use_dual_stack().unwrap_or_default());
|
||||
.use_dual_stack(self.use_dual_stack().unwrap_or_default())
|
||||
.behavior_major_version(crate::BehaviorMajorVersion::latest());
|
||||
builder.set_http_client(self.http_client.clone());
|
||||
builder.set_sleep_impl(self.sleep_impl.clone());
|
||||
builder.build()
|
||||
|
|
|
@ -319,7 +319,9 @@ impl Builder {
|
|||
/// This will panic if any of the required fields are not given.
|
||||
pub async fn build(mut self) -> SsoTokenProvider {
|
||||
if self.sdk_config.is_none() {
|
||||
self.sdk_config = Some(crate::load_from_env().await);
|
||||
self.sdk_config = Some(
|
||||
crate::load_from_env_with_version(crate::BehaviorMajorVersion::latest()).await,
|
||||
);
|
||||
}
|
||||
self.build_with(Env::real(), Fs::real())
|
||||
}
|
||||
|
@ -427,6 +429,7 @@ mod tests {
|
|||
.sleep_impl(SharedAsyncSleep::new(sleep_impl))
|
||||
// disable retry to simplify testing
|
||||
.retry_config(RetryConfig::disabled())
|
||||
.behavior_major_version(crate::BehaviorMajorVersion::latest())
|
||||
.build();
|
||||
Self {
|
||||
time_source,
|
||||
|
|
|
@ -221,7 +221,7 @@ impl AssumeRoleProviderBuilder {
|
|||
pub async fn build(self) -> AssumeRoleProvider {
|
||||
let mut conf = match self.sdk_config {
|
||||
Some(conf) => conf,
|
||||
None => crate::load_from_env().await,
|
||||
None => crate::load_from_env_with_version(crate::BehaviorMajorVersion::latest()).await,
|
||||
};
|
||||
// ignore a identity cache set from SdkConfig
|
||||
conf = conf
|
||||
|
@ -264,7 +264,7 @@ impl AssumeRoleProviderBuilder {
|
|||
) -> AssumeRoleProvider {
|
||||
let conf = match self.sdk_config {
|
||||
Some(conf) => conf,
|
||||
None => crate::load_from_env().await,
|
||||
None => crate::load_from_env_with_version(crate::BehaviorMajorVersion::latest()).await,
|
||||
};
|
||||
let conf = conf
|
||||
.into_builder()
|
||||
|
@ -334,6 +334,7 @@ mod test {
|
|||
capture_request, ReplayEvent, StaticReplayClient,
|
||||
};
|
||||
use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs;
|
||||
use aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion;
|
||||
use aws_smithy_types::body::SdkBody;
|
||||
use aws_types::os_shim_internal::Env;
|
||||
use aws_types::region::Region;
|
||||
|
@ -351,6 +352,7 @@ mod test {
|
|||
))
|
||||
.http_client(http_client)
|
||||
.region(Region::from_static("this-will-be-overridden"))
|
||||
.behavior_major_version(crate::BehaviorMajorVersion::latest())
|
||||
.build();
|
||||
let provider = AssumeRoleProvider::builder("myrole")
|
||||
.configure(&sdk_config)
|
||||
|
@ -371,6 +373,7 @@ mod test {
|
|||
async fn loads_region_from_sdk_config() {
|
||||
let (http_client, request) = capture_request(None);
|
||||
let sdk_config = SdkConfig::builder()
|
||||
.behavior_major_version(crate::BehaviorMajorVersion::latest())
|
||||
.sleep_impl(SharedAsyncSleep::new(TokioSleep::new()))
|
||||
.time_source(StaticTimeSource::new(
|
||||
UNIX_EPOCH + Duration::from_secs(1234567890 - 120),
|
||||
|
@ -405,7 +408,7 @@ mod test {
|
|||
.body(SdkBody::from(""))
|
||||
.unwrap(),
|
||||
));
|
||||
let conf = crate::from_env()
|
||||
let conf = crate::from_env_with_version(BehaviorMajorVersion::latest())
|
||||
.env(Env::from_slice(&[
|
||||
("AWS_ACCESS_KEY_ID", "123-key"),
|
||||
("AWS_SECRET_ACCESS_KEY", "456"),
|
||||
|
@ -421,7 +424,7 @@ mod test {
|
|||
.configure(&conf)
|
||||
.build()
|
||||
.await;
|
||||
let _ = provider.provide_credentials().await;
|
||||
let _ = dbg!(provider.provide_credentials().await);
|
||||
let req = request.expect_request();
|
||||
let auth_header = req.headers().get(AUTHORIZATION).unwrap().to_string();
|
||||
let expect = "Credential=123-key/20090213/us-west-17/sts/aws4_request";
|
||||
|
@ -454,6 +457,7 @@ mod test {
|
|||
.sleep_impl(SharedAsyncSleep::new(sleep))
|
||||
.time_source(testing_time_source.clone())
|
||||
.http_client(http_client)
|
||||
.behavior_major_version(crate::BehaviorMajorVersion::latest())
|
||||
.build();
|
||||
let credentials_list = std::sync::Arc::new(std::sync::Mutex::new(vec![
|
||||
Credentials::new(
|
||||
|
|
|
@ -9,6 +9,7 @@ allowed_external_types = [
|
|||
"aws_smithy_runtime_api::client::http::SharedHttpClient",
|
||||
"aws_smithy_runtime_api::client::identity::ResolveCachedIdentity",
|
||||
"aws_smithy_runtime_api::client::identity::SharedIdentityCache",
|
||||
"aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion",
|
||||
"aws_smithy_runtime_api::http::headers::Headers",
|
||||
"aws_smithy_types::config_bag::storable::Storable",
|
||||
"aws_smithy_types::config_bag::storable::StoreReplace",
|
||||
|
|
|
@ -17,6 +17,7 @@ pub use aws_credential_types::provider::SharedCredentialsProvider;
|
|||
use aws_smithy_async::rt::sleep::AsyncSleep;
|
||||
pub use aws_smithy_async::rt::sleep::SharedAsyncSleep;
|
||||
pub use aws_smithy_async::time::{SharedTimeSource, TimeSource};
|
||||
use aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion;
|
||||
use aws_smithy_runtime_api::client::http::HttpClient;
|
||||
pub use aws_smithy_runtime_api::client::http::SharedHttpClient;
|
||||
use aws_smithy_runtime_api::client::identity::{ResolveCachedIdentity, SharedIdentityCache};
|
||||
|
@ -62,6 +63,7 @@ pub struct SdkConfig {
|
|||
http_client: Option<SharedHttpClient>,
|
||||
use_fips: Option<bool>,
|
||||
use_dual_stack: Option<bool>,
|
||||
behavior_major_version: Option<BehaviorMajorVersion>,
|
||||
}
|
||||
|
||||
/// Builder for AWS Shared Configuration
|
||||
|
@ -83,6 +85,7 @@ pub struct Builder {
|
|||
http_client: Option<SharedHttpClient>,
|
||||
use_fips: Option<bool>,
|
||||
use_dual_stack: Option<bool>,
|
||||
behavior_major_version: Option<BehaviorMajorVersion>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
|
@ -536,6 +539,21 @@ impl Builder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the [`BehaviorMajorVersion`] for the [`SdkConfig`]
|
||||
pub fn behavior_major_version(mut self, behavior_major_version: BehaviorMajorVersion) -> Self {
|
||||
self.set_behavior_major_version(Some(behavior_major_version));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`BehaviorMajorVersion`] for the [`SdkConfig`]
|
||||
pub fn set_behavior_major_version(
|
||||
&mut self,
|
||||
behavior_major_version: Option<BehaviorMajorVersion>,
|
||||
) -> &mut Self {
|
||||
self.behavior_major_version = behavior_major_version;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a [`SdkConfig`](SdkConfig) from this builder
|
||||
pub fn build(self) -> SdkConfig {
|
||||
SdkConfig {
|
||||
|
@ -551,6 +569,7 @@ impl Builder {
|
|||
use_fips: self.use_fips,
|
||||
use_dual_stack: self.use_dual_stack,
|
||||
time_source: self.time_source,
|
||||
behavior_major_version: self.behavior_major_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -617,6 +636,11 @@ impl SdkConfig {
|
|||
self.use_dual_stack
|
||||
}
|
||||
|
||||
/// Behavior major version configured for this client
|
||||
pub fn behavior_major_version(&self) -> Option<BehaviorMajorVersion> {
|
||||
self.behavior_major_version.clone()
|
||||
}
|
||||
|
||||
/// Config builder
|
||||
///
|
||||
/// _Important:_ Using the `aws-config` crate to configure the SDK is preferred to invoking this
|
||||
|
@ -646,6 +670,7 @@ impl SdkConfig {
|
|||
http_client: self.http_client,
|
||||
use_fips: self.use_fips,
|
||||
use_dual_stack: self.use_dual_stack,
|
||||
behavior_major_version: self.behavior_major_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenCon
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
aws-config = "$awsConfigVersion"
|
||||
aws-config = { version = "$awsConfigVersion", features = ["behavior-version-latest"] }
|
||||
$moduleName = "${codegenContext.settings.moduleVersion}"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
```
|
||||
|
|
|
@ -117,6 +117,8 @@ private class AwsFluentClientExtensions(private val codegenContext: ClientCodege
|
|||
/// the `sleep_impl` on the Config passed into this function to fix it.
|
||||
/// - This method will panic if the `sdk_config` is missing an HTTP connector. If you experience this panic, set the
|
||||
/// `http_connector` on the Config passed into this function to fix it.
|
||||
/// - This method will panic if no `BehaviorMajorVersion` is provided. If you experience this panic, set `behavior_major_version` on the Config or enable the `behavior-version-latest` Cargo feature.
|
||||
##[track_caller]
|
||||
pub fn new(sdk_config: &#{aws_types}::sdk_config::SdkConfig) -> Self {
|
||||
Self::from_conf(sdk_config.into())
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator {
|
|||
|
||||
${section.serviceConfigBuilder}.set_http_client(${section.sdkConfig}.http_client());
|
||||
${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source());
|
||||
${section.serviceConfigBuilder}.set_behavior_major_version(${section.sdkConfig}.behavior_major_version());
|
||||
|
||||
if let Some(cache) = ${section.sdkConfig}.identity_cache() {
|
||||
${section.serviceConfigBuilder}.set_identity_cache(cache);
|
||||
|
|
|
@ -5,20 +5,14 @@
|
|||
|
||||
package software.amazon.smithy.rustsdk
|
||||
|
||||
import SdkCodegenIntegrationTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
|
||||
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.testutil.integrationTest
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.tokioTest
|
||||
|
||||
internal class CredentialProviderConfigTest {
|
||||
@Test
|
||||
fun `generates a valid config`() {
|
||||
val codegenContext = awsTestCodegenContext()
|
||||
validateConfigCustomizations(codegenContext, CredentialProviderConfig(codegenContext))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `configuring credentials provider at operation level should work`() {
|
||||
awsSdkIntegrationTest(SdkCodegenIntegrationTest.model) { ctx, rustCrate ->
|
||||
|
|
|
@ -5,22 +5,18 @@
|
|||
|
||||
package software.amazon.smithy.rustsdk
|
||||
|
||||
import SdkCodegenIntegrationTest
|
||||
import org.junit.jupiter.api.Test
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.rustSettings
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
|
||||
|
||||
internal class RegionProviderConfigTest {
|
||||
@Test
|
||||
fun `generates a valid config`() {
|
||||
val project = TestWorkspace.testProject()
|
||||
val codegenContext = awsTestCodegenContext(
|
||||
settings = testClientRustSettings(
|
||||
moduleName = project.rustSettings().moduleName,
|
||||
runtimeConfig = AwsTestRuntimeConfig,
|
||||
),
|
||||
)
|
||||
validateConfigCustomizations(codegenContext, RegionProviderConfig(codegenContext), project)
|
||||
awsSdkIntegrationTest(SdkCodegenIntegrationTest.model) { _ctx, crate ->
|
||||
crate.unitTest {
|
||||
rustTemplate("let conf: Option<crate::Config> = None; let _reg: Option<crate::config::Region> = conf.and_then(|c|c.region().cloned());")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,27 +41,29 @@ fun awsSdkIntegrationTest(
|
|||
) =
|
||||
clientIntegrationTest(
|
||||
model,
|
||||
IntegrationTestParams(
|
||||
cargoCommand = "cargo test --features test-util",
|
||||
runtimeConfig = AwsTestRuntimeConfig,
|
||||
additionalSettings = ObjectNode.builder().withMember(
|
||||
"customizationConfig",
|
||||
ObjectNode.builder()
|
||||
.withMember(
|
||||
"awsSdk",
|
||||
ObjectNode.builder()
|
||||
.withMember("generateReadme", false)
|
||||
.withMember("integrationTestPath", "../sdk/integration-tests")
|
||||
.build(),
|
||||
).build(),
|
||||
)
|
||||
.withMember(
|
||||
"codegen",
|
||||
ObjectNode.builder()
|
||||
.withMember("includeFluentClient", false)
|
||||
.withMember("includeEndpointUrlConfig", false)
|
||||
.build(),
|
||||
).build(),
|
||||
),
|
||||
awsIntegrationTestParams(),
|
||||
test = test,
|
||||
)
|
||||
|
||||
fun awsIntegrationTestParams() = IntegrationTestParams(
|
||||
cargoCommand = "cargo test --features test-util behavior-version-latest",
|
||||
runtimeConfig = AwsTestRuntimeConfig,
|
||||
additionalSettings = ObjectNode.builder().withMember(
|
||||
"customizationConfig",
|
||||
ObjectNode.builder()
|
||||
.withMember(
|
||||
"awsSdk",
|
||||
ObjectNode.builder()
|
||||
.withMember("generateReadme", false)
|
||||
.withMember("integrationTestPath", "../sdk/integration-tests")
|
||||
.build(),
|
||||
).build(),
|
||||
)
|
||||
.withMember(
|
||||
"codegen",
|
||||
ObjectNode.builder()
|
||||
.withMember("includeFluentClient", false)
|
||||
.withMember("includeEndpointUrlConfig", false)
|
||||
.build(),
|
||||
).build(),
|
||||
)
|
||||
|
|
|
@ -15,7 +15,7 @@ approx = "0.5.1"
|
|||
aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-sdk-dynamodb = { path = "../../build/aws-sdk/sdk/dynamodb" }
|
||||
aws-sdk-dynamodb = { path = "../../build/aws-sdk/sdk/dynamodb", features = ["behavior-version-latest"] }
|
||||
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" }
|
||||
|
|
|
@ -12,7 +12,7 @@ aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" }
|
|||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["client"] }
|
||||
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
|
||||
aws-sdk-ec2 = { path = "../../build/aws-sdk/sdk/ec2" }
|
||||
aws-sdk-ec2 = { path = "../../build/aws-sdk/sdk/ec2", features = ["behavior-version-latest"] }
|
||||
tokio = { version = "1.23.1", features = ["full"]}
|
||||
http = "0.2.0"
|
||||
tokio-stream = "0.1.5"
|
||||
|
|
|
@ -13,7 +13,7 @@ publish = false
|
|||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http"}
|
||||
aws-sdk-glacier = { path = "../../build/aws-sdk/sdk/glacier" }
|
||||
aws-sdk-glacier = { path = "../../build/aws-sdk/sdk/glacier", features = ["behavior-version-latest"] }
|
||||
aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test"}
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
bytes = "1.0.0"
|
||||
|
|
|
@ -13,7 +13,7 @@ publish = false
|
|||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http"}
|
||||
aws-sdk-iam = { path = "../../build/aws-sdk/sdk/iam" }
|
||||
aws-sdk-iam = { path = "../../build/aws-sdk/sdk/iam", features = ["behavior-version-latest"] }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
|
||||
bytes = "1.0.0"
|
||||
|
|
|
@ -6,19 +6,17 @@
|
|||
use aws_sdk_iam::config::{Credentials, Region};
|
||||
use aws_smithy_runtime::client::http::test_util::capture_request;
|
||||
|
||||
// this test is ignored because pseudoregions have been removed. This test should be re-enabled
|
||||
// once FIPS support is added in aws-config
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn correct_endpoint_resolver() {
|
||||
let (http_client, request) = capture_request(None);
|
||||
let conf = aws_sdk_iam::Config::builder()
|
||||
.region(Region::from_static("iam-fips"))
|
||||
.credentials_provider(Credentials::for_tests())
|
||||
.use_fips(true)
|
||||
.region(Region::new("us-east-1"))
|
||||
.http_client(http_client)
|
||||
.build();
|
||||
let client = aws_sdk_iam::Client::from_conf(conf);
|
||||
let _ = client.list_roles().send().await;
|
||||
let _ = dbg!(client.list_roles().send().await);
|
||||
let req = request.expect_request();
|
||||
assert_eq!(&req.uri().to_string(), "https://iam-fips.amazonaws.com/");
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ test-util = []
|
|||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-runtime = { path = "../../build/aws-sdk/sdk/aws-runtime" }
|
||||
aws-sdk-kms = { path = "../../build/aws-sdk/sdk/kms", features = ["test-util"] }
|
||||
aws-sdk-kms = { path = "../../build/aws-sdk/sdk/kms", features = ["test-util", "behavior-version-latest"] }
|
||||
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
|
||||
|
|
|
@ -11,7 +11,7 @@ publish = false
|
|||
async-stream = "0.3.0"
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-sdk-lambda = { path = "../../build/aws-sdk/sdk/lambda" }
|
||||
aws-sdk-lambda = { path = "../../build/aws-sdk/sdk/lambda", features = ["behavior-version-latest"] }
|
||||
aws-smithy-eventstream = { path = "../../build/aws-sdk/sdk/aws-smithy-eventstream" }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
|
|
|
@ -22,3 +22,4 @@ aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types",
|
|||
futures = "0.3.25"
|
||||
tokio = { version = "1.23.1", features = ["full", "test-util"] }
|
||||
tracing-subscriber = { version = "0.3.15", features = ["env-filter"] }
|
||||
http = "0.2.9"
|
||||
|
|
|
@ -4,10 +4,14 @@
|
|||
*/
|
||||
|
||||
use aws_sdk_s3::config::IdentityCache;
|
||||
|
||||
use aws_sdk_s3::config::{
|
||||
retry::RetryConfig, timeout::TimeoutConfig, Config, Credentials, Region, SharedAsyncSleep,
|
||||
Sleep,
|
||||
retry::RetryConfig, timeout::TimeoutConfig, BehaviorMajorVersion, Config, Credentials, Region,
|
||||
SharedAsyncSleep, Sleep,
|
||||
};
|
||||
use aws_sdk_s3::primitives::SdkBody;
|
||||
use aws_smithy_runtime::client::http::test_util::infallible_client_fn;
|
||||
|
||||
use aws_sdk_s3::error::DisplayErrorContext;
|
||||
use aws_smithy_async::rt::sleep::AsyncSleep;
|
||||
use aws_smithy_runtime::client::http::test_util::capture_request;
|
||||
|
@ -22,7 +26,7 @@ use std::time::Duration;
|
|||
expected = "Enable the `rustls` crate feature or configure a HTTP client to fix this."
|
||||
)]
|
||||
async fn test_clients_from_sdk_config() {
|
||||
aws_config::load_from_env().await;
|
||||
aws_config::load_from_env_with_version(BehaviorMajorVersion::latest()).await;
|
||||
}
|
||||
|
||||
// This will fail due to lack of a connector when constructing the service client
|
||||
|
@ -42,6 +46,7 @@ async fn test_clients_from_service_config() {
|
|||
.region(Region::new("us-east-1"))
|
||||
.credentials_provider(Credentials::for_tests())
|
||||
.sleep_impl(SharedAsyncSleep::new(StubSleep))
|
||||
.behavior_major_version(BehaviorMajorVersion::latest())
|
||||
.build();
|
||||
// Creating the client shouldn't panic or error since presigning doesn't require a connector
|
||||
let client = aws_sdk_s3::Client::from_conf(config);
|
||||
|
@ -58,6 +63,23 @@ async fn test_clients_from_service_config() {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "Invalid client configuration: A behavior major version must be set")]
|
||||
async fn test_missing_behavior_major_version() {
|
||||
use aws_sdk_s3::config::Region;
|
||||
let http_client =
|
||||
infallible_client_fn(|_req| http::Response::builder().body(SdkBody::empty()).unwrap());
|
||||
|
||||
let config = Config::builder()
|
||||
.region(Region::new("us-east-1"))
|
||||
.identity_cache(IdentityCache::no_cache())
|
||||
.credentials_provider(Credentials::for_tests())
|
||||
.http_client(http_client)
|
||||
.build();
|
||||
// This line panics
|
||||
let _client = aws_sdk_s3::Client::from_conf(config);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(
|
||||
expected = "Invalid client configuration: An async sleep implementation is required for retry to work."
|
||||
|
@ -73,6 +95,7 @@ async fn test_missing_async_sleep_time_source_retries() {
|
|||
.credentials_provider(Credentials::for_tests())
|
||||
.retry_config(RetryConfig::standard())
|
||||
.timeout_config(TimeoutConfig::disabled())
|
||||
.behavior_major_version(BehaviorMajorVersion::latest())
|
||||
.build();
|
||||
|
||||
// should panic with a validation error
|
||||
|
@ -93,6 +116,7 @@ async fn test_missing_async_sleep_time_source_timeouts() {
|
|||
.region(Region::new("us-east-1"))
|
||||
.credentials_provider(Credentials::for_tests())
|
||||
.retry_config(RetryConfig::disabled())
|
||||
.behavior_major_version(BehaviorMajorVersion::latest())
|
||||
.timeout_config(
|
||||
TimeoutConfig::builder()
|
||||
.operation_timeout(Duration::from_secs(5))
|
||||
|
@ -120,8 +144,60 @@ async fn test_time_source_for_identity_cache() {
|
|||
.credentials_provider(Credentials::for_tests())
|
||||
.retry_config(RetryConfig::disabled())
|
||||
.timeout_config(TimeoutConfig::disabled())
|
||||
.behavior_major_version(BehaviorMajorVersion::latest())
|
||||
.build();
|
||||
|
||||
// should panic with a validation error
|
||||
let _client = aws_sdk_s3::Client::from_conf(config);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn behavior_mv_from_aws_config() {
|
||||
let (http_client, req) = capture_request(None);
|
||||
let cfg = aws_config::from_env_with_version(BehaviorMajorVersion::v2023_11_09())
|
||||
.http_client(http_client)
|
||||
.retry_config(RetryConfig::disabled())
|
||||
.credentials_provider(Credentials::for_tests())
|
||||
.identity_cache(IdentityCache::no_cache())
|
||||
.timeout_config(TimeoutConfig::disabled())
|
||||
.region(Region::new("us-west-2"))
|
||||
.load()
|
||||
.await;
|
||||
let s3_client = aws_sdk_s3::Client::new(&cfg);
|
||||
let _err = s3_client
|
||||
.list_buckets()
|
||||
.send()
|
||||
.await
|
||||
.expect_err("it should fail to send a request because there is no HTTP client");
|
||||
assert_eq!(
|
||||
req.expect_request().uri(),
|
||||
"https://s3.us-west-2.amazonaws.com/"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn behavior_mv_from_client_construction() {
|
||||
let (http_client, req) = capture_request(None);
|
||||
let cfg = aws_config::SdkConfig::builder()
|
||||
.http_client(http_client)
|
||||
.retry_config(RetryConfig::disabled())
|
||||
.identity_cache(IdentityCache::no_cache())
|
||||
.timeout_config(TimeoutConfig::disabled())
|
||||
.region(Region::new("us-west-2"))
|
||||
.build();
|
||||
let s3_client = aws_sdk_s3::Client::from_conf(
|
||||
aws_sdk_s3::config::Builder::from(&cfg)
|
||||
.credentials_provider(Credentials::for_tests())
|
||||
.behavior_major_version(aws_sdk_s3::config::BehaviorMajorVersion::v2023_11_09())
|
||||
.build(),
|
||||
);
|
||||
let _err = dbg!(s3_client
|
||||
.list_buckets()
|
||||
.send()
|
||||
.await
|
||||
.expect_err("it should fail to send a request because there is no HTTP client"));
|
||||
assert_eq!(
|
||||
req.expect_request().uri(),
|
||||
"https://s3.us-west-2.amazonaws.com/"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ publish = false
|
|||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http"}
|
||||
aws-sdk-polly = { path = "../../build/aws-sdk/sdk/polly" }
|
||||
aws-sdk-polly = { path = "../../build/aws-sdk/sdk/polly", features = ["behavior-version-latest"] }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
bytes = "1.0.0"
|
||||
http = "0.2.0"
|
||||
|
|
|
@ -17,7 +17,7 @@ test-util = []
|
|||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-sdk-qldbsession = { path = "../../build/aws-sdk/sdk/qldbsession", features = ["test-util"] }
|
||||
aws-sdk-qldbsession = { path = "../../build/aws-sdk/sdk/qldbsession", features = ["test-util", "behavior-version-latest"] }
|
||||
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
|
|
|
@ -16,12 +16,12 @@ test-util = []
|
|||
|
||||
[dev-dependencies]
|
||||
async-std = "1.12.0"
|
||||
aws-config = { path = "../../build/aws-sdk/sdk/aws-config" }
|
||||
aws-config = { path = "../../build/aws-sdk/sdk/aws-config", features = ["behavior-version-latest"] }
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-runtime = { path = "../../build/aws-sdk/sdk/aws-runtime", features = ["test-util"] }
|
||||
aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", features = ["test-util"] }
|
||||
aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" }
|
||||
aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", features = ["test-util", "behavior-version-latest"] }
|
||||
# aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" }
|
||||
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util", "rt-tokio"] }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" }
|
||||
|
@ -32,7 +32,7 @@ aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
|
|||
bytes = "1"
|
||||
bytes-utils = "0.1.2"
|
||||
fastrand = "2.0.1"
|
||||
futures-util = { version = "0.3.16" }
|
||||
futures-util = { version = "0.3.16", default-features = false, features = ["alloc"] }
|
||||
hdrhistogram = "7.5.2"
|
||||
http = "0.2.3"
|
||||
http-body = "0.4.5"
|
||||
|
|
|
@ -17,6 +17,7 @@ fn test_client(update_builder: fn(Builder) -> Builder) -> (CaptureRequestReceive
|
|||
.credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests()))
|
||||
.region(Region::new("us-west-4"))
|
||||
.http_client(http_client)
|
||||
.behavior_major_version(aws_sdk_s3::config::BehaviorMajorVersion::latest())
|
||||
.with_test_defaults();
|
||||
let client = Client::from_conf(update_builder(config).build());
|
||||
(captured_request, client)
|
||||
|
|
|
@ -17,7 +17,7 @@ test-util = []
|
|||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-sdk-s3control = { path = "../../build/aws-sdk/sdk/s3control", features = ["test-util"] }
|
||||
aws-sdk-s3control = { path = "../../build/aws-sdk/sdk/s3control", features = ["test-util", "behavior-version-latest"] }
|
||||
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
|
||||
|
|
|
@ -12,7 +12,7 @@ publish = false
|
|||
|
||||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" }
|
||||
aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts", features = ["behavior-version-latest"] }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" }
|
||||
|
|
|
@ -11,6 +11,6 @@ for f in *; do
|
|||
echo
|
||||
echo "Testing ${f}..."
|
||||
echo "###############"
|
||||
cargo test --manifest-path "${f}/Cargo.toml"
|
||||
cargo test --manifest-path "${f}/Cargo.toml" --all-features
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -12,7 +12,7 @@ publish = false
|
|||
|
||||
[dev-dependencies]
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-sdk-timestreamquery = { path = "../../build/aws-sdk/sdk/timestreamquery" }
|
||||
aws-sdk-timestreamquery = { path = "../../build/aws-sdk/sdk/timestreamquery", features = ["behavior-version-latest"] }
|
||||
aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"] }
|
||||
aws-types = { path = "../../build/aws-sdk/sdk/aws-types" }
|
||||
|
|
|
@ -12,7 +12,7 @@ publish = false
|
|||
async-stream = "0.3.0"
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
|
||||
aws-http = { path = "../../build/aws-sdk/sdk/aws-http" }
|
||||
aws-sdk-transcribestreaming = { path = "../../build/aws-sdk/sdk/transcribestreaming" }
|
||||
aws-sdk-transcribestreaming = { path = "../../build/aws-sdk/sdk/transcribestreaming", features = ["behavior-version-latest"] }
|
||||
aws-smithy-eventstream = { path = "../../build/aws-sdk/sdk/aws-smithy-eventstream" }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "test-util"] }
|
||||
|
|
|
@ -18,7 +18,7 @@ crate-type = ["cdylib"]
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false, features = ["rt-tokio"]}
|
||||
aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false, features = ["rt-tokio", "behavior-version-latest"]}
|
||||
aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["hardcoded-credentials"] }
|
||||
aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", default-features = false }
|
||||
aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" }
|
||||
|
|
|
@ -36,6 +36,7 @@ internal class EndpointConfigCustomization(
|
|||
"SharedEndpointResolver" to epModule.resolve("SharedEndpointResolver"),
|
||||
"StaticUriEndpointResolver" to epRuntimeModule.resolve("StaticUriEndpointResolver"),
|
||||
"ServiceSpecificResolver" to codegenContext.serviceSpecificEndpointResolver(),
|
||||
"IntoShared" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("shared::IntoShared"),
|
||||
)
|
||||
|
||||
override fun section(section: ServiceConfig): Writable {
|
||||
|
@ -89,7 +90,7 @@ internal class EndpointConfigCustomization(
|
|||
##[allow(deprecated)]
|
||||
self.set_endpoint_resolver(
|
||||
endpoint_url.map(|url| {
|
||||
#{StaticUriEndpointResolver}::uri(url).into_shared()
|
||||
#{IntoShared}::into_shared(#{StaticUriEndpointResolver}::uri(url))
|
||||
})
|
||||
);
|
||||
self
|
||||
|
|
|
@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Attribute
|
|||
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.EscapeFor
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.Feature
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustType
|
||||
|
@ -31,6 +32,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docLink
|
|||
import software.amazon.smithy.rust.codegen.core.rustlang.docs
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.documentShape
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.escape
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.featureGatedBlock
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.implBlock
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.normalizeHtml
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.qualifiedName
|
||||
|
@ -57,10 +59,12 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape
|
|||
import software.amazon.smithy.rust.codegen.core.util.sdkId
|
||||
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
|
||||
|
||||
private val BehaviorVersionLatest = Feature("behavior-version-latest", false, listOf())
|
||||
class FluentClientGenerator(
|
||||
private val codegenContext: ClientCodegenContext,
|
||||
private val customizations: List<FluentClientCustomization> = emptyList(),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun clientOperationFnName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String =
|
||||
RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operationShape).name.toSnakeCase())
|
||||
|
@ -94,6 +98,7 @@ class FluentClientGenerator(
|
|||
}
|
||||
|
||||
private fun renderFluentClient(crate: RustCrate) {
|
||||
crate.mergeFeature(BehaviorVersionLatest)
|
||||
crate.withModule(ClientRustModule.client) {
|
||||
rustTemplate(
|
||||
"""
|
||||
|
@ -119,8 +124,10 @@ class FluentClientGenerator(
|
|||
///
|
||||
/// - Retries or timeouts are enabled without a `sleep_impl` configured.
|
||||
/// - Identity caching is enabled without a `sleep_impl` and `time_source` configured.
|
||||
/// - No `behavior_major_version` is provided.
|
||||
///
|
||||
/// The panic message for each of these will have instructions on how to resolve them.
|
||||
##[track_caller]
|
||||
pub fn from_conf(conf: crate::Config) -> Self {
|
||||
let handle = Handle {
|
||||
conf: conf.clone(),
|
||||
|
@ -451,6 +458,9 @@ private fun baseClientRuntimePluginsFn(codegenContext: ClientCodegenContext): Ru
|
|||
RuntimeType.forInlineFun("base_client_runtime_plugins", ClientRustModule.config) {
|
||||
val api = RuntimeType.smithyRuntimeApiClient(rc)
|
||||
val rt = RuntimeType.smithyRuntime(rc)
|
||||
val behaviorVersionError = "Invalid client configuration: A behavior major version must be set when sending a " +
|
||||
"request or constructing a client. You must set it during client construction or by enabling the " +
|
||||
"`${BehaviorVersionLatest.name}` cargo feature."
|
||||
rustTemplate(
|
||||
"""
|
||||
pub(crate) fn base_client_runtime_plugins(
|
||||
|
@ -458,12 +468,16 @@ private fun baseClientRuntimePluginsFn(codegenContext: ClientCodegenContext): Ru
|
|||
) -> #{RuntimePlugins} {
|
||||
let mut configured_plugins = #{Vec}::new();
|
||||
::std::mem::swap(&mut config.runtime_plugins, &mut configured_plugins);
|
||||
##[allow(unused_mut)]
|
||||
let mut behavior_major_version = config.behavior_major_version.clone();
|
||||
#{update_bmv}
|
||||
|
||||
let mut plugins = #{RuntimePlugins}::new()
|
||||
// defaults
|
||||
.with_client_plugins(#{default_plugins}(
|
||||
#{DefaultPluginParams}::new()
|
||||
.with_retry_partition_name(${codegenContext.serviceShape.sdkId().dq()})
|
||||
.with_behavior_major_version(behavior_major_version.expect(${behaviorVersionError.dq()}))
|
||||
))
|
||||
// user config
|
||||
.with_client_plugin(
|
||||
|
@ -474,6 +488,7 @@ private fun baseClientRuntimePluginsFn(codegenContext: ClientCodegenContext): Ru
|
|||
// codegen config
|
||||
.with_client_plugin(crate::config::ServiceRuntimePlugin::new(config))
|
||||
.with_client_plugin(#{NoAuthRuntimePlugin}::new());
|
||||
|
||||
for plugin in configured_plugins {
|
||||
plugins = plugins.with_client_plugin(plugin);
|
||||
}
|
||||
|
@ -486,6 +501,17 @@ private fun baseClientRuntimePluginsFn(codegenContext: ClientCodegenContext): Ru
|
|||
"NoAuthRuntimePlugin" to rt.resolve("client::auth::no_auth::NoAuthRuntimePlugin"),
|
||||
"RuntimePlugins" to RuntimeType.runtimePlugins(rc),
|
||||
"StaticRuntimePlugin" to api.resolve("client::runtime_plugin::StaticRuntimePlugin"),
|
||||
"update_bmv" to featureGatedBlock(BehaviorVersionLatest) {
|
||||
rustTemplate(
|
||||
"""
|
||||
if behavior_major_version.is_none() {
|
||||
behavior_major_version = Some(#{BehaviorMajorVersion}::latest());
|
||||
}
|
||||
|
||||
""",
|
||||
"BehaviorMajorVersion" to api.resolve("client::behavior_version::BehaviorMajorVersion"),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,37 +189,38 @@ fun loadFromConfigBag(innerTypeName: String, newtype: RuntimeType): Writable = w
|
|||
* 2. convenience setter (non-optional)
|
||||
* 3. standard setter (&mut self)
|
||||
*/
|
||||
fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext): ConfigCustomization = object : ConfigCustomization() {
|
||||
override fun section(section: ServiceConfig): Writable {
|
||||
return when (section) {
|
||||
ServiceConfig.BuilderImpl -> writable {
|
||||
docsOrFallback(param.setterDocs)
|
||||
rust(
|
||||
"""
|
||||
pub fn ${param.name}(mut self, ${param.name}: impl Into<#T>) -> Self {
|
||||
self.set_${param.name}(Some(${param.name}.into()));
|
||||
self
|
||||
fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext): ConfigCustomization =
|
||||
object : ConfigCustomization() {
|
||||
override fun section(section: ServiceConfig): Writable {
|
||||
return when (section) {
|
||||
ServiceConfig.BuilderImpl -> writable {
|
||||
docsOrFallback(param.setterDocs)
|
||||
rust(
|
||||
"""
|
||||
pub fn ${param.name}(mut self, ${param.name}: impl Into<#T>) -> Self {
|
||||
self.set_${param.name}(Some(${param.name}.into()));
|
||||
self
|
||||
}""",
|
||||
param.type,
|
||||
)
|
||||
param.type,
|
||||
)
|
||||
|
||||
docsOrFallback(param.setterDocs)
|
||||
rustTemplate(
|
||||
"""
|
||||
pub fn set_${param.name}(&mut self, ${param.name}: Option<#{T}>) -> &mut Self {
|
||||
self.config.store_or_unset(${param.name}.map(#{newtype}));
|
||||
self
|
||||
}
|
||||
""",
|
||||
"T" to param.type,
|
||||
"newtype" to param.newtype!!,
|
||||
)
|
||||
docsOrFallback(param.setterDocs)
|
||||
rustTemplate(
|
||||
"""
|
||||
pub fn set_${param.name}(&mut self, ${param.name}: Option<#{T}>) -> &mut Self {
|
||||
self.config.store_or_unset(${param.name}.map(#{newtype}));
|
||||
self
|
||||
}
|
||||
""",
|
||||
"T" to param.type,
|
||||
"newtype" to param.newtype!!,
|
||||
)
|
||||
}
|
||||
|
||||
else -> emptySection
|
||||
}
|
||||
|
||||
else -> emptySection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ServiceShape.needsIdempotencyToken(model: Model): Boolean {
|
||||
val operationIndex = OperationIndex.of(model)
|
||||
|
@ -279,8 +280,69 @@ class ServiceConfigGenerator(
|
|||
"RuntimePlugin" to configReexport(RuntimeType.runtimePlugin(runtimeConfig)),
|
||||
"SharedRuntimePlugin" to configReexport(RuntimeType.sharedRuntimePlugin(runtimeConfig)),
|
||||
"runtime_plugin" to RuntimeType.smithyRuntimeApiClient(runtimeConfig).resolve("client::runtime_plugin"),
|
||||
"BehaviorMajorVersion" to configReexport(
|
||||
RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::behavior_version::BehaviorMajorVersion"),
|
||||
),
|
||||
)
|
||||
|
||||
private fun behaviorMv() = writable {
|
||||
val docs = """
|
||||
/// Sets the [`behavior major version`](crate::config::BehaviorMajorVersion).
|
||||
///
|
||||
/// Over time, new best-practice behaviors are introduced. However, these behaviors might not be backwards
|
||||
/// compatible. For example, a change which introduces new default timeouts or a new retry-mode for
|
||||
/// all operations might be the ideal behavior but could break existing applications.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// Set the behavior major version to `latest`. This is equivalent to enabling the `behavior-version-latest` cargo feature.
|
||||
/// ```no_run
|
||||
/// use $moduleUseName::config::BehaviorMajorVersion;
|
||||
///
|
||||
/// let config = $moduleUseName::Config::builder()
|
||||
/// .behavior_major_version(BehaviorMajorVersion::latest())
|
||||
/// // ...
|
||||
/// .build();
|
||||
/// let client = $moduleUseName::Client::from_conf(config);
|
||||
/// ```
|
||||
///
|
||||
/// Customizing behavior major version:
|
||||
/// ```no_run
|
||||
/// use $moduleUseName::config::BehaviorMajorVersion;
|
||||
///
|
||||
/// let config = $moduleUseName::Config::builder()
|
||||
/// .behavior_major_version(BehaviorMajorVersion::v2023_11_09())
|
||||
/// // ...
|
||||
/// .build();
|
||||
/// let client = $moduleUseName::Client::from_conf(config);
|
||||
/// ```
|
||||
"""
|
||||
rustTemplate(
|
||||
"""
|
||||
$docs
|
||||
pub fn behavior_major_version(mut self, behavior_major_version: crate::config::BehaviorMajorVersion) -> Self {
|
||||
self.set_behavior_major_version(Some(behavior_major_version));
|
||||
self
|
||||
}
|
||||
|
||||
$docs
|
||||
pub fn set_behavior_major_version(&mut self, behavior_major_version: Option<crate::config::BehaviorMajorVersion>) -> &mut Self {
|
||||
self.behavior_major_version = behavior_major_version;
|
||||
self
|
||||
}
|
||||
|
||||
/// Convenience method to set the latest behavior major version
|
||||
///
|
||||
/// This is equivalent to enabling the `behavior-version-latest` Cargo feature
|
||||
pub fn behavior_major_version_latest(mut self) -> Self {
|
||||
self.set_behavior_major_version(Some(crate::config::BehaviorMajorVersion::latest()));
|
||||
self
|
||||
}
|
||||
""",
|
||||
*codegenScope,
|
||||
)
|
||||
}
|
||||
|
||||
fun render(writer: RustWriter) {
|
||||
writer.docs("Configuration for a $moduleUseName service client.\n")
|
||||
customizations.forEach {
|
||||
|
@ -297,6 +359,7 @@ class ServiceConfigGenerator(
|
|||
cloneable: #{CloneableLayer},
|
||||
pub(crate) runtime_components: #{RuntimeComponentsBuilder},
|
||||
pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>,
|
||||
behavior_major_version: #{Option}<#{BehaviorMajorVersion}>,
|
||||
""",
|
||||
*codegenScope,
|
||||
)
|
||||
|
@ -320,6 +383,7 @@ class ServiceConfigGenerator(
|
|||
config: self.cloneable.clone(),
|
||||
runtime_components: self.runtime_components.clone(),
|
||||
runtime_plugins: self.runtime_plugins.clone(),
|
||||
behavior_major_version: self.behavior_major_version.clone(),
|
||||
}
|
||||
}
|
||||
""",
|
||||
|
@ -337,6 +401,7 @@ class ServiceConfigGenerator(
|
|||
pub(crate) config: #{CloneableLayer},
|
||||
pub(crate) runtime_components: #{RuntimeComponentsBuilder},
|
||||
pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>,
|
||||
pub(crate) behavior_major_version: #{Option}<#{BehaviorMajorVersion}>,
|
||||
""",
|
||||
*codegenScope,
|
||||
)
|
||||
|
@ -354,6 +419,7 @@ class ServiceConfigGenerator(
|
|||
config: #{Default}::default(),
|
||||
runtime_components: #{RuntimeComponentsBuilder}::new("service config"),
|
||||
runtime_plugins: #{Default}::default(),
|
||||
behavior_major_version: #{Default}::default(),
|
||||
}
|
||||
}
|
||||
""",
|
||||
|
@ -367,11 +433,18 @@ class ServiceConfigGenerator(
|
|||
customizations.forEach {
|
||||
it.section(ServiceConfig.BuilderImpl)(this)
|
||||
}
|
||||
behaviorMv()(this)
|
||||
|
||||
val visibility = if (enableUserConfigurableRuntimePlugins) { "pub" } else { "pub(crate)" }
|
||||
val visibility = if (enableUserConfigurableRuntimePlugins) {
|
||||
"pub"
|
||||
} else {
|
||||
"pub(crate)"
|
||||
}
|
||||
|
||||
docs("Adds a runtime plugin to the config.")
|
||||
if (!enableUserConfigurableRuntimePlugins) { Attribute.AllowUnused.render(this) }
|
||||
if (!enableUserConfigurableRuntimePlugins) {
|
||||
Attribute.AllowUnused.render(this)
|
||||
}
|
||||
rustTemplate(
|
||||
"""
|
||||
$visibility fn runtime_plugin(mut self, plugin: impl #{RuntimePlugin} + 'static) -> Self {
|
||||
|
@ -382,7 +455,9 @@ class ServiceConfigGenerator(
|
|||
*codegenScope,
|
||||
)
|
||||
docs("Adds a runtime plugin to the config.")
|
||||
if (!enableUserConfigurableRuntimePlugins) { Attribute.AllowUnused.render(this) }
|
||||
if (!enableUserConfigurableRuntimePlugins) {
|
||||
Attribute.AllowUnused.render(this)
|
||||
}
|
||||
rustTemplate(
|
||||
"""
|
||||
$visibility fn push_runtime_plugin(&mut self, plugin: #{SharedRuntimePlugin}) -> &mut Self {
|
||||
|
@ -433,6 +508,7 @@ class ServiceConfigGenerator(
|
|||
cloneable: layer,
|
||||
runtime_components: self.runtime_components,
|
||||
runtime_plugins: self.runtime_plugins,
|
||||
behavior_major_version: self.behavior_major_version,
|
||||
""",
|
||||
*codegenScope,
|
||||
)
|
||||
|
|
|
@ -18,7 +18,7 @@ import java.nio.file.Path
|
|||
|
||||
fun clientIntegrationTest(
|
||||
model: Model,
|
||||
params: IntegrationTestParams = IntegrationTestParams(),
|
||||
params: IntegrationTestParams = IntegrationTestParams(cargoCommand = "cargo test --features behavior-version-latest"),
|
||||
additionalDecorators: List<ClientCodegenDecorator> = listOf(),
|
||||
test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> },
|
||||
): Path {
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package software.amazon.smithy.rust.codegen.client.testutil
|
||||
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype
|
||||
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.rustTemplate
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.writable
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
|
||||
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
|
||||
|
||||
/**
|
||||
* Test helper to produce a valid config customization to test that a [ConfigCustomization] can be used in conjunction
|
||||
* with other [ConfigCustomization]s.
|
||||
*/
|
||||
fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): ConfigCustomization {
|
||||
return object : ConfigCustomization() {
|
||||
override fun section(section: ServiceConfig): Writable = writable {
|
||||
when (section) {
|
||||
ServiceConfig.ConfigImpl -> {
|
||||
rustTemplate(
|
||||
"""
|
||||
##[allow(missing_docs)]
|
||||
pub fn $name(&self) -> u64 {
|
||||
self.config.load::<#{T}>().map(|u| u.0).unwrap()
|
||||
}
|
||||
""",
|
||||
"T" to configParamNewtype(
|
||||
"_$name".toPascalCase(), RuntimeType.U64.toSymbol(),
|
||||
codegenContext.runtimeConfig,
|
||||
),
|
||||
)
|
||||
}
|
||||
ServiceConfig.BuilderImpl -> {
|
||||
rustTemplate(
|
||||
"""
|
||||
/// docs!
|
||||
pub fn $name(mut self, $name: u64) -> Self {
|
||||
self.config.store_put(#{T}($name));
|
||||
self
|
||||
}
|
||||
""",
|
||||
"T" to configParamNewtype(
|
||||
"_$name".toPascalCase(), RuntimeType.U64.toSymbol(),
|
||||
codegenContext.runtimeConfig,
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> emptySection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Basic validation of [ConfigCustomization]s
|
||||
*
|
||||
* This test is not comprehensive, but it ensures that your customization generates Rust code that compiles and correctly
|
||||
* composes with other customizations.
|
||||
* */
|
||||
@Suppress("NAME_SHADOWING")
|
||||
fun validateConfigCustomizations(
|
||||
codegenContext: ClientCodegenContext,
|
||||
customization: ConfigCustomization,
|
||||
project: TestWriterDelegator? = null,
|
||||
): TestWriterDelegator {
|
||||
val project = project ?: TestWorkspace.testProject()
|
||||
stubConfigProject(codegenContext, customization, project)
|
||||
project.compileAndTest()
|
||||
return project
|
||||
}
|
||||
|
||||
fun stubConfigProject(codegenContext: ClientCodegenContext, customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator {
|
||||
val customizations = listOf(stubConfigCustomization("a", codegenContext)) + customization + stubConfigCustomization("b", codegenContext)
|
||||
val generator = ServiceConfigGenerator(codegenContext, customizations = customizations.toList())
|
||||
project.withModule(ClientRustModule.config) {
|
||||
generator.render(this)
|
||||
unitTest(
|
||||
"config_send_sync",
|
||||
"""
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
assert_send_sync::<Config>();
|
||||
""",
|
||||
)
|
||||
}
|
||||
project.lib { rust("pub use config::Config;") }
|
||||
return project
|
||||
}
|
|
@ -99,3 +99,5 @@ fun TestWriterDelegator.clientRustSettings() =
|
|||
moduleName = "test_${baseDir.toFile().nameWithoutExtension}",
|
||||
codegenConfig = codegenConfig as ClientCodegenConfig,
|
||||
)
|
||||
|
||||
fun TestWriterDelegator.clientCodegenContext(model: Model) = testClientCodegenContext(model, settings = clientRustSettings())
|
||||
|
|
|
@ -11,29 +11,15 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
|
|||
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.BasicTestModels
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.testModule
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.tokioTest
|
||||
|
||||
class MetadataCustomizationTest {
|
||||
private val model = """
|
||||
namespace com.example
|
||||
use aws.protocols#awsJson1_0
|
||||
@awsJson1_0
|
||||
service HelloService {
|
||||
operations: [SayHello],
|
||||
version: "1"
|
||||
}
|
||||
@optionalAuth
|
||||
operation SayHello { input: TestInput }
|
||||
structure TestInput {
|
||||
foo: String,
|
||||
}
|
||||
""".asSmithyModel()
|
||||
|
||||
@Test
|
||||
fun `extract metadata via customizable operation`() {
|
||||
clientIntegrationTest(model) { clientCodegenContext, rustCrate ->
|
||||
clientIntegrationTest(BasicTestModels.AwsJson10TestModel) { clientCodegenContext, rustCrate ->
|
||||
val runtimeConfig = clientCodegenContext.runtimeConfig
|
||||
val codegenScope = arrayOf(
|
||||
*preludeScope,
|
||||
|
|
|
@ -6,49 +6,25 @@
|
|||
package software.amazon.smithy.rust.codegen.client.smithy.customizations
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenConfig
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginGenerator
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.clientRustSettings
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.BasicTestModels
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
|
||||
|
||||
internal class ResiliencyConfigCustomizationTest {
|
||||
private val baseModel = """
|
||||
namespace test
|
||||
use aws.protocols#awsQuery
|
||||
|
||||
structure SomeOutput {
|
||||
@xmlAttribute
|
||||
someAttribute: Long,
|
||||
|
||||
someVal: String
|
||||
}
|
||||
|
||||
operation SomeOperation {
|
||||
output: SomeOutput
|
||||
}
|
||||
""".asSmithyModel()
|
||||
|
||||
@Test
|
||||
fun `generates a valid config`() {
|
||||
val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(baseModel))
|
||||
val project = TestWorkspace.testProject(model, ClientCodegenConfig())
|
||||
val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings())
|
||||
|
||||
stubConfigProject(codegenContext, ResiliencyConfigCustomization(codegenContext), project)
|
||||
project.withModule(ClientRustModule.config) {
|
||||
ServiceRuntimePluginGenerator(codegenContext).render(
|
||||
this,
|
||||
emptyList(),
|
||||
)
|
||||
clientIntegrationTest(BasicTestModels.AwsJson10TestModel) { _, crate ->
|
||||
crate.unitTest("resiliency_fields") {
|
||||
rustTemplate(
|
||||
"""
|
||||
let mut conf = crate::Config::builder();
|
||||
conf.set_sleep_impl(None);
|
||||
conf.set_retry_config(None);
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
ResiliencyReExportCustomization(codegenContext).extras(project)
|
||||
project.compileAndTest()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,8 @@
|
|||
package software.amazon.smithy.rust.codegen.client.smithy.endpoint
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
|
||||
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.testutil.TestWorkspace
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
|
||||
|
||||
|
@ -18,6 +15,7 @@ class ClientContextConfigCustomizationTest {
|
|||
val model = """
|
||||
namespace test
|
||||
use smithy.rules#clientContextParams
|
||||
use aws.protocols#awsJson1_0
|
||||
|
||||
@clientContextParams(aStringParam: {
|
||||
documentation: "string docs",
|
||||
|
@ -27,58 +25,54 @@ class ClientContextConfigCustomizationTest {
|
|||
documentation: "bool docs",
|
||||
type: "boolean"
|
||||
})
|
||||
@awsJson1_0
|
||||
service TestService { operations: [] }
|
||||
""".asSmithyModel()
|
||||
|
||||
@Test
|
||||
fun `client params generate a valid customization`() {
|
||||
val project = TestWorkspace.testProject()
|
||||
val context = testClientCodegenContext(model)
|
||||
project.unitTest {
|
||||
rustTemplate(
|
||||
"""
|
||||
use #{RuntimePlugin};
|
||||
let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build();
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::AStringParam>()
|
||||
.map(|u| u.0.clone())
|
||||
.unwrap(),
|
||||
"hello!"
|
||||
);
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::ABoolParam>()
|
||||
.map(|u| u.0),
|
||||
Some(true)
|
||||
);
|
||||
""",
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(context.runtimeConfig),
|
||||
)
|
||||
clientIntegrationTest(model) { _, crate ->
|
||||
crate.unitTest {
|
||||
rustTemplate(
|
||||
"""
|
||||
let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build();
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::AStringParam>()
|
||||
.map(|u| u.0.clone())
|
||||
.unwrap(),
|
||||
"hello!"
|
||||
);
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::ABoolParam>()
|
||||
.map(|u| u.0),
|
||||
Some(true)
|
||||
);
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
crate.unitTest("unset_fields") {
|
||||
rustTemplate(
|
||||
"""
|
||||
let conf = crate::Config::builder().a_string_param("hello!").build();
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::AStringParam>()
|
||||
.map(|u| u.0.clone())
|
||||
.unwrap(),
|
||||
"hello!"
|
||||
);
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::ABoolParam>()
|
||||
.map(|u| u.0),
|
||||
None,
|
||||
);
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
// unset fields
|
||||
project.unitTest {
|
||||
rustTemplate(
|
||||
"""
|
||||
use #{RuntimePlugin};
|
||||
let conf = crate::Config::builder().a_string_param("hello!").build();
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::AStringParam>()
|
||||
.map(|u| u.0.clone())
|
||||
.unwrap(),
|
||||
"hello!"
|
||||
);
|
||||
assert_eq!(
|
||||
conf.config
|
||||
.load::<crate::config::ABoolParam>()
|
||||
.map(|u| u.0),
|
||||
None,
|
||||
);
|
||||
""",
|
||||
"RuntimePlugin" to RuntimeType.runtimePlugin(context.runtimeConfig),
|
||||
)
|
||||
}
|
||||
validateConfigCustomizations(context, ClientContextConfigCustomization(context), project)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ class EndpointsDecoratorTest {
|
|||
val testDir = clientIntegrationTest(
|
||||
model,
|
||||
// Just run integration tests.
|
||||
IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }),
|
||||
IntegrationTestParams(command = { "cargo test --all-features --test *".runWithWarnings(it) }),
|
||||
) { clientCodegenContext, rustCrate ->
|
||||
rustCrate.integrationTest("endpoint_params_test") {
|
||||
val moduleName = clientCodegenContext.moduleUseName()
|
||||
|
|
|
@ -106,7 +106,7 @@ class ErrorCorrectionTest {
|
|||
assert_eq!(shape.not_required(), None);
|
||||
|
||||
// set defaults for everything else
|
||||
assert_eq!(shape.blob().as_ref(), &[]);
|
||||
assert_eq!(shape.blob().as_ref(), b"");
|
||||
|
||||
assert!(shape.list_value().is_empty());
|
||||
assert!(shape.map_value().is_empty());
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package software.amazon.smithy.rust.codegen.client.smithy.generators.config
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
|
||||
|
||||
class IdempotencyTokenProviderCustomizationTest {
|
||||
@Test
|
||||
fun `generates a valid config`() {
|
||||
val model = "namespace test".asSmithyModel()
|
||||
val codegenContext = testClientCodegenContext(model)
|
||||
validateConfigCustomizations(
|
||||
codegenContext,
|
||||
IdempotencyTokenProviderCustomization(codegenContext),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -10,16 +10,15 @@ import org.junit.jupiter.api.Test
|
|||
import software.amazon.smithy.model.shapes.ServiceShape
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.withEnableUserConfigurableRuntimePlugins
|
||||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
|
||||
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.writable
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.BasicTestModels
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
|
||||
import software.amazon.smithy.rust.codegen.core.util.lookup
|
||||
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
|
||||
|
@ -125,45 +124,50 @@ internal class ServiceConfigGeneratorTest {
|
|||
}
|
||||
}
|
||||
|
||||
val model = "namespace empty".asSmithyModel()
|
||||
val codegenContext = testClientCodegenContext(model)
|
||||
.withEnableUserConfigurableRuntimePlugins(true)
|
||||
val sut = ServiceConfigGenerator(codegenContext, listOf(ServiceCustomizer(codegenContext)))
|
||||
val symbolProvider = codegenContext.symbolProvider
|
||||
val project = TestWorkspace.testProject(symbolProvider)
|
||||
project.withModule(ClientRustModule.config) {
|
||||
sut.render(this)
|
||||
unitTest(
|
||||
"set_config_fields",
|
||||
"""
|
||||
let builder = Config::builder().config_field(99);
|
||||
let config = builder.build();
|
||||
assert_eq!(config.config_field(), 99);
|
||||
""",
|
||||
)
|
||||
|
||||
unitTest(
|
||||
"set_runtime_plugin",
|
||||
"""
|
||||
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
|
||||
use aws_smithy_types::config_bag::FrozenLayer;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestRuntimePlugin;
|
||||
|
||||
impl RuntimePlugin for TestRuntimePlugin {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
todo!("ExampleRuntimePlugin.config")
|
||||
}
|
||||
}
|
||||
|
||||
let config = Config::builder()
|
||||
.runtime_plugin(TestRuntimePlugin)
|
||||
.build();
|
||||
assert_eq!(config.runtime_plugins.len(), 1);
|
||||
""",
|
||||
)
|
||||
val serviceDecorator = object : ClientCodegenDecorator {
|
||||
override val name: String = "Add service plugin"
|
||||
override val order: Byte = 0
|
||||
override fun configCustomizations(
|
||||
codegenContext: ClientCodegenContext,
|
||||
baseCustomizations: List<ConfigCustomization>,
|
||||
): List<ConfigCustomization> {
|
||||
return baseCustomizations + ServiceCustomizer(codegenContext)
|
||||
}
|
||||
}
|
||||
|
||||
clientIntegrationTest(BasicTestModels.AwsJson10TestModel, additionalDecorators = listOf(serviceDecorator)) { ctx, rustCrate ->
|
||||
rustCrate.withModule(ClientRustModule.config) {
|
||||
unitTest(
|
||||
"set_config_fields",
|
||||
"""
|
||||
let builder = Config::builder().config_field(99);
|
||||
let config = builder.build();
|
||||
assert_eq!(config.config_field(), 99);
|
||||
""",
|
||||
)
|
||||
|
||||
unitTest(
|
||||
"set_runtime_plugin",
|
||||
"""
|
||||
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
|
||||
use aws_smithy_types::config_bag::FrozenLayer;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestRuntimePlugin;
|
||||
|
||||
impl RuntimePlugin for TestRuntimePlugin {
|
||||
fn config(&self) -> Option<FrozenLayer> {
|
||||
todo!("ExampleRuntimePlugin.config")
|
||||
}
|
||||
}
|
||||
|
||||
let config = Config::builder()
|
||||
.runtime_plugin(TestRuntimePlugin)
|
||||
.build();
|
||||
assert_eq!(config.runtime_plugins.len(), 1);
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
project.compileAndTest()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -449,8 +449,23 @@ fun RustWriter.implBlock(symbol: Symbol, block: Writable) {
|
|||
|
||||
/** Write a `#[cfg(feature = "...")]` block for the given feature */
|
||||
fun RustWriter.featureGateBlock(feature: String, block: Writable) {
|
||||
rustBlock("##[cfg(feature = ${feature.dq()})]") {
|
||||
block()
|
||||
featureGatedBlock(feature, block)(this)
|
||||
}
|
||||
|
||||
/** Write a `#[cfg(feature = "...")]` block for the given feature */
|
||||
fun featureGatedBlock(feature: String, block: Writable): Writable {
|
||||
return writable {
|
||||
rustBlock("##[cfg(feature = ${feature.dq()})]") {
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun featureGatedBlock(feature: Feature, block: Writable): Writable {
|
||||
return writable {
|
||||
rustBlock("##[cfg(feature = ${feature.name.dq()})]") {
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package software.amazon.smithy.rust.codegen.core.testutil
|
||||
|
||||
object BasicTestModels {
|
||||
val AwsJson10TestModel = """
|
||||
namespace com.example
|
||||
use aws.protocols#awsJson1_0
|
||||
@awsJson1_0
|
||||
service HelloService {
|
||||
operations: [SayHello],
|
||||
version: "1"
|
||||
}
|
||||
@optionalAuth
|
||||
operation SayHello { input: TestInput }
|
||||
structure TestInput {
|
||||
foo: String,
|
||||
}
|
||||
""".asSmithyModel()
|
||||
}
|
|
@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordConfig
|
|||
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.Visibility
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
|
||||
import software.amazon.smithy.rust.codegen.core.rustlang.implBlock
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider
|
||||
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
|
||||
|
@ -138,7 +139,11 @@ fun testRustSettings(
|
|||
)
|
||||
|
||||
private const val SmithyVersion = "1.0"
|
||||
fun String.asSmithyModel(sourceLocation: String? = null, smithyVersion: String = SmithyVersion, disableValidation: Boolean = false): Model {
|
||||
fun String.asSmithyModel(
|
||||
sourceLocation: String? = null,
|
||||
smithyVersion: String = SmithyVersion,
|
||||
disableValidation: Boolean = false,
|
||||
): Model {
|
||||
val processed = letIf(!this.trimStart().startsWith("\$version")) { "\$version: ${smithyVersion.dq()}\n$it" }
|
||||
val assembler = Model.assembler().discoverModels().addUnparsedModel(sourceLocation ?: "test.smithy", processed)
|
||||
if (disableValidation) {
|
||||
|
@ -208,3 +213,10 @@ fun StructureShape.renderWithModelBuilder(
|
|||
BuilderGenerator(model, symbolProvider, struct, emptyList()).render(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun RustCrate.unitTest(name: String? = null, test: Writable) {
|
||||
lib {
|
||||
val testName = name ?: safeName("test")
|
||||
unitTest(testName, block = test)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
- [RFC-0037: The HTTP Wrapper](./rfcs/rfc0037_http_wrapper.md)
|
||||
- [RFC-0038: User-configurable retry classification](./rfcs/rfc0038_retry_classifier_customization.md)
|
||||
- [RFC-0039: Forward Compatible Errors](./rfcs/rfc0039_forward_compatible_errors.md)
|
||||
- [RFC-0040: Behavior Major Versions](./rfcs/rfc0040_behavior_major_versions.md)
|
||||
|
||||
- [Contributing](./contributing/overview.md)
|
||||
- [Writing and debugging a low-level feature that relies on HTTP](./contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md)
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<!-- Give your RFC a descriptive name saying what it would accomplish or what feature it defines -->
|
||||
RFC: Behavior Major Versions
|
||||
=============
|
||||
|
||||
<!-- RFCs start with the "RFC" status and are then either "Implemented" or "Rejected". -->
|
||||
> Status: RFC
|
||||
>
|
||||
> Applies to: client
|
||||
|
||||
<!-- A great RFC will include a list of changes at the bottom so that the implementor can be sure they haven't missed anything -->
|
||||
For a summarized list of proposed changes, see the [Changes Checklist](#changes-checklist) section.
|
||||
|
||||
<!-- Insert a short paragraph explaining, at a high level, what this RFC is for -->
|
||||
This RFC describes "Behavior Major Versions," a mechanism to allow SDKs to ship breaking behavioral changes like a new retry strategy, while allowing customers who rely on extremely consistent behavior to evolve at their own pace.
|
||||
|
||||
By adding behavior major versions (BMV) to the Rust SDK, we will make it possible to ship new secure/recommended defaults to new customers without impacting legacy customers.
|
||||
|
||||
The fundamental issue stems around our inability to communicate and decouple releases of service updates and behavior within a single major version.
|
||||
|
||||
Both legacy and new SDKs have the need to alter their SDKs default. Historically, this caused new customers on legacy SDKs to be subject to legacy defaults, even when a better alternative existed.
|
||||
|
||||
For new SDKs, a GA cutline presents difficult choices around timeline and features that can’t be added later without altering behavior.
|
||||
|
||||
Both of these use cases are addressed by Behavior Major Versions.
|
||||
|
||||
<!-- Explain how users will use this new feature and, if necessary, how this compares to the current user experience -->
|
||||
The user experience if this RFC is implemented
|
||||
----------------------------------------------
|
||||
|
||||
In the current version of the SDK, users can construct clients without indicating any sort of behavior major version.
|
||||
Once this RFC is implemented, there will be two ways to set a behavior major version:
|
||||
|
||||
1. In code via `aws_config::from_env_with_version(BehaviorMajorVersion::latest())` and `<service>::Config::builder().behavior_major_version(...)`. This will also work for `config_override`.
|
||||
2. By enabling `behavior-version-latest` in either `aws-config` (which brings back `from_env`) OR a specific generated SDK crate
|
||||
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
aws-config = { version = "1", features = ["behavior-version-latest"] }
|
||||
# OR
|
||||
aws-sdk-s3 = { version = "1", features = ["behavior-version-latest"] }
|
||||
```
|
||||
|
||||
If no `BehaviorMajorVersion` is set, the client will panic during construction.
|
||||
|
||||
`BehaviorMajorVersion` is an opaque struct with initializers like `::latest()`, `::v2023_11_09()`. Downstream code can check the version by calling methods like `::supports_v1()`
|
||||
|
||||
When new BMV are added, the previous version constructor will be marked as `deprecated`. This serves as a mechanism to alert customers that a new BMV exists to allow them to upgrade.
|
||||
|
||||
How to actually implement this RFC
|
||||
----------------------------------
|
||||
|
||||
In order to implement this feature, we need to create a `BehaviorMajorVersion` struct, add config options to `SdkConfig` and `aws-config`, and wire it throughout the stack.
|
||||
```rust
|
||||
/// Behavior major-version of the client
|
||||
///
|
||||
/// Over time, new best-practice behaviors are introduced. However, these behaviors might not be backwards
|
||||
/// compatible. For example, a change which introduces new default timeouts or a new retry-mode for
|
||||
/// all operations might be the ideal behavior but could break existing applications.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BehaviorMajorVersion {
|
||||
// currently there is only 1 MV so we don't actually need anything in here.
|
||||
}
|
||||
```
|
||||
|
||||
To help customers migrate, we are including `from_env` hooks that set `behavior-version-latest` that are _deprecated_. This allows customers to see that they are missing the required cargo feature and add it to remove the deprecation warning.
|
||||
|
||||
Internally, `BehaviorMajorVersion` will become an additional field on `<client>::Config`. It is _not_ ever stored in the `ConfigBag` or in `RuntimePlugins`.
|
||||
|
||||
When constructing the set of "default runtime plugins," the default runtime plugin parameters will be passed the `BehaviorMajorVersion`. This will select the correct runtime plugin. Logging will clearly indicate which plugin was selected.
|
||||
|
||||
Design Alternatives Considered
|
||||
------------------------------
|
||||
|
||||
An original design was also considered that made BMV optional and relied on documentation to steer customers in the right direction. This was
|
||||
deemed too weak of a mechanism to ensure that customers aren't broken by unexpected changes.
|
||||
|
||||
Changes checklist
|
||||
-----------------
|
||||
|
||||
- [x] Create `BehaviorMajorVersion` and the BMV runtime plugin
|
||||
- [x] Add BMV as a required runtime component
|
||||
- [x] Wire up setters throughout the stack
|
||||
- [x] Add tests of BMV (set via aws-config, cargo features & code params)
|
||||
- [x] ~Remove `aws_config::from_env` deprecation stand-ins~ We decided to persist these deprecations
|
||||
- [x] Update generated usage examples
|
|
@ -13,7 +13,7 @@ publish = false
|
|||
# eliminating the need to directly depend on the crates that provide them. In rare instances,
|
||||
# you may still need to include one of these crates as a dependency. Examples that require this
|
||||
# are specifically noted in comments above the corresponding dependency in this file.
|
||||
pokemon-service-client = { path = "../pokemon-service-client/" }
|
||||
pokemon-service-client = { path = "../pokemon-service-client/", features = ["behavior-version-latest"] }
|
||||
|
||||
# Required for getting the operation name from the `Metadata`.
|
||||
aws-smithy-http = { path = "../../rust-runtime/aws-smithy-http/" }
|
||||
|
|
|
@ -20,4 +20,4 @@ hyper-rustls = { version = "0.24", features = ["http2"] }
|
|||
aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime/", features = ["client", "connector-hyper-0-14-x"] }
|
||||
aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http/" }
|
||||
aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types/" }
|
||||
pokemon-service-client = { path = "../pokemon-service-client/" }
|
||||
pokemon-service-client = { path = "../pokemon-service-client/", features = ["behavior-version-latest"] }
|
||||
|
|
|
@ -116,4 +116,5 @@ pub mod runtime_components;
|
|||
|
||||
pub mod runtime_plugin;
|
||||
|
||||
pub mod behavior_version;
|
||||
pub mod ser_de;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Behavior Major version of the client
|
||||
|
||||
/// Behavior major-version of the client
|
||||
///
|
||||
/// Over time, new best-practice behaviors are introduced. However, these behaviors might not be backwards
|
||||
/// compatible. For example, a change which introduces new default timeouts or a new retry-mode for
|
||||
/// all operations might be the ideal behavior but could break existing applications.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BehaviorMajorVersion {}
|
||||
|
||||
impl BehaviorMajorVersion {
|
||||
/// This method will always return the latest major version.
|
||||
///
|
||||
/// This is the recommend choice for customers who aren't reliant on extremely specific behavior
|
||||
/// characteristics. For example, if you are writing a CLI app, the latest behavior major version
|
||||
/// is probably the best setting for you.
|
||||
///
|
||||
/// If, however, you're writing a service that is very latency sensitive, or that has written
|
||||
/// code to tune Rust SDK behaviors, consider pinning to a specific major version.
|
||||
///
|
||||
/// The latest version is currently [`BehaviorMajorVersion::v2023_11_09`]
|
||||
pub fn latest() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// This method returns the behavior configuration for November 9th, 2023
|
||||
///
|
||||
/// When a new behavior major version is released, this method will be deprecated.
|
||||
pub fn v2023_11_09() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// Returns whether the current version is `v2023_11_09`
|
||||
pub fn supports_v2023_11_09(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
|
@ -474,7 +474,7 @@ impl RuntimeComponentsBuilder {
|
|||
auth_scheme_option_resolver: Option<impl ResolveAuthSchemeOptions + 'static>,
|
||||
) -> &mut Self {
|
||||
self.auth_scheme_option_resolver =
|
||||
auth_scheme_option_resolver.map(|r| Tracked::new(self.builder_name, r.into_shared()));
|
||||
self.tracked(auth_scheme_option_resolver.map(IntoShared::into_shared));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,7 @@ impl RuntimeComponentsBuilder {
|
|||
|
||||
/// Sets the HTTP client.
|
||||
pub fn set_http_client(&mut self, connector: Option<impl HttpClient + 'static>) -> &mut Self {
|
||||
self.http_client = connector.map(|c| Tracked::new(self.builder_name, c.into_shared()));
|
||||
self.http_client = self.tracked(connector.map(IntoShared::into_shared));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -717,13 +717,13 @@ impl RuntimeComponentsBuilder {
|
|||
|
||||
/// Sets the async sleep implementation.
|
||||
pub fn set_sleep_impl(&mut self, sleep_impl: Option<SharedAsyncSleep>) -> &mut Self {
|
||||
self.sleep_impl = sleep_impl.map(|s| Tracked::new(self.builder_name, s));
|
||||
self.sleep_impl = self.tracked(sleep_impl);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the async sleep implementation.
|
||||
pub fn with_sleep_impl(mut self, sleep_impl: Option<impl AsyncSleep + 'static>) -> Self {
|
||||
self.sleep_impl = sleep_impl.map(|s| Tracked::new(self.builder_name, s.into_shared()));
|
||||
self.set_sleep_impl(sleep_impl.map(IntoShared::into_shared));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -734,13 +734,13 @@ impl RuntimeComponentsBuilder {
|
|||
|
||||
/// Sets the time source.
|
||||
pub fn set_time_source(&mut self, time_source: Option<SharedTimeSource>) -> &mut Self {
|
||||
self.time_source = time_source.map(|s| Tracked::new(self.builder_name, s));
|
||||
self.time_source = self.tracked(time_source);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the time source.
|
||||
pub fn with_time_source(mut self, time_source: Option<impl TimeSource + 'static>) -> Self {
|
||||
self.time_source = time_source.map(|s| Tracked::new(self.builder_name, s.into_shared()));
|
||||
self.set_time_source(time_source.map(IntoShared::into_shared));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -805,6 +805,11 @@ impl RuntimeComponentsBuilder {
|
|||
validate!(self.retry_strategy);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Wraps `v` in tracking associated with this builder
|
||||
fn tracked<T>(&self, v: Option<T>) -> Option<Tracked<T>> {
|
||||
v.map(|v| Tracked::new(self.builder_name, v))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::client::retries::RetryPartition;
|
|||
use aws_smithy_async::rt::sleep::default_async_sleep;
|
||||
use aws_smithy_async::time::SystemTimeSource;
|
||||
use aws_smithy_runtime_api::box_error::BoxError;
|
||||
use aws_smithy_runtime_api::client::behavior_version::BehaviorMajorVersion;
|
||||
use aws_smithy_runtime_api::client::http::SharedHttpClient;
|
||||
use aws_smithy_runtime_api::client::runtime_components::{
|
||||
RuntimeComponentsBuilder, SharedConfigValidator,
|
||||
|
@ -170,6 +171,7 @@ pub fn default_identity_cache_plugin() -> Option<SharedRuntimePlugin> {
|
|||
#[derive(Debug, Default)]
|
||||
pub struct DefaultPluginParams {
|
||||
retry_partition_name: Option<Cow<'static, str>>,
|
||||
behavior_major_version: Option<BehaviorMajorVersion>,
|
||||
}
|
||||
|
||||
impl DefaultPluginParams {
|
||||
|
@ -183,6 +185,12 @@ impl DefaultPluginParams {
|
|||
self.retry_partition_name = Some(name.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the behavior major version.
|
||||
pub fn with_behavior_major_version(mut self, version: BehaviorMajorVersion) -> Self {
|
||||
self.behavior_major_version = Some(version);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// All default plugins.
|
||||
|
|
|
@ -27,12 +27,9 @@ pub struct CaptureRequestHandler(Arc<Mutex<Inner>>);
|
|||
impl HttpConnector for CaptureRequestHandler {
|
||||
fn call(&self, request: HttpRequest) -> HttpConnectorFuture {
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner
|
||||
.sender
|
||||
.take()
|
||||
.expect("already sent")
|
||||
.send(request)
|
||||
.expect("channel not ready");
|
||||
if let Err(_e) = inner.sender.take().expect("already sent").send(request) {
|
||||
tracing::trace!("The receiver was already dropped");
|
||||
}
|
||||
HttpConnectorFuture::ready(Ok(inner
|
||||
.response
|
||||
.take()
|
||||
|
|
|
@ -76,6 +76,7 @@ struct LambdaMain {
|
|||
}
|
||||
|
||||
impl LambdaMain {
|
||||
#[allow(deprecated)]
|
||||
async fn new() -> Self {
|
||||
Self {
|
||||
sdk_config: aws_config::load_from_env().await,
|
||||
|
|
Loading…
Reference in New Issue