From f192530f4dce2b8ca70f20156228a36b1aece145 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Fri, 5 Mar 2021 12:24:37 -0500 Subject: [PATCH] Pseudo-Integration tests for KMS (#239) * wip * Generate Cargo.toml after lib rs, cleanup deps * Move RecordingConnection into an extras crate * Move generation code into extras crate * Delete unused conn method * Add todo about storing req/resps externally * Add note about credential expiry * Fix clippy lint * Replace creds with fake creds * Delete note about real credentials * Give cargo fmt a hand --- aws/rust-runtime/aws-auth/src/provider.rs | 4 +- aws/rust-runtime/aws-hyper/src/retry.rs | 6 +- .../aws-hyper/src/test_connection.rs | 23 +++ aws/rust-runtime/aws-hyper/tests/e2e_test.rs | 7 +- aws/rust-runtime/aws-types/src/region.rs | 2 +- .../smithy/rustsdk/AwsCodegenDecorator.kt | 3 +- .../amazon/smithy/rustsdk/AwsHyperDevDep.kt | 45 ++++++ aws/sdk/integration-tests/kms/Cargo.toml | 5 + .../kms/tests/integration.rs | 151 ++++++++++++++++++ .../kms/tests/sensitive-it.rs | 17 +- .../rust/codegen/rustlang/CargoDependency.kt | 11 +- .../rust/codegen/smithy/CodegenDelegator.kt | 14 +- .../smithy/generators/CargoTomlGenerator.kt | 15 +- rust-runtime/smithy-http/src/operation.rs | 8 + 14 files changed, 275 insertions(+), 36 deletions(-) create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsHyperDevDep.kt create mode 100644 aws/sdk/integration-tests/kms/tests/integration.rs diff --git a/aws/rust-runtime/aws-auth/src/provider.rs b/aws/rust-runtime/aws-auth/src/provider.rs index d0adef20e..fc9f18175 100644 --- a/aws/rust-runtime/aws-auth/src/provider.rs +++ b/aws/rust-runtime/aws-auth/src/provider.rs @@ -116,7 +116,9 @@ mod test { fn missing() { let env = HashMap::new(); let provider = EnvironmentVariableCredentialsProvider::for_map(env); - let err = provider.provide_credentials().expect_err("no credentials defined"); + let err = provider + .provide_credentials() + .expect_err("no credentials defined"); match err { CredentialsError::Unhandled(_) => panic!("wrong error type"), _ => (), diff --git a/aws/rust-runtime/aws-hyper/src/retry.rs b/aws/rust-runtime/aws-hyper/src/retry.rs index f31b34ae7..3954884cc 100644 --- a/aws/rust-runtime/aws-hyper/src/retry.rs +++ b/aws/rust-runtime/aws-hyper/src/retry.rs @@ -256,11 +256,13 @@ where RetryKind::Error(err) => self.attempt_retry(Err(err))?, _ => return None, }; + let fut = async move { tokio::time::sleep(dur).await; next - }.instrument(tracing::info_span!("retry", kind = &debug(retry))); - Some(Box::pin(fut)) + } + .instrument(tracing::info_span!("retry", kind = &debug(retry))); + Some(Box::pin(fut)) } fn clone_request(&self, req: &Operation) -> Option> { diff --git a/aws/rust-runtime/aws-hyper/src/test_connection.rs b/aws/rust-runtime/aws-hyper/src/test_connection.rs index 45ef6aa65..58970f1ca 100644 --- a/aws/rust-runtime/aws-hyper/src/test_connection.rs +++ b/aws/rust-runtime/aws-hyper/src/test_connection.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +use http::header::HeaderName; use http::Request; use smithy_http::body::SdkBody; use std::future::Ready; @@ -18,6 +19,28 @@ pub struct ValidateRequest { pub actual: http::Request, } +impl ValidateRequest { + pub fn assert_matches(&self, ignore_headers: Vec) { + let (actual, expected) = (&self.actual, &self.expected); + for (name, value) in expected.headers() { + if !ignore_headers.contains(name) { + let actual_header = actual + .headers() + .get(name) + .unwrap_or_else(||panic!("Header {:?} missing", name)); + assert_eq!(actual_header, value, "Header mismatch for {:?}", name); + } + } + let actual_str = std::str::from_utf8(actual.body().bytes().unwrap_or(&[])); + let expected_str = std::str::from_utf8(expected.body().bytes().unwrap_or(&[])); + match (actual_str, expected_str) { + (Ok(actual), Ok(expected)) => assert_eq!(actual, expected), + _ => assert_eq!(actual.body().bytes(), expected.body().bytes()), + }; + assert_eq!(actual.uri(), expected.uri()); + } +} + /// TestConnection for use with a [`aws_hyper::Client`](crate::Client) /// /// A basic test connection. It will: diff --git a/aws/rust-runtime/aws-hyper/tests/e2e_test.rs b/aws/rust-runtime/aws-hyper/tests/e2e_test.rs index 17087a9cc..53774acf0 100644 --- a/aws/rust-runtime/aws-hyper/tests/e2e_test.rs +++ b/aws/rust-runtime/aws-hyper/tests/e2e_test.rs @@ -7,7 +7,7 @@ use aws_auth::Credentials; use aws_endpoint::{set_endpoint_resolver, DefaultAwsEndpointResolver}; use aws_http::user_agent::AwsUserAgent; use aws_http::AwsErrorRetryPolicy; -use aws_hyper::test_connection::{TestConnection, ValidateRequest}; +use aws_hyper::test_connection::TestConnection; use aws_hyper::{Client, RetryConfig, SdkError}; use aws_sig_auth::signer::OperationSigningConfig; use aws_types::region::Region; @@ -115,10 +115,7 @@ async fn e2e_test() { assert_eq!(resp, "Hello!"); assert_eq!(conn.requests().len(), 1); - let ValidateRequest { expected, actual } = &conn.requests()[0]; - assert_eq!(actual.headers(), expected.headers()); - assert_eq!(actual.body().bytes(), expected.body().bytes()); - assert_eq!(actual.uri(), expected.uri()); + conn.requests()[0].assert_matches(vec![]); } #[tokio::test] diff --git a/aws/rust-runtime/aws-types/src/region.rs b/aws/rust-runtime/aws-types/src/region.rs index a129a741a..2bd0c34dc 100644 --- a/aws/rust-runtime/aws-types/src/region.rs +++ b/aws/rust-runtime/aws-types/src/region.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; pub struct Region( // Regions are almost always known statically. However, as an escape hatch for when they // are not, allow for an owned region - Cow<'static, str> + Cow<'static, str>, ); impl AsRef for Region { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index 80a6a1932..93bdf1449 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -13,7 +13,8 @@ val DECORATORS = listOf( AwsEndpointDecorator(), UserAgentDecorator(), SigV4SigningDecorator(), - RetryPolicyDecorator() + RetryPolicyDecorator(), + IntegrationTestDecorator() ) class AwsCodegenDecorator : CombinedCodegenDecorator(DECORATORS) { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsHyperDevDep.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsHyperDevDep.kt new file mode 100644 index 000000000..c2e3ea26e --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsHyperDevDep.kt @@ -0,0 +1,45 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.rustlang.CratesIo +import software.amazon.smithy.rust.codegen.rustlang.DependencyScope +import software.amazon.smithy.rust.codegen.rustlang.Local +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection +import software.amazon.smithy.rust.codegen.smithy.generators.ProtocolConfig +import software.amazon.smithy.rust.codegen.smithy.letIf + +val TestedServices = setOf("kms") + +class IntegrationTestDecorator : RustCodegenDecorator { + override val name: String = "IntegrationTest" + override val order: Byte = 0 + + override fun libRsCustomizations( + protocolConfig: ProtocolConfig, + baseCustomizations: List + ): List = baseCustomizations.letIf(TestedServices.contains(protocolConfig.moduleName)) { + it + AwsHyperDevDep(protocolConfig.runtimeConfig) + } +} + +class AwsHyperDevDep(private val runtimeConfig: RuntimeConfig) : LibRsCustomization() { + override fun section(section: LibRsSection) = when (section) { + LibRsSection.Body -> writable { + addDependency(runtimeConfig.awsHyper().copy(scope = DependencyScope.Dev)) + addDependency(Tokio) + } + else -> emptySection + } +} + +val Tokio = CargoDependency("tokio", CratesIo("1"), features = listOf("macros"), scope = DependencyScope.Dev) +fun RuntimeConfig.awsHyper() = CargoDependency("aws-hyper", Local(relativePath)) diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml index a627e3092..581738f85 100644 --- a/aws/sdk/integration-tests/kms/Cargo.toml +++ b/aws/sdk/integration-tests/kms/Cargo.toml @@ -12,3 +12,8 @@ kms = { path = "../../build/aws-sdk/kms" } smithy-http = { path = "../../build/aws-sdk/smithy-http" } smithy-types = { path = "../../build/aws-sdk/smithy-types" } http = "0.2.3" +aws-hyper = { path = "../../build/aws-sdk/aws-hyper" } +aws-auth = { path = "../../build/aws-sdk/aws-auth" } +aws-http = { path = "../../build/aws-sdk/aws-http" } +tokio = { version = "1", features = ["full"]} +tracing-subscriber = "0.2.16" diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs new file mode 100644 index 000000000..3d46de812 --- /dev/null +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -0,0 +1,151 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +use aws_auth::Credentials; +use aws_http::user_agent::AwsUserAgent; +use aws_hyper::test_connection::TestConnection; +use aws_hyper::Client; +use http::Uri; +use kms::operation::GenerateRandom; +use kms::{Config, Region}; +use smithy_http::body::SdkBody; +use std::time::{Duration, UNIX_EPOCH}; + +// TODO: having the full HTTP requests right in the code is a bit gross, consider something +// like https://github.com/davidbarsky/sigv4/blob/master/aws-sigv4/src/lib.rs#L283-L315 to store +// the requests/responses externally + +#[tokio::test] +async fn generate_random() { + let creds = Credentials::from_keys( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + ); + let conn = TestConnection::new(vec![( + http::Request::builder() + .header("content-type", "application/x-amz-json-1.1") + .header("x-amz-target", "TrentService.GenerateRandom") + .header("content-length", "20") + .header("host", "kms.us-east-1.amazonaws.com") + .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210305/us-east-1/kms/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-target, Signature=750c6333c96dcbe4c4c11a9af8483ff68ac40e0e8ba8244772d981aab3cda703") + .header("x-amz-date", "20210305T134922Z") + .header("x-amz-security-token", "notarealsessiontoken") + .header("user-agent", "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0") + .header("x-amz-user-agent", "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0") + .uri(Uri::from_static("https://kms.us-east-1.amazonaws.com/")) + .body(SdkBody::from(r#"{"NumberOfBytes":64}"#)).unwrap(), + http::Response::builder() + .status(http::StatusCode::from_u16(200).unwrap()) + .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA=="}"#).unwrap()) + ]); + let client = Client::new(conn.clone()); + let conf = Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(creds) + .build(); + let mut op = GenerateRandom::builder().number_of_bytes(64).build(&conf); + op.config_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1614952162)); + op.config_mut().insert(AwsUserAgent::for_tests()); + let resp = client.call(op).await.expect("request should succeed"); + // primitive checksum + assert_eq!( + resp.plaintext + .expect("blob should exist") + .as_ref() + .iter() + .map(|i| *i as u32) + .sum::(), + 8562 + ); + assert_eq!(conn.requests().len(), 1); + for validate_request in conn.requests().iter() { + validate_request.assert_matches(vec![]); + } +} + +#[tokio::test] +async fn generate_random_malformed_response() { + let creds = Credentials::from_keys( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + ); + let conn = TestConnection::new(vec![( + http::Request::builder().body(SdkBody::from(r#"{"NumberOfBytes":64}"#)).unwrap(), + http::Response::builder() + .status(http::StatusCode::from_u16(200).unwrap()) + // last `}` replaced with a space, invalid JSON + .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA==" "#).unwrap()) + ]); + let client = Client::new(conn.clone()); + let conf = Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(creds) + .build(); + let op = GenerateRandom::builder().number_of_bytes(64).build(&conf); + client.call(op).await.expect_err("response was malformed"); +} + +#[tokio::test] +async fn generate_random_keystore_not_found() { + let creds = Credentials::from_keys( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + Some("notarealsessiontoken".to_string()), + ); + let conf = Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(creds) + .build(); + let conn = TestConnection::new(vec![( + http::Request::builder() + .header("content-type", "application/x-amz-json-1.1") + .header("x-amz-target", "TrentService.GenerateRandom") + .header("content-length", "56") + .header("host", "kms.us-east-1.amazonaws.com") + .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210305/us-east-1/kms/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-target, Signature=4ca5cde61676c0ee49fde9ba3c886967e8af16461b6aafdfaee18033eb4ac7a5") + .header("x-amz-date", "20210305T144724Z") + .header("x-amz-security-token", "notarealsessiontoken") + .header("user-agent", "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0") + .header("x-amz-user-agent", "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0") + .uri(Uri::from_static("https://kms.us-east-1.amazonaws.com/")) + .body(SdkBody::from(r#"{"NumberOfBytes":64,"CustomKeyStoreId":"does not exist"}"#)).unwrap(), + http::Response::builder() + .status(http::StatusCode::from_u16(400).unwrap()) + .header("x-amzn-requestid", "bfe81a0a-9a08-4e71-9910-cdb5ab6ea3b6") + .header("cache-control", "no-cache, no-store, must-revalidate, private") + .header("expires", "0") + .header("pragma", "no-cache") + .header("date", "Fri, 05 Mar 2021 15:01:40 GMT") + .header("content-type", "application/x-amz-json-1.1") + .header("content-length", "44") + .body(r#"{"__type":"CustomKeyStoreNotFoundException"}"#).unwrap()) + ]); + + let mut op = GenerateRandom::builder() + .number_of_bytes(64) + .custom_key_store_id("does not exist") + .build(&conf); + + op.config_mut() + .insert(UNIX_EPOCH + Duration::from_secs(1614955644)); + op.config_mut().insert(AwsUserAgent::for_tests()); + let client = Client::new(conn.clone()); + let err = client.call(op).await.expect_err("key store doesn't exist"); + let inner = match err { + aws_hyper::SdkError::ServiceError { + err: kms::error::GenerateRandomError::CustomKeyStoreNotFoundError(e), + .. + } => e, + other => panic!("Incorrect error received: {:}", other), + }; + assert_eq!(inner.message, None); + assert_eq!(conn.requests().len(), 1); + for validate_request in conn.requests().iter() { + validate_request.assert_matches(vec![]); + } +} diff --git a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs index 32aada776..cf99cc659 100644 --- a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs +++ b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs @@ -4,8 +4,8 @@ */ use kms::error::{CreateAliasError, LimitExceededError}; -use kms::operation::{CreateAlias}; -use kms::output::{GenerateRandomOutput, CreateAliasOutput}; +use kms::operation::CreateAlias; +use kms::output::{CreateAliasOutput, GenerateRandomOutput}; use kms::Blob; use smithy_http::result::{SdkError, SdkSuccess}; use smithy_http::retry::ClassifyResponse; @@ -29,13 +29,12 @@ fn errors_are_retryable() { let conf = kms::Config::builder().build(); let op = CreateAlias::builder().build(&conf); - let err = - Result::, SdkError>::Err( - SdkError::ServiceError { - raw: http::Response::builder().body("resp").unwrap(), - err, - }, - ); + let err = Result::, SdkError>::Err( + SdkError::ServiceError { + raw: http::Response::builder().body("resp").unwrap(), + err, + }, + ); let retry_kind = op.retry_policy().classify(err.as_ref()); assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt index 280ec6791..0e7207613 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt @@ -11,9 +11,10 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.util.dq -sealed class DependencyScope -object Dev : DependencyScope() -object Compile : DependencyScope() +sealed class DependencyScope { + object Dev : DependencyScope() + object Compile : DependencyScope() +} sealed class DependencyLocation data class CratesIo(val version: String) : DependencyLocation() @@ -111,7 +112,7 @@ fun CargoDependency.asType(): RuntimeType = data class CargoDependency( override val name: String, private val location: DependencyLocation, - val scope: DependencyScope = Compile, + val scope: DependencyScope = DependencyScope.Compile, private val features: List = listOf() ) : RustDependency(name) { @@ -171,7 +172,7 @@ data class CargoDependency( ) fun ProtocolTestHelpers(runtimeConfig: RuntimeConfig) = CargoDependency( - "protocol-test-helpers", Local(runtimeConfig.relativePath), scope = Dev + "protocol-test-helpers", Local(runtimeConfig.relativePath), scope = DependencyScope.Dev ) val SerdeJson: CargoDependency = diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt index 7cf84cb30..ee1873a00 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt @@ -44,6 +44,13 @@ fun CodegenWriterDelegator.finalize(settings: RustSettings, libRsCus } } } + this.useFileWriter("src/lib.rs", "crate::lib") { writer -> + val includedModules = this.includedModules().toSet().filter { it != "lib" } + val modules = includedModules.map { moduleName -> + RustModule.default(moduleName, PublicModules.contains(moduleName)) + } + LibRsGenerator(settings.moduleDescription, modules, libRsCustomizations).render(writer) + } val cargoDependencies = loadDependencies().filterIsInstance().distinct() this.useFileWriter("Cargo.toml") { val cargoToml = CargoTomlGenerator( @@ -53,13 +60,6 @@ fun CodegenWriterDelegator.finalize(settings: RustSettings, libRsCus ) cargoToml.render() } - this.useFileWriter("src/lib.rs", "crate::lib") { writer -> - val includedModules = this.includedModules().toSet().filter { it != "lib" } - val modules = includedModules.map { moduleName -> - RustModule.default(moduleName, PublicModules.contains(moduleName)) - } - LibRsGenerator(settings.moduleDescription, modules, libRsCustomizations).render(writer) - } flushWriters() } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt index c8b6efd56..c2c2ab92a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt @@ -7,12 +7,15 @@ package software.amazon.smithy.rust.codegen.smithy.generators import com.moandjiezana.toml.TomlWriter import software.amazon.smithy.rust.codegen.rustlang.CargoDependency -import software.amazon.smithy.rust.codegen.rustlang.Compile -import software.amazon.smithy.rust.codegen.rustlang.Dev +import software.amazon.smithy.rust.codegen.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.utils.CodeWriter -class CargoTomlGenerator(private val settings: RustSettings, private val writer: CodeWriter, private val dependencies: List) { +class CargoTomlGenerator( + private val settings: RustSettings, + private val writer: CodeWriter, + private val dependencies: List +) { fun render() { val cargoToml = mapOf( "package" to mapOf( @@ -22,8 +25,10 @@ class CargoTomlGenerator(private val settings: RustSettings, private val writer: "authors" to settings.moduleAuthors, "edition" to "2018" ), - "dependencies" to dependencies.filter { it.scope == Compile }.map { it.name to it.toMap() }.toMap(), - "dev-dependencies" to dependencies.filter { it.scope == Dev }.map { it.name to it.toMap() }.toMap() + "dependencies" to dependencies.filter { it.scope == DependencyScope.Compile }.map { it.name to it.toMap() } + .toMap(), + "dev-dependencies" to dependencies.filter { it.scope == DependencyScope.Dev }.map { it.name to it.toMap() } + .toMap() ) writer.writeWithNoFormatting(TomlWriter().write(cargoToml)) } diff --git a/rust-runtime/smithy-http/src/operation.rs b/rust-runtime/smithy-http/src/operation.rs index 8dc5014a7..0d841c1a2 100644 --- a/rust-runtime/smithy-http/src/operation.rs +++ b/rust-runtime/smithy-http/src/operation.rs @@ -2,6 +2,7 @@ use crate::body::SdkBody; use crate::property_bag::PropertyBag; use std::borrow::Cow; use std::cell::{Ref, RefCell, RefMut}; +use std::ops::DerefMut; use std::rc::Rc; #[derive(Clone)] @@ -47,6 +48,13 @@ impl Operation { pub fn into_request_response(self) -> (Request, Parts) { (self.request, self.parts) } + pub fn from_parts(request: Request, parts: Parts) -> Self { + Self { request, parts } + } + + pub fn config_mut(&mut self) -> impl DerefMut + '_ { + self.request.config_mut() + } pub fn with_metadata(mut self, metadata: Metadata) -> Self { self.parts.metadata = Some(metadata); -- GitLab